How to use HTTP POST in C++ with Wininet - c++

I am sending values of two variables using POST to the PHP server. The C++ application using Wininet connects to the server side script, but instead of sending the data correctly, it just shows empty fields in the parameters send using POST.
#include <windows.h> #include <wininet.h> #include <stdio.h> #include <fstream> #include <cstring>
#pragma comment (lib, "Wininet.lib")
#define SIZE 128
int main() {
HINTERNET Initialize, Connection, File;
DWORD dwBytes;
static const char *postData = "name=Jack+Din&age=38";
LPSTR accept1[2] = { "Accept: */*", NULL };
const char * const frmdata = "name=Arun+Pushkar&age=38";
static const char *hdrs[] = { "Content-Type: application/x-www-form-urlencoded" };
static const char *accept[] = { "*/*", NULL };
char ch;
Initialize = InternetOpen(L"HTTPGET", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!Initialize)
{
printf("Failed to open session\n");
exit(0);
}
Connection = InternetConnect(Initialize, L"192.168.1.10", INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
File = HttpOpenRequest(
Connection,
L"POST",
L"/trydata.php",
L"HTTP/1.0",
NULL,
NULL,
INTERNET_FLAG_NO_CACHE_WRITE,
0);
unsigned long dataLen = strlen((char*)frmdata)+1;
bool res = HttpSendRequest(
File, // file to which reply will come
(LPCWSTR)hdrs,
0,
(char*)frmdata, // post variables to be send
dataLen); // length of data send
if (res)
{
std::ofstream webSource;
webSource.open("a.html");
while (InternetReadFile(File, &ch, 1, &dwBytes))
{
if (dwBytes != 1)break;
webSource << ch;
}
webSource.close();
}
InternetCloseHandle(File);
InternetCloseHandle(Connection);
InternetCloseHandle(Initialize);
return 0;
}
The server side script is
<?php
$yourname = isset($_POST['name']) ? $_POST['name'] : 'no name';
$yourage = isset($_POST['age']) ? $_POST['age'] : 'no age';
echo "Hello".htmlspecialchars($yourname). "!";
echo "Your Age".htmlspecialchars($yourage). "!";
?>
When I run this C++ code I get the following in my a.html:
Hello no Name!Your Age no Age!

I would use ATL Server classes to do the same. Here is the example:
CAtlHttpClient client;
AtlNavigateData navData;
LPCSTR lpData = "data=toto";
navData.SetMethod(ATL_HTTP_METHOD_POST);
navData.SetPostData((BYTE*)lpData, lstrlenA(lpData), _T("application/x-www-form-urlencoded"));
client.Navigate(_T("mysite.net"), _T("myscript.php"), &navData);
const char* pszBody = (const char*)client.GetBody();

Related

What causes SEC_E_INVALID_TOKEN?

#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <windows.h>
#define SECURITY_WIN32
#include <security.h>
#include <schannel.h>
#include <shlwapi.h>
#include <assert.h>
#include <stdio.h>
#include <iostream>
#pragma comment (lib, "ws2_32.lib")
#pragma comment (lib, "secur32.lib")
#pragma comment (lib, "shlwapi.lib")
#define TLS_MAX_PACKET_SIZE (16384+512) // payload + extra over head for header/mac/padding (probably an overestimate)
typedef struct {
SOCKET sock;
CredHandle handle;
CtxtHandle context;
SecPkgContext_StreamSizes sizes;
int received; // byte count in incoming buffer (ciphertext)
int used; // byte count used from incoming buffer to decrypt current packet
int available; // byte count available for decrypted bytes
char* decrypted; // points to incoming buffer where data is decrypted inplace
char incoming[TLS_MAX_PACKET_SIZE];
} tls_socket;
int main() {
const char* hostname = "api.openai.com";
//const char* hostname = "badssl.com";
//const char* hostname = "expired.badssl.com";
//const char* hostname = "wrong.host.badssl.com";
//const char* hostname = "self-signed.badssl.com";
//const char* hostname = "untrusted-root.badssl.com";
const char* path = "/";
tls_socket s;
// initialize windows sockets
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {
return -1;
}
// create TCP IPv4 socket
s.sock = socket(AF_INET, SOCK_STREAM, 0);
if (s.sock == INVALID_SOCKET) {
WSACleanup();
return -1;
}
char sport[64];
wnsprintfA(sport, sizeof(sport), "%u", 443);
// connect to server
if (!WSAConnectByNameA(s.sock, hostname, sport, NULL, NULL, NULL, NULL, NULL, NULL))
{
closesocket(s.sock);
WSACleanup();
return -7;
}
// initialize schannel
{
SCHANNEL_CRED cred {};
cred
.dwVersion = SCHANNEL_CRED_VERSION;
cred .dwFlags = SCH_USE_STRONG_CRYPTO // use only strong crypto alogorithms
| SCH_CRED_AUTO_CRED_VALIDATION // automatically validate server certificate
| SCH_CRED_NO_DEFAULT_CREDS; // no client certificate authentication
cred.grbitEnabledProtocols = SP_PROT_TLS1_2; // allow only TLS v1.2
if (AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &cred, NULL, NULL, &s.handle, NULL) != SEC_E_OK)
{
closesocket(s.sock);
WSACleanup();
return -1;
}
}
s.received = s.used = s.available = 0;
s.decrypted = NULL;
// perform tls handshake
// 1) call InitializeSecurityContext to create/update schannel context
// 2) when it returns SEC_E_OK - tls handshake completed
// 3) when it returns SEC_I_INCOMPLETE_CREDENTIALS - server requests client certificate (not supported here)
// 4) when it returns SEC_I_CONTINUE_NEEDED - send token to server and read data
// 5) when it returns SEC_E_INCOMPLETE_MESSAGE - need to read more data from server
// 6) otherwise read data from server and go to step 1
CtxtHandle* context = NULL;
int result = 0;
for (;;) {
SecBuffer inbuffers[2] = { 0 };
inbuffers[0].BufferType = SECBUFFER_TOKEN;
inbuffers[0].pvBuffer = s.incoming;
inbuffers[0].cbBuffer = s.received;
inbuffers[1].BufferType = SECBUFFER_EMPTY;
SecBuffer outbuffers[1] = { 0 };
outbuffers[0].BufferType = SECBUFFER_TOKEN;
SecBufferDesc indesc = { SECBUFFER_VERSION, ARRAYSIZE(inbuffers), inbuffers };
SecBufferDesc outdesc = { SECBUFFER_VERSION, ARRAYSIZE(outbuffers), outbuffers };
DWORD flags = ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM;
SECURITY_STATUS sec = InitializeSecurityContextA(
&s.handle,
context,
context ? NULL : (SEC_CHAR*)hostname,
flags,
0,
0,
context ? &indesc : NULL,
0,
context ? NULL : &s.context,
&outdesc,
&flags,
NULL);
if (sec == SEC_E_INVALID_TOKEN) std::cout << "failed\n";
}
}
This is the code I'm running, from https://gist.github.com/mmozeiko/c0dfcc8fec527a90a02145d2cc0bfb6d here. The error code in question is "SEC_E_INVALID_TOKEN" The same error does persist with or without the full code. It only happens on some requests, to some sites. Sending the same request several times, sometimes it has this error, other times it doesn't, and only on this site that I've found so far. What does it mean, and how can I fix it? I don't know what "token" is being referred to, because I don't even see where anything called a token is passed.

Converting WinHttp to WinInet API POST request

I am trying to convert some HTTP request code from using the WinHttp COM interface to using lower-level WinInet calls from <wininet.h>. The COM version is working but I am having difficulty translating the calls into the WinInet API.
This code works fine and gets the correct error response (as the request data is empty) to the POST request:
#import <winhttpcom.dll>
#include <iostream>
#include <string>
int main()
{
HRESULT hr = CoInitialize(NULL);
using namespace WinHttp;
IWinHttpRequestPtr pReq = NULL;
hr = pReq.CreateInstance(__uuidof(WinHttpRequest));
const char* pszReq = "";
if (SUCCEEDED(hr))
{
_bstr_t bstrMethod("POST");
_bstr_t bstrUrl("https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb9.asmx");
hr = pReq->Open(bstrMethod, bstrUrl);
pReq->SetRequestHeader(_bstr_t("Content-Type"), _bstr_t("text/*"));
_variant_t vReq(pszReq);
hr = pReq->Send(vReq);
if (SUCCEEDED(hr))
{
_bstr_t bstrResp;
hr = pReq->get_ResponseText(&bstrResp.GetBSTR());
if (SUCCEEDED(hr))
{
std::cout << std::string(bstrResp) << "\n";
}
}
}
CoUninitialize();
}
Saving the output as html, gives this rendering of the response (which is what I expect, since I haven't provided any request data, which would usually include an access token).
This is the code (amended after comments below, and should be reproducible) that I am using to try and replicate this result using wininet.h and the low-level Win32 calls (I realize I haven't closed the handles).
#include <windows.h>
#include <WinInet.h>
#include <iostream>
int main()
{
const char* pszReq = "";
const char* pszUrl = "https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb9.asmx";
char szHostName[256];
char szPath[256];
URL_COMPONENTSA comps = {};
comps.dwStructSize = sizeof(comps);
comps.lpszHostName = szHostName;
comps.dwHostNameLength = sizeof(szHostName);
comps.lpszUrlPath = szPath;
comps.dwUrlPathLength = sizeof(szPath);
if (!InternetCrackUrlA(pszUrl, strlen(pszUrl), 0, &comps)) return 1;
HINTERNET hOpen = InternetOpenA("XYZ",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if (!hOpen) return 1;
HINTERNET hConnect = InternetConnectA(hOpen,szHostName,comps.nPort,
NULL,NULL,INTERNET_SERVICE_HTTP,0,NULL);
if (!hConnect) return 1;
const char * rgpszAcceptTypes[] = { "text/*", NULL };
HINTERNET hOpenReq = HttpOpenRequestA(hConnect,"POST",szPath,NULL, NULL,
rgpszAcceptTypes, 0,NULL);
if (!hOpenReq) return 1;
const char* pszHeader = "Content-Type: text/xml;charset=UTF-8";
//*** This line returns FALSE ***
BOOL bRet = HttpSendRequestA(hOpenReq, pszHeader, strlen(pszHeader), (LPVOID)pszReq, strlen(pszReq));
//*** LastError is ERROR_HTTP_INVALID_SERVER_RESPONSE
DWORD dwErr = GetLastError();
return 0;
}
All the WinInet handles are non-zero, suggesting the calls are working, but the last HttpSendRequestA() is returning FALSE immediately, with LastError set to ERROR_HTTP_INVALID_SERVER_RESPONSE.
Clearly the COM route hides a lot of intermediate working, and presumably some constants are defaulted to specific values. It may also be adding other header information, I suppose.
Perhaps someone can suggest where I am going wrong?
There are some mistakes in your WinInet code:
the pszServerName value needs to be just the host name by itself, not a full URL. If you have a URL as input, you can parse it into its constituent pieces using InternetCrackUrlA().
the 3rd parameter of HttpOpenRequestA() is the requested resource relative to pszServerName. So, in your example, you need to use "/" to request the root resource.
the 1st parameter of HttpSendRequestA() needs to be hOpenReq, not hOpen. Also, you should not be including the null-terminators in your buffer sizes.
If you have not already done so, you should have a look at WinInet's documentation on HTTP Sessions.
With that said, try this:
#include <windows.h>
#include <WinInet.h>
#include <iostream>
const char * pszUrl = "https://someUrl";
const char * pszReq = "A string of request data";
const char* pszHeader = "Content-Type: text/xml;charset=UTF-8";
char szHostName[256];
char szPath[256];
URL_COMPONENTSA comps = {};
comps.dwStructSize = sizeof(comps);
comps.lpszHostName = szHostName;
comps.dwHostNameLength = sizeof(szHostName);
comps.lpszUrlPath = szPath;
comps.dwUrlPathLength = sizeof(szPath);
BOOL bRet = InternetCrackUrlA(pszUrl, strlen(pszUrl), 0, &comps);
if (!bRet) ...
HINTERNET hOpen = InternetOpenA("XYZ", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hOpen) ...
HINTERNET hConnect = InternetConnectA(hOpen, szHostName, comps.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
if (!hConnect) ...
HINTERNET hOpenReq = HttpOpenRequestA(hConnect, "POST", szPath, NULL, NULL, NULL, comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, NULL);
if (!hOpenReq) ...
bRet = HttpSendRequestA(hOpenReq, pszHeader, strlen(pszHeader), pszReq, strlen(pszReq));
if (!bRet) ...
...

How to make GET requests with WinInet for both HTTP and HTTPS? [duplicate]

I want to send HTTPS GET request using WinInet. As far as i know, i should do it just like sending HTTP request except i have to use INTERNET_DEFAULT_HTTPS_PORT and INTERNET_FLAG_SECURE flag.
So here is what i tried:
#include "stdafx.h"
#include <string>
#include <windows.h>
#include <WinInet.h>
#pragma comment (lib, "Wininet.lib")
using namespace std;
// convert string
wstring CharPToWstring(const char* _charP)
{
return wstring(_charP, _charP + strlen(_charP));
}
// send https request
wstring SendHTTPSRequest_GET(const wstring& _server,
const wstring& _page,
const wstring& _params = L"")
{
char szData[1024];
// initialize WinInet
HINTERNET hInternet = ::InternetOpen(TEXT("WinInet Test"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet != NULL)
{
// open HTTP session
HINTERNET hConnect = ::InternetConnect(hInternet, _server.c_str(), INTERNET_DEFAULT_HTTPS_PORT, NULL,NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_SECURE, 1);
if (hConnect != NULL)
{
wstring request = _page +
(_params.empty() ? L"" : (L"?" + _params));
// open request
HINTERNET hRequest = ::HttpOpenRequest(hConnect, L"GET", (LPCWSTR)request.c_str() ,NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION, 1);
if (hRequest != NULL)
{
// send request
BOOL isSend = ::HttpSendRequest(hRequest, NULL, 0, NULL, 0);
if (isSend)
{
for(;;)
{
// reading data
DWORD dwByteRead;
BOOL isRead = ::InternetReadFile(hRequest, szData, sizeof(szData) - 1, &dwByteRead);
// break cycle if error or end
if (isRead == FALSE || dwByteRead == 0)
break;
// saving result
szData[dwByteRead] = 0;
}
}
// close request
::InternetCloseHandle(hRequest);
}
// close session
::InternetCloseHandle(hConnect);
}
// close WinInet
::InternetCloseHandle(hInternet);
}
wstring answer = CharPToWstring(szData);
return answer;
}
int _tmain(int argc, _TCHAR* argv[])
{
wstring answer = SendHTTPSRequest_GET(L"www.site.com", L"page.php", L"param1=value1&param2=value2&param3=value3&param4=value4");
return 0;
}
And my function returned an answer:
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.0.10</center>
</body>
</html>
What am i doing wrong?
INTERNET_FLAG_SECURE should be used in the flags for HttpOpenRequest, not InternetConnect.
This is explained in the MSDN documentation for WinINet API flags:
INTERNET_FLAG_SECURE
0x00800000
Uses secure transaction semantics. This translates to using Secure Sockets Layer/Private Communications Technology (SSL/PCT) and is only meaningful in HTTP requests. This flag is used by HttpOpenRequest and InternetOpenUrl, but this is redundant if https:// appears in the URL. The InternetConnect function uses this flag for HTTP connections; all the request handles created under this connection will inherit this flag.

C++ download file - if not available increasing RAM usage 1mb/s

my Code keeps filling the RAM, if it cant reach the file to download.(network disabled to test)
How can I stop downloading after timeout?
here is the main part for downloading:
int WINAPI WinMain(HINSTANCE instanceHandle, HINSTANCE, char*, int)
{
using namespace std;
std::wstring loadme = targetfolder;
loadme += L"\\filename.txt";
std::wstring url1(L"fileurl");
HRESULT hr1 = URLDownloadToFile(NULL, (url1.c_str()), (loadme.c_str()), 0, NULL); //Download-Start
}
You can use WinINet functions to check if internet is available, check if url link is available, and report progress. Needs "wininet.lib"
WinINet Reference
#include <windows.h>
#include <wininet.h>
#include <fstream>
void geturl(const wchar_t *url)
{
std::ofstream file("c:\\test\\test.htm");
HINTERNET hopen = InternetOpen(L"myAppName",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
if (hopen)
{
HINTERNET hurl = InternetOpenUrl(hopen,url,NULL,0,INTERNET_FLAG_DONT_CACHE,0);
if (hurl)
{
DWORD received;
const int bufsize = 1024;
char buf[bufsize];
while (InternetReadFile(hurl, buf, bufsize, &received))
{
//progress...
if (!received) break;
file.write(buf, received);
}
InternetCloseHandle(hurl);
}
InternetCloseHandle(hopen);
}
}

Using InternetConnect with IP-address fails (error 12029)

Here i have a snippet that doesn't work when i'am using IP address. But in MSDN it says that parameter could be dotted-decimal IP address, so it must work clearly. What is wrong? Thank you.
#include "stdafx.h"
#include <windows.h>
#include <wininet.h>
#include <tchar.h>
#include <iostream>
#pragma comment(lib,"wininet.lib")
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char szServer[] = "127.0.0.1";
char szUrl[] = "/test/upload.php";
char Data[] = "text123";
char szHdrs[] = "Content-Type: multipart/form-data; boundary=AaB03x";
char szTead[] = "--AaB03x\r\n"
"Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n"
"Content-Type: application/octet-stream\r\n"
"\r\n";
char szTail[] = "\r\n"
"--AaB03x--\r\n";
char szUserAgent[] = "MyUA";
char *szTypes[] = {"*/*", NULL};
HINTERNET hIOpen = InternetOpenA(szUserAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(!hIOpen)
{
printf("InternetOpenA Error\n");
}
HINTERNET hIConnect = InternetConnectA(hIOpen, szServer, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
if(!hIConnect)
{
printf("InternetConnectA Error\n");
}
HINTERNET hHttpOpReq = HttpOpenRequestA(hIConnect, "POST", szUrl, NULL, NULL, (LPCSTR *)szTypes, 0, 1);
if(!hHttpOpReq)
{
printf("HttpOpenRequestA Error\n");
}
BOOL bHttpSendReq = HttpSendRequestA(hHttpOpReq, szHdrs, strlen(szHdrs), Data, strlen(Data));
if (!bHttpSendReq)
{ int Error = GetLastError();
printf("HttpSendRequest Error %d\n", Error);
}
system("pause");
return 0;
}
There is nothing more i can add, actually about this issue. I'am using Windows 7 x64 Ultimate and VS2008.
Now i checked this .exe on other XP and Seven machines. Looks like it works, but not on my machine. Maybe the problem is (out there) somewhere else.
Three hints:
Add following flags to your internetconnect:
INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE
Also add http to your reqest:
char szServer[] = "http://127.0.0.1";
You might also try with INTERNET_OPEN_TYPE_DIRECT instead of INTERNET_OPEN_TYPE_PRECONFIG
in my code base I have a work around for a 12029 error, but it happens only when connection is lost during file downloading - to fix it I must fully close all WinInet handles and retry download.