WinInet ERROR_INTERNET_CANNOT_CONNECT on SSL - c++

I've wrote program that does simple HTTP POST.
When using HTTP protocol all goes well, but when changing to HTTPS i get this error:
ERROR_INTERNET_CANNOT_CONNECT
Request("GET","www.google.pl",80,false,"/","",""); //works
Request("GET","www.google.pl",443,true,"/","",""); //ERROR_INTERNET_CANNOT_CONNECT
What's funny is that i've also tried it on XP and there, insteed of ERROR_INTERNET_CANNOT_CONNECT i have (Internet Explorer 7, which can open https pages correctly)
Here is the Request() body:
string Request(const char* Method, const char* Host,int Port,bool Secured, const char* url, const char* header, const char* data)
{
char buffer[1024];
string ret;
HINTERNET internet = InternetOpenA("mikmas", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(internet == NULL)
throw OpenInternetException();
HINTERNET connect = InternetConnectA(internet, Host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if(connect == NULL)
{
InternetCloseHandle(internet);
throw CantConnectException();
}
int flag=INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_PRAGMA_NOCACHE;
if (Secured)
flag|=INTERNET_FLAG_SECURE;
HINTERNET request = HttpOpenRequest(connect, Method, url, "HTTP/1.1", NULL, NULL, flag, NULL);
if(request == NULL)
{
InternetCloseHandle(request);
InternetCloseHandle(connect);
throw CantOpenRequestException();
}
int datalen = 0;
if(data != NULL) datalen = strlen(data);
int headerlen = 0;
if(header != NULL) headerlen = strlen(header);
if (!HttpSendRequest(request, header, headerlen, (void*)data, datalen))
throw CantSendRequestException();
DWORD read;
while(InternetReadFile(request, buffer, 1023, &read) && read)
{
buffer[read] = 0;
ret+=buffer;
}
InternetCloseHandle(request);
InternetCloseHandle(connect);
InternetCloseHandle(internet);
return ret;
}
I'm really confused when using WinINET. A so simple action - stupid, simplest POST over https and it's almost impossible in cpp?

Related

Bad Request: message text is empty when sending get request via winapi to telegram bot

I'm trying to send message to telegram chat from bot using winapi and c++.
Here is my code
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, L"api.telegram.org", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 1);
if (hConnect != NULL)
{
wstring request = L"/bot<bot_id>/sendMessage";
// open request
HINTERNET hRequest = ::HttpOpenRequest(hConnect, L"GET", (LPCWSTR)request.c_str(), NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE, 1);
if (hRequest != NULL)
{
// send request
const wchar_t* params = L"?chat_id=<chat_id>&text=test";
BOOL isSend = ::HttpSendRequest(hRequest, NULL, 0, (LPVOID)params, wcslen(params));
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;
But I've got {"ok":false,"error_code":400,"description":"Bad Request: message text is empty"} response. <chat_id> is id consisted of digits(12345678).
If I run this request in postman or in browser - then everything is ok.
I also tried to run this request using WinHttp* methods and result is the same.
What should I change in my request parameters to make it work?
There are a number of issues with this code:
You don't need to typecast the return value of wstring::c_str() to LPCWSTR (aka const wchar_t*), as it is already that type.
You can't send body data in a GET request. The Telegram Bot API expects body data to be sent in a POST request instead.
You are telling HttpSendRequest() to send body data from a wchar_t* UTF-16 string, but that is not the correct encoding that the server is expecting. You need to use a char* UTF-8 string instead.
You are not sending a Content-Type request header to tell the server what the format of the body data is. The API supports several different formats. In this case, since you are sending the data in application/x-www-form-urlencoded format, you need to add a Content-Type: application/x-www-form-urlencoded header to the request.
With all of that said, try this instead:
// initialize WinInet
HINTERNET hInternet = ::InternetOpenW(L"WinInet Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL) ... // error handling
// open HTTP session
HINTERNET hConnect = ::InternetConnectW(hInternet, L"api.telegram.org", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 1);
if (hConnect == NULL) ... // error handling
// open request
wstring wsResource = L"/bot<bot_id>/sendMessage";
HINTERNET hRequest = ::HttpOpenRequestW(hConnect, L"POST", wsResource.c_str(), NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE, 1);
if (hRequest == NULL) ... // error handling
// send request
string sBody = u8"chat_id=<chat_id>&text=test";
BOOL isSend = ::HttpSendRequestW(hRequest, L"Content-Type: application/x-www-form-urlencoded", -1L, sBody.c_str(), sBody.size());
if (!isSend) ... // error handling
string sReply;
char szData[1024];
DWORD dwByteRead;
while (::InternetReadFile(hRequest, szData, sizeof(szData), &dwByteRead) && dwByteRead != 0)
{
// saving result
sReply.append(szData, dwByteRead);
}
...
// use sReply as needed ...

Only Fiddler fixes my WININET GET request

I need to send WININET request to rest server and get json response. Code works only while fiddler is working. I've tried everything what I've found but every time I get 0 into bytesRead. There aren't any errors and HttpSendRequestand and InternetReadFile both return true but buffer is still empty.
Server works fine and answers in a right way. When fiddler is running I get my 76 bytes, which I need. I tried to figure out using that blog Help! Running Fiddler Fixes My App???, no luck.
HINTERNET session = nullptr;
HINTERNET request = nullptr;
HINTERNET connect = nullptr;
BOOST_SCOPE_EXIT((&session)(&request)(&connect)) {
if (request != nullptr)
::InternetCloseHandle(request);
if (session != nullptr)
::InternetCloseHandle(session);
if (connect != nullptr)
::InternetCloseHandle(connect);
} BOOST_SCOPE_EXIT_END;
try
{
//std::wstring agent(L"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
std::wstring agent{ L"Mozilla/5.0 (compatible)" };
session = ::InternetOpen(agent.c_str(), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!session)
return;
InternetSetStatusCallback(
session,
(INTERNET_STATUS_CALLBACK)IStatusCallback);
connect = InternetConnect(
session
, serverName.c_str()
, INTERNET_DEFAULT_HTTP_PORT
, NULL
, NULL
, INTERNET_SERVICE_HTTP
, 0
, 1);
if (!connect)
return;
//const wchar_t* parrAcceptTypes[] = { L"text/*", NULL };
const wchar_t* parrAcceptTypes[] = { L"application/json", L"text/*", NULL };
request = HttpOpenRequest(
connect
, L"GET"
, virtualFolder.c_str()
, L"HTTP/1.1"
, NULL
, parrAcceptTypes
, /*INTERNET_FLAG_KEEP_CONNECTION*/INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE
, 1);
std::string data;
if (request)
{
BOOL isRequestComplete = HttpSendRequest(request, NULL, 0, NULL, 0);
if (isRequestComplete)
{
const int dataSize = 1024;
BYTE buff[dataSize];
DWORD bytesRead = (DWORD)-1;
BOOL bKeepReading = true;
while (bKeepReading && bytesRead != 0)
{
bKeepReading = InternetReadFile(request, buff, sizeof(buff) - 1, &bytesRead);
data.append((char*)buff, bytesRead);
}
}
else
{
DWORD dwErr = GetLastError();
InternetErrorDlg(parent_, request, dwErr, 0, NULL);
LERR_ << "Request was failed. Error code: " << dwErr;
}
}

HInternet works only one time

This code is in a C++ DLL injected in a game. (This is on a private server so it is legal). The code has to be executed multiple times. However, it works only the first time. It could be on the WCF end but I managed to send 2 and 5 successful requests from a .net dll loaded from the c++ dll before.
Since I didn't figure out why it was blocked after 2 and 5 requests, I decided to go all native but now I'm blocked after one request. I have a feeling it now has to do with the way I'm parsing the response.
The first execution gets a 200 code status and the second one gets a 0.
edit : my fire wall is turned off.
HINTERNET hInternet = InternetOpen(_T("MyApp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hInternet, _T("localhost"), INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
LPCSTR rgpszAcceptTypes[] = { _T("application/json"), NULL };
HINTERNET hRequest = HttpOpenRequest(hConnect, _T("POST"), _T("/xxxBackend/service1.svc/SaveDataVoid"), NULL, NULL, rgpszAcceptTypes, 0, 0);
HttpAddRequestHeaders(hRequest, _T("Content-Type: application/json\r\n"), -1, HTTP_ADDREQ_FLAG_ADD);
char *JsonData = "{\"data\":{\"AccountName\":\"\",\"CharName\":\"SilverDeth-IV\",\"GameDiff\":\"1\",\"CompressedData\":[8228138568842941382,8247906688399250381,8244242016143283142]}}";
HttpSendRequest(hRequest, NULL, 0, JsonData, strlen(JsonData));
DWORD StatusCode = 0;
DWORD StatusCodeLen = sizeof(StatusCode);
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &StatusCode, &StatusCodeLen, NULL);
if (StatusCode == 200)
{
char *lpBuffer[2000];
DWORD lpdwNumberOfBytesRead = 0;
InternetQueryDataAvailable(hRequest, &StatusCodeLen, 0, 0);
bool bRetval = InternetReadFile(hRequest, lpBuffer, 2000, &lpdwNumberOfBytesRead);
}
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
The code you have shown is not doing ANY error handling at all. Add that in, and then see where the failure is really occurring. The fact that StatusCode is 0 means HttpQueryInfo() is failing, which could happen if you did not obtain a valid hRequest to begin with.
BTW, rgpszAcceptTypes needs to be declared as LPCTSTR instead of LPCSTR, and you should be using the TEXT() macro instead of the _T() macro. TEXT() belongs to the Win32 API, but _T() belongs to the C runtime library, which you are not using in this code.
Try this:
void InetError(LPCTSTR msg)
{
DWORD dwErr = GetLastError();
if (dwErr == ERROR_INTERNET_EXTENDED_ERROR)
{
DWORD dwInetErr = 0;
LPTSTR szResp = NULL;
DWORD dwLength = 0;
InternetGetLastResponseInfo(&dwInetErr, NULL, &dwLength);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
szResp = new TCHAR[dwLength+1];
InternetGetLastResponseInfo(&dwInetErr, szResp, &dwLength);
}
// use msg, szResp, and dwInetErr as needed...
delete[] szResp;
}
else
{
// use msg and dwErr as needed...
}
}
...
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
hInternet = InternetOpen(TEXT("MyApp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (!hInternet)
{
InetError(TEXT("InternetOpen failed"));
goto done;
}
hConnect = InternetConnect(hInternet, TEXT("localhost"), INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if (!hConnect)
{
InetError(TEXT("InternetConnect failed"));
goto done;
}
LPCTSTR rgpszAcceptTypes[] = { TEXT("application/json"), NULL };
hRequest = HttpOpenRequest(hConnect, TEXT("POST"), TEXT("/xxxBackend/service1.svc/SaveDataVoid"), NULL, NULL, rgpszAcceptTypes, 0, 0);
if (!hRequest)
{
InetError(TEXT("HttpOpenRequest failed"));
goto done;
}
if (!HttpAddRequestHeaders(hRequest, TEXT("Content-Type: application/json\r\n"), -1, HTTP_ADDREQ_FLAG_ADD))
{
InetError(TEXT("HttpAddRequestHeaders failed"));
goto done;
}
char *JsonData = "{\"data\":{\"AccountName\":\"\",\"CharName\":\"SilverDeth-IV\",\"GameDiff\":\"1\",\"CompressedData\":[8228138568842941382,8247906688399250381,8244242016143283142]}}";
if (!HttpSendRequest(hRequest, NULL, 0, JsonData, strlen(JsonData)))
{
InetError(TEXT("HttpSendRequest failed"));
goto done;
}
DWORD StatusCode = 0;
DWORD StatusCodeLen = sizeof(StatusCode);
if (!HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &StatusCode, &StatusCodeLen, NULL))
{
InetError(TEXT("HttpQueryInfo failed"));
goto done;
}
if (StatusCode == 200)
{
BYTE Buffer[2000];
DWORD dwNumberOfBytes = 0;
DWORD dwNumberOfBytesRead = 0;
if (!InternetQueryDataAvailable(hRequest, &dwNumberOfBytes, 0, 0))
{
InetError(TEXT("InternetQueryDataAvailable failed"));
goto done;
}
if (!InternetReadFile(hRequest, Buffer, min(dwNumberOfBytes, sizeof(Buffer)), &dwNumberOfBytesRead))
{
InetError(TEXT("InternetReadFile failed"));
goto done;
}
//...
}
done:
if (hRequest) InternetCloseHandle(hRequest);
if (hConnect) InternetCloseHandle(hConnect);
if (hInternet) InternetCloseHandle(hInternet);

Check certificate thumbprint before doing the actual request. Windows C

I have this piece of code:
int Request(char* Method, LPCSTR Host, LPCSTR url, LPCSTR header, LPSTR data, bool ssl){
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa385096%28v=vs.85%29.aspx
HINTERNET internet = InternetOpenA("Firefox", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (internet != NULL)
{
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa384363%28v=vs.85%29.aspx
HINTERNET connect = InternetConnectA(internet, Host, port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if (connect != NULL)
{
//Here I want to get the certificate from the server and check the thumbprint. If it's not the want I want I don't do any request
if(thumbprint != "49023842938429034809234"){
InternetCloseHandle(connect);
InternetCloseHandle(internet);
return error;
}
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa384233%28v=vs.85%29.aspx
HINTERNET request = HttpOpenRequestA(connect, m, url, "HTTP/1.1", NULL, NULL,
dwFlags, NULL);
if (request != NULL)
{
int datalen = 0;
if (data != NULL) datalen = strlen(data);
int headerlen = 0;
if (header != NULL) headerlen = strlen(header);
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa384247%28v=vs.85%29.aspx
HttpSendRequestA(request, header, headerlen, data, datalen);
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa384350%28v=vs.85%29.aspx
InternetCloseHandle(request);
}
}
InternetCloseHandle(connect);
}
InternetCloseHandle(internet);
}
I want to check the thumbprint and if the server does not provide me the expected thumbprint I will close the connection and don't launch the actual request.
I saw this post but the author does a HEAD requests to get it. http://blogs.msdn.com/b/jpsanders/archive/2009/04/17/how-to-get-certificate-information-using-wininet-apis.aspx. Can't I get the certificate details just after connect to the server?
Thank you

Passing Multiple params HttpPostRequest c++

I have this function to send HTTP POST requests in C++, but i am having a little problem passing multiple strings arguments to it. Here's my code so far:
#include <Windows.h>
int doHttpPost(char *szDomain, char *szPage, char *szPost)
{
int iReturn = 1;
HINTERNET hSession = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
static TCHAR hdrs[] = "Content-Type: application/x-www-form-urlencoded";
const char *accept[2]={"*/*", NULL};
TCHAR *frmdata = szPost;
hSession = InternetOpen("AGENT", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(hSession)
{
hConnect = InternetConnect(hSession, szDomain, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
if(hConnect)
{
hRequest = HttpOpenRequest(hConnect, "POST", szPage, NULL, NULL, accept, INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, 0);
if(hRequest)
{
if(HttpSendRequest(hRequest, hdrs, strlen(hdrs), frmdata, strlen(frmdata)))
iReturn = 0;
else
iReturn = 5;
}
else //HttpOpenRequest
iReturn = 3;
}
else //InternetConnect
iReturn = 2;
}
else //InternetOpen
iReturn = 1;
//Cleanup
InternetCloseHandle(hSession);
InternetCloseHandle(hConnect);
InternetCloseHandle(hRequest);
return iReturn;
}
I call the function this way:
doHttpPost((char*)"127.0.0.1",(char*)"/test/post.php",(char*)postreq);
Is there a way I can add multiple requests in the post field, such as:
value1=id&value2=password&value3=details
where id, password and details will be variables containing data.
You can build a string from parts using sprintf and friends.
The simplest code snippet, which however ignores escaping you need to do on the values, is as follows:
CHAR pszRequest[1024] = { 0 };
CHAR* pszValue1 = "id";
CHAR* pszValue2 = "password";
CHAR* pszValue3 = "details";
sprintf(pszRequest, "value1=%s&value2=%s&value3=%s",
pszValue1, pszValue2, pszValue3);
doHttpPost((char*) "127.0.0.1", (char*) "/test/post.php", pszRequest);
For a robust solution you need to read up on basic string manipulation functions, and check RFC that defines body format for "application/x-www-form-urlencoded" MIME type.