Purpose of WINHTTP_QUERY_URI in WinHTTP? - c++

What HTTP header exactly can be queried using WINHTTP_QUERY_URI flag with WinHttpQueryHeaders function? After reading its description I was under impression this flag was supposed to be used to get the URI of the request specified in WinHttpOpenRequest function. Yet the following program gives me an error code 12019 ERROR_INTERNET_INCORRECT_HANDLE_STATE (and 12150 ERROR_HTTP_HEADER_NOT_FOUND if I uncomment two commented lines).
#include <cstdio>
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
int main()
{
HINTERNET hSession = WinHttpOpen(nullptr, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET hConnect = WinHttpConnect(hSession, L"www.ietf.org", INTERNET_DEFAULT_HTTP_PORT, 0);
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/rfc/rfc2616.txt", nullptr, WINHTTP_NO_REFERER, nullptr, 0);
//WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
//WinHttpReceiveResponse(hRequest, 0);
wchar_t url[1024] = {};
DWORD url_size = sizeof(url);
auto success = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_URI, WINHTTP_HEADER_NAME_BY_INDEX, url, &url_size, WINHTTP_NO_HEADER_INDEX);
auto error_code = GetLastError();
wprintf(L"success=%d error_code=%u url=%s", success, error_code, url);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
}
P.S. Yes, I know I can get request URI using WinHttpQueryOption and WINHTTP_OPTION_URL, no need to point that out.
EDIT. Adding WINHTTP_QUERY_FLAG_REQUEST_HEADERS flag as per Captain Obvlious answer below (which totally makes sense if WINHTTP_QUERY_URI was indeed supposed to return request URI) did not make much difference: now with or without WinHttpSendRequest and WinHttpReceiveResponse calls WinHttpQueryHeaders function produces error code 12150 ERROR_HTTP_HEADER_NOT_FOUND.

You are querying the response headers of the request which do not contain the URI. You need to include the WINHTTP_QUERY_FLAG_REQUEST_HEADERS modifier flag to retrieve from the request header.
WinHttpQueryHeaders(
hRequest,
WINHTTP_QUERY_URI | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
WINHTTP_HEADER_NAME_BY_INDEX,
url,
&url_size,
WINHTTP_NO_HEADER_INDEX);

Related

Winhttp to send data back and forth between networked computer?

I am trying to send and retrieve data to/from a remote client computer. I can't use DCOM or Named Pipes because ports 135 and 445 are closed and cannot be opened.
The advantage of winhttp, according to Microsoft docs, is you can use http as the transport (instead of named pipes) without having an IIS web server on the other end.
I'd consider using TCP, but I can't find any examples of using TCP in C/C++, and at this point I'm not sure if any exist?
I think I've got the basic connection down of the request, but how do I send information to the other computer? And how does the other server send it back?
Here's my code for doing a GET for Winhttp:
#include <windows.h>
#include <tchar.h>
#include <winhttp.h>
#include <strsafe.h>
#define tnew(nCharacters) new CHAR[nCharacters]()
#pragma comment(lib, "winhttp.lib")
int main()
{
CHAR *sResult = tnew(1024);
DWORD dwSize = 0, dwDownloaded = 0;
LPSTR pszOutBuffer = NULL;
BOOL bResults = FALSE;
HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(_T("WinHTTP Example/1.0"), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession)
hConnect = WinHttpConnect(hSession, L"www.microsoft.com", INTERNET_DEFAULT_HTTPS_PORT, 0); // Specify an HTTP server.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect, _T("GET"), NULL, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); // Create an HTTP request handle.
if (hRequest)
bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); // Send a request.
if (bResults)
bResults = WinHttpReceiveResponse(hRequest, NULL); // End the request.
if (bResults) // Keep checking for data until there is nothing left.
{
do
{
dwSize = 0;
if (WinHttpQueryDataAvailable(hRequest, &dwSize)) // Check for available data.
{
pszOutBuffer = new CHAR[dwSize + 1];
ZeroMemory(pszOutBuffer, dwSize + 1);
if (WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) // Read the data.
StringCchPrintfA(sResult, 1024, "%s%s", sResult, pszOutBuffer);
}
}
while (dwSize > 0);
}
delete[] pszOutBuffer;
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}

Windows SDK Troubles

The Background:
I'm trying to write a windows program to serve as a client-side HTML viewer. Since the development IDE that I'm using (Code::Blocks v:13.12) doesn't come with a copy of winHTTP.lib, I had to download a copy of the Windows 10 SDK.
I then proceeded to write the following code:
#include <iostream>
#include <windows.h>
#include <...\Windows Kits\10\Include\10.0.10586.0\um\winhttp.h>
const LPCWSTR Agent = (wchar_t*)TEXT("User Agent");
const LPCWSTR Url = (wchar_t*)TEXT("www.google.com");
using namespace std;
int main()
{
// Declare Variables:
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(Agent,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0 );
// Specify an HTTP server.
if( hSession )
hConnect = WinHttpConnect( hSession, Url, INTERNET_DEFAULT_HTTPS_PORT, 0 );
// Create an HTTP request handle.
if( hConnect )
hRequest = WinHttpOpenRequest(hConnect, L"GET", NULL,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_SECURE );
/*
* <INCOMPLETE>
*/
// Terminate Process Successfully:
return EXIT_SUCCESS;
}
(Note: "...\Windows Kits\" is the path to my copy of the Windows SDK)
I then proceeded to link my program to the "winHTTP.lib" library.
The Problem:
Now, every time I try to compile my program, I get the following result:
In "...\Windows Kits\10\Include\10.0.10586.0\um\winhttp.h":
fatal error: winapifamily.h: No such file or directory
It seems that for whatever reason, my compiler is unable to find this "winapifamily.h".
The Questions:
Is this a problem with the compiler, the Windows SDK, or have I made an error in my code?
How can I fix this? Do I need to install another copy of the winHTTP Library, or is there a more simple way for me to solve the problem?
Forgot To Mention:
I suspect that this might be a problem with the search path for my IDE not lining up with the Windows 10 SDK. Is this a reasonable conclusion to draw?

How can I find if a webpage exists using WinINet

I am trying to connect and ensure various pages exist on a webserver provided by an instrument we design. I am trying to do this through C++ Win32 using WinInet commands.
I am happy that I have connected correctly to the webserver via HTTP:
hInternet = InternetOpen("Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0/*INTERNET_FLAG_ASYNC*/);
hhttp = InternetConnect(hInternet, "192.168.111.222", INTERNET_DEFAULT_HTTP_PORT, "admin", "admin", INTERNET_SERVICE_HTTP, 0, 0);
I believe I then have to open a request.
hHttpRequest = HttpOpenRequest(hhttp, "GET", "galogo.png", NULL, "192.168.111.222", lplpszAcceptTypes, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE, 0);
and then send the request.
HttpSendRequest(hHttpRequest, NULL, 0, NULL, 0)
Note: 192.168.111.222 is the address of the unit running the webserver and galogo.png is an image displayed on the home page. Please also note that I am error checking between each statements so if I do disconnect the Ethernet then I do get a failure.
Initially I did try just connecting to the home.html page but this always passed so I thought I should try and get the image but I am probably lacking in knowledge. Other examples seem to then stream data but I wasn't sure if I needed to do this.
Most of the examples I have seen seem to show the HtppSendRequest in this format and I don't really understand about headers etc. Maybe it is here I am going wrong.
The HttpQueryInfo function will give header information relating to the request, and you can extract the HTTP status code from this.
You may be able to achieve the result more easily using higher level WinINet functions. I would suggest a sequence consisting of InternetOpen, InternetOpenUrl, HttpQueryInfo and then repeated calls to InternetReadFile if the HTTP status code is OK.
This Delphi code (from Delphi 7, so pre-Unicode) seems to do the job: -
function GetUrlContent(const Agent, Url: string): string;
var
NetHandle: HINTERNET;
UrlHandle: HINTERNET;
Buffer: array [0..1024] of Char;
BytesRead: DWORD;
Dummy: DWORD;
BufLen: DWORD;
HttpStatus: string;
begin
Result := '';
NetHandle := InternetOpen(PChar(Agent), INTERNET_OPEN_TYPE_PRECONFIG,
nil, nil, 0);
if Assigned(NetHandle) then
begin
UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0,
INTERNET_FLAG_RELOAD, 0);
if Assigned(UrlHandle) then
// UrlHandle valid? Proceed with download.
try
BufLen := Length(Buffer);
Dummy := 0;
// only get the file if the HTTP status code is 200
if HttpQueryInfo(UrlHandle, HTTP_QUERY_STATUS_CODE, #Buffer[0], BufLen, Dummy) then
begin
HttpStatus := Buffer;
if HttpStatus = '200' then
begin
FillChar(Buffer, SizeOf(Buffer), 0);
repeat
Result := Result + Buffer;
FillChar(Buffer, SizeOf(Buffer), 0);
InternetReadFile(UrlHandle, #Buffer, SizeOf(Buffer), BytesRead);
until BytesRead = 0;
end
else begin
raise Exception.CreateFmt('HTTP status code %s', [HttpStatus]);
end;
end
else begin
raise Exception.Create('Unable to read HTTP status code');
end;
finally
InternetCloseHandle(UrlHandle);
end
else begin
// UrlHandle is not valid. Raise an exception.
raise Exception.CreateFmt('Cannot open URL %s', [Url]);
end;
InternetCloseHandle(NetHandle);
end
else begin
// NetHandle is not valid. Raise an exception.
raise Exception.Create('Unable to initialize WinINet');
end;
end;
So, using a combination of cURL and Wireshark I'm finally there. I was making some fundamental mistakes but basically on the right track.
First open the connection and connect as previously stated, making sure it is not ASYNC (this lead to some overlapped IO errors):
hInternet = InternetOpen("Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0/*INTERNET_FLAG_ASYNC*/);
hhttp = InternetConnect(hInternet, "192.168.111.222", INTERNET_DEFAULT_HTTP_PORT, "admin", "admin", INTERNET_SERVICE_HTTP, 0, 0);
I needed to create the request and then send it. I only needed to specify the page as the request will take in connection details.
hHttpRequest = HttpOpenRequest(hhttp, "GET", "home.html", NULL, NULL, lplpszAcceptTypes, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE, 0);
HttpSendRequest(hHttpRequest, NULL, 0, NULL, 0);
Then use the HttpQueryInfo function to retrieve the status and convert back to integer. Make sure you are sending the handle from the Request not the Connect.
//These are defined earlier
DWORD statCharLen = 0;
char statChar[256]="";
statCharLen = sizeof(statChar);
HttpQueryInfo(hHttpRequest, HTTP_QUERY_STATUS_CODE, statChar, &statCharLen, NULL);
Finally shut down connection:
InternetCloseHandle(hInternet)
Thanks
Its is Simple following are the Steps:
1- Open Connection
2- Connect
3- Open request
4- Send request
5- Read file
6- Save file (as png or jpg)
7- Close handles
The code is as follow:
#include <iostream>
#include <string>
#include <Windows.h>
#include <wininet.h>
#pragma comment(lib, "wininet")
using namespace std;
void download(string domain,string url,string filepath)
{
//Step 1:
HINTERNET hIntSession = InternetOpenA("MyApp", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
//Step 2:
HINTERNET hHttpSession = InternetConnectA(hIntSession, domain.c_str(), 80, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
//Step 3:
HINTERNET hHttpRequest = HttpOpenRequestA( hHttpSession, "GET",url.c_str(),0, 0, 0, INTERNET_FLAG_RELOAD, 0);
TCHAR* szHeaders = L"";
CHAR szReq[1024] = "";
//Step 4:
if( !HttpSendRequest(hHttpRequest, szHeaders, wcslen(szHeaders), szReq, strlen(szReq))) {
DWORD dwErr = GetLastError();
cout<<"error "<<dwErr<<endl;
/// handle error
}
TCHAR szBuffer[1025];
DWORD dwRead=0;
FILE *f;
f=fopen(filepath.c_str(),"wb");
//Step 5 & 6:
while(InternetReadFile(hHttpRequest,szBuffer, 1024, &dwRead) && dwRead)
{
fwrite(szBuffer,sizeof(BYTE),1024,f);
dwRead=0;
}
fclose(f);
//Step 7:
InternetCloseHandle(hHttpRequest);
InternetCloseHandle(hHttpSession);
InternetCloseHandle(hIntSession);
}
int main()
{
download("www.stacktoheap.com","images/stackoverflow.png","C:\\Example\\example.png");
}

POST request to a friendly URL fails

I'm trying to post an HTTP request to an HTML form that is accessible through the friendly URL address.
But when I post it, library returns error code "bad URL provided".
HINTERNET hRequest = HttpOpenRequest(hConn, L"POST", L"newform",
NULL, L"http://www.example.com/add/newform/",
rgpszAcceptTypes, NULL, NULL);
DWORD error_code = GetLastError();
bool hResult = HttpSendRequestW(hRequest,NULL,NULL,NULL,NULL);
error_code = GetLastError(); //12005 - bad URL provided
What am I doing wrong, why does my request fails?
The server name (www.site.com in your above example) must be specified in the call to InternetConnect, as must the protocol (INTERNET_SERVICE_HTTP).
In the call to HttpOpenRequest you only give the name of the "object", which in this case is "/add/newform/".
For example (added for more clarity):
HINTERNET hConn = InternetConnect(hInternet, L"www.site.com", INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
HINTERNET hRequest = HttpOpenRequest(hConn, L"POST", L"/add/newform", NULL, NULL,
rgpszAcceptTypes, NULL, NULL);

POST form data using WinInet c++

I'm trying to make this program connect to a website and submit form data in order to login, but I don't know what I'm doing wrong. I have heard of others like curl and Winsock but I chose the WinINet library. So just for the testing of this program I've been using the website Pastebin to post to. So far, I haven't seen any results from this. If this program succeeds in posting the form data it will give me the header to the location of the post on their site.
Am I writing the form data char* correctly? I have seen on other stackoverflow posts where they had a large amount of dashes before some number then the put their form data.
Do I need to add something to it make it simulate clicking the submit button?
Do I need to write out values for each elements on the form?
I have tried HttpAddRequestHeaders and that didn't help me.
Also, I get the ERROR_INSUFFICIENT_BUFFER error on HttpOpenRequest but it still returns a valid HINTERNET.
#include <Windows.h>
#include <WinInet.h>
#include <iostream>
#pragma comment( lib,"Wininet.lib")
using namespace std;
char* getheaders(HINTERNET hRequest){
DWORD dwInfoLevel=HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength=10;
char* pInfoBuffer=(char*)malloc(dwInfoBufferLength+1);
while(!HttpQueryInfo(hRequest,dwInfoLevel,pInfoBuffer,&dwInfoBufferLength,NULL)){
if (GetLastError()==ERROR_INSUFFICIENT_BUFFER){
free(pInfoBuffer);
pInfoBuffer=(char*)malloc(dwInfoBufferLength+1);
}else{
fprintf(stderr,"HttpQueryInfo failed, error = %d (0x%x)\n",GetLastError(),GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
return pInfoBuffer;
}
void readfile(HINTERNET hRequest,char** buffs,int size){
DWORD dwBytesAvailable;
DWORD dwBytesRead;
for(int i=0;i<size;i++){
if(!InternetQueryDataAvailable(hRequest,&dwBytesAvailable,0,0)) break;
buffs[i]=(char*)malloc(dwBytesAvailable+1);
bool bResult=InternetReadFile(hRequest,buffs[i],dwBytesAvailable,&dwBytesRead);
if(!bResult | dwBytesRead==0) break;
}
}
int main(int argc,char** argv){
char* hdrs="Content-Type: application/x-www-form-urlencoded";
char* frmdata="paste_code=test";
LPCSTR accept[2]={"*/*", NULL};
HINTERNET hSession = InternetOpen("http generic",INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hSession, "www.pastebin.com",INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
HINTERNET hRequest = HttpOpenRequest(hConnect, "GET","/", NULL, NULL, accept, 0, 0);
//ERROR_INSUFFICIENT_BUFFER (122) with "accept".
bool send=HttpSendRequest(hRequest, hdrs, strlen(hdrs), NULL,NULL);
if(!send){
printf("HttpSendRequest failed, code=%d",GetLastError());
system("pause>nul");
return 0;
}
char* heads=getheaders(hRequest);
printf("%s\n\n\n\n",heads);
HINTERNET hRequest2 = HttpOpenRequest(hConnect, "POST","/", NULL, NULL, accept, 0, 0);
//ERROR_INSUFFICIENT_BUFFER (122) with "accept".
send=HttpSendRequest(hRequest2, hdrs, strlen(hdrs), frmdata,strlen(frmdata));
if(!send){
printf("HttpSendRequest failed, code=%d",GetLastError());
system("pause>nul");
return 0;
}
heads=getheaders(hRequest);
printf("%s\n\n\n\n",heads);
InternetCloseHandle(hRequest);
InternetCloseHandle(hRequest2);
InternetCloseHandle(hConnect);
InternetCloseHandle(hSession);
system("pause>nul");
return 0;
}
Your code is nearly correct, you must make sure of the following points:
char* hdrs="Content-Type: application/x-www-form-urlencoded";
you must make sure that your return object from POST message will be of type x-www-form-urlencoded or JSON . if it's JSON you
need to define char* hdrs="Content-Type: application/json\r\n";
Note: you must append \r\n to the hdrs.
try to call readFile method with buffer of size 10000 for example
and print buffer , it will print the output of the response to the
connection
In HINTERNET hRequest2 = HttpOpenRequest(hConnect, "POST","/", NULL, NULL, accept, 0, 0); instead of "/" you must call the path of
the requested API for example: .
In HINTERNET hRequest2 = HttpOpenRequest(hConnect, "POST", "/users/jsonlogin", NULL, NULL, accept, 0, 0);