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
Related
I am trying to develop a WebSockets client in C++ using WinHttp. I am making very slow progress (I am .NET developer). I have my WS server developed in .NET 6 and now I am working on a C++ console app that connects to the server and listens for messages.
So far I successfully connect to the server and upgrade the connection to WS but struggle setting up the callback that triggers when a new message was sent from the server. I spent hours finding an example with no success.
Can someone please help me how to set the callback for the below code or provide me with a working example?
static void CALLBACK Callback(HINTERNET handle,
DWORD_PTR context,
DWORD code,
void* info,
DWORD length)
{
switch (code) {
case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
{
int i = 0;
break;
}
case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
{
int i = 0;
break;
}
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
{
int i = 0;
break;
}
default:
{
}
}
if (0 != context)
{
}
}
int main()
{
DWORD dwError = ERROR_SUCCESS;
BOOL fStatus = FALSE;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer;
BOOL bResults = FALSE;
HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL, hWebSocket = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
//WINHTTP_FLAG_ASYNC);
if (hSession == NULL)
{
dwError = GetLastError();
}
// Setup the status callback function.
WINHTTP_STATUS_CALLBACK isCallback = WinHttpSetStatusCallback(hSession,
(WINHTTP_STATUS_CALLBACK)Callback,
WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,
NULL);
hConnect = WinHttpConnect(hSession, L"localhost", 5004, 0);
if (hConnect == NULL)
{
dwError = GetLastError();
}
// Create an HTTP request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect,
L"GET",
L"/ws",
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
0);//WINHTTP_FLAG_SECURE); // flags
if (hRequest == NULL)
{
dwError = GetLastError();
}
//
// Request protocol upgrade from http to websocket.
//
#pragma prefast(suppress:6387, "WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET does not take any arguments.")
fStatus = WinHttpSetOption(hRequest,
WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET,
NULL,
0);
if (!fStatus)
{
dwError = GetLastError();
}
// Send a request.
if (hRequest)
fStatus = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0, // request data length
0, // total length
0); // context
if (!fStatus)
{
dwError = GetLastError();
}
fStatus = WinHttpReceiveResponse(hRequest, 0);
if (!fStatus)
{
dwError = GetLastError();
}
//
// Application should check what is the HTTP status code returned by the server and behave accordingly.
// WinHttpWebSocketCompleteUpgrade will fail if the HTTP status code is different than 101.
//
hWebSocket = WinHttpWebSocketCompleteUpgrade(hRequest, NULL);
if (hWebSocket == NULL)
{
dwError = GetLastError();
}
//
// The request handle is not needed anymore. From now on we will use the websocket handle.
//
WinHttpCloseHandle(hRequest);
hRequest = NULL;
std::cout << "Connected to WS!\n";
}
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 ...
I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this.
Thanks in advance.
i Have the following code:
void QtGuiApplication::getBtnClick() {
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer;
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
// Specify an HTTP server.
if (hSession) {
hConnect = WinHttpConnect(hSession, L"127.0.0.1",
INTERNET_DEFAULT_HTTP_PORT, 0);
}
else {
qDebug("Error has WinHttpOpen");
QString errorStr = QString::number(GetLastError());
qDebug(qPrintable(errorStr));
}
// Create an HTTP request handle.
if (hConnect) {
hRequest = WinHttpOpenRequest(hConnect, L"GET", NULL,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE);
}
else {
qDebug("Error has WinHttpConnect");
QString errorStr = QString::number(GetLastError());
qDebug(qPrintable(errorStr));
}
// Send a request.
if (hRequest) {
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);
}
else {
qDebug("Error has WinHttpOpenRequest");
QString errorStr = QString::number(GetLastError());
qDebug(qPrintable(errorStr));
}
// End the request.
if (bResults) {
bResults = WinHttpReceiveResponse(hRequest, NULL);
}
else {
qDebug("Error has WinHttpSendRequest");
QString errorStr = QString::number(GetLastError());
qDebug(qPrintable(errorStr));
}
// Keep checking for data until there is nothing left.
if (bResults)
do
{
// Check for available data.
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());
// Allocate space for the buffer.
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer)
{
printf("Out of memory\n");
dwSize = 0;
}
else
{
// Read the Data.
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer,
dwSize, &dwDownloaded))
printf("Error %u in WinHttpReadData.\n", GetLastError());
else
qDebug(pszOutBuffer);
// Free the memory allocated to the buffer.
delete[] pszOutBuffer;
}
} while (dwSize > 0);
// Report any errors.
if (!bResults) {
qDebug("Error has occurred");
QString str = QString::number(GetLastError());
qDebug(qPrintable(str));
}
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}
I try to access the local server through winhttp.
The browser can normally access 127.0.0.1.
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);
error var
GetLastError returns 6 after call to WinHttpSendRequest
The code of the service running with springboot is as follows
#Controller
public class MyController {
#ResponseBody
#RequestMapping("")
public String home() {
return "Home";
}
}
Springboot run information: Tomcat started on port(s): 80 (http) with context path ''
Using winhttp API to access services, Get springboot error information
I've tried a lot of ways, and it's still useless.
For example, modify the springboot port and winhttp API request port to 8089.
From WinHttpConnect
INTERNET_DEFAULT_HTTP_PORT
Uses the default port for HTTP servers (port 80).
INTERNET_DEFAULT_HTTPS_PORT
Uses the default port for HTTPS servers (port 443). Selecting this
port does not automatically establish a secure connection. You must
still specify the use of secure transaction semantics by using the
WINHTTP_FLAG_SECURE flag with WinHttpOpenRequest.
The :80 part is the TCP port. You can consider these ports as communications endpoints on a particular IP address (in the case of localhost - 127.0.0.1). The IANA is responsible for maintaining the official assignments of standard port numbers for specific services. Port 80 happens to be the standard port for HTTP.
Refer: What's the whole point of “localhost”, hosts and ports at all?
So you need to change INTERNET_DEFAULT_HTTPS_PORT to INTERNET_DEFAULT_HTTP_PORT.
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?
Hey all i want to login onto my works webpage with wininet, this is my current code:
int main()
{
HINTERNET hInet = InternetOpenA("UserAgent/1.0", INTERNET_OPEN_TYPE_PRECONFIG,0, 0, 0 );
if(!hInet)
{
printf("hInet Failed!\n");
return -1;
}
HINTERNET hConnection = InternetConnectA( hInet,"app.tamigo.com",INTERNET_DEFAULT_HTTPS_PORT,"","", INTERNET_SERVICE_HTTP,0,0);
if (!hConnection)
{
InternetCloseHandle(hInet);
printf("InternetConnectA failed!\n");
return -1;
}
HINTERNET hRequest = HttpOpenRequestA( hConnection, "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",NULL,"https://app.tamigo.com/Home/Pages/Login.aspx", NULL, INTERNET_FLAG_KEEP_CONNECTION, 0 );
if (!hRequest)
{
printf("BuildRequestHeader failed %d!\n",GetLastError());
InternetCloseHandle(hConnection);
InternetCloseHandle(hInet);
return -1;
}
HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength = 10;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
while (!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
}
else
{
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
printf("%s", pInfoBuffer);
free(pInfoBuffer);
cin.get();
return 1;
}
if this code is right, i have to login with my username and pass,i got a cookie using "Firefox plugin Tamper Data". How can i set this cookie with wininet?
Thanks alot for reading and for your time
If the cookie already exists from a previous WinInet request, then WinInet will send it automatically. However, if the cookie does not exist in WinInet's cookie cache (if instance, if you got the cookie from another source), then you will have to use HttpAddRequestHeaders() to provide your own Cookie: request header before calling HttpSendRequest().