1806 lines
50 KiB
C++

/*
* File: gsoapWinHTTPX.cpp
*
* This file is a modified version of gsoapWinInet.cpp. The Initial Developer
* of the Original Code of the gsoapWinInet.cpp file is Robert A. van Engelen.
* Copyright (C) 2000-2004, Robert van Engelen, Genivia, Inc., All Rights Reserved.
* It has been modified by Intel(R) Corporation in purpose to permit
* digest authentication, to block basic authentication and to provide
* the support for client authentication mechanism.
* This file converted to use WinHTTP instead of WinInet library and renamed appropriately
*
* gSOAP redistribution notes:
* Feel free to use, improve, and share. I would appreciate
* notification of any bugs found/fixed, or improvements made. This
* code has not been extensively tested, so use at your own risk.
*/
#ifdef USE_WINSOCK2
#include <winsock2.h>
#endif
/* system */
#define _WINSOCKAPI_
#include <windows.h>
#include <crtdbg.h>
#include <winhttp.h>
#include <tchar.h>
#include <sstream>
#include <string>
using namespace std;
/* local */
#include "gsoapWinHTTP.h"
/* ensure that the winhttp library is linked */
#pragma comment( lib, "winhttp.lib" )
#define UNUSED_ARG(x) (x)
#define INVALID_BUFFER_LENGTH ((DWORD)-1)
/* plugin id */
static const char winhttp_id[] = "winhttp-5.1";
/* plugin private data */
struct winhttp_data
{
HINTERNET hInternet; /* internet session handle */
HINTERNET hConnection; /* current connection handle */
BOOL bDisconnect; /* connection is disconnected */
DWORD dwRequestFlags; /* extra request flags from user */
char * pBuffer; /* send buffer */
size_t uiBufferLenMax; /* total length of the message */
size_t uiBufferLen; /* length of data in buffer */
BOOL bIsChunkSize; /* expecting a chunk size buffer */
/* Intel(R) additions */
const char * username; /* user name for digest auth */
const char * password; /* password for digest auth */
const _TCHAR * certificateName; /* Common Name of the client certificate */
PCCERT_CONTEXT certificate; /* client certificate */
BOOL local; /* true if an app runs locally */
BOOL krb; /* true if an user uses the Kerberos Authentication scheme */
BOOL localMachineStore; /* true if the client certificate is installed
otherwise the authentication scheme is digest */
wstring headers; /* HTTP headers from http response. One line for each header*/
#ifdef SOAP_DEBUG
/* this is only used for DBGLOG output */
char * pszErrorMessage; /* winhttp/system error message */
#endif
};
/* forward declarations */
static BOOL
winhttp_init(
struct soap * soap,
struct winhttp_data * a_pData,
DWORD a_dwRequestFlags );
static int
winhttp_copy(
struct soap * soap,
struct soap_plugin * a_pDst,
struct soap_plugin * a_pSrc );
static void
winhttp_delete(
struct soap * soap,
struct soap_plugin * a_pPluginData );
static SOAP_SOCKET
winhttp_connect(
struct soap * soap,
const char * a_pszEndpoint,
const char * a_pszHost,
int a_nPort );
static int
winhttp_post_header(
struct soap * soap,
const char * a_pszKey,
const char * a_pszValue );
static int
winhttp_fsend(
struct soap * soap,
const char * a_pBuffer,
size_t a_uiBufferLen );
static size_t
winhttp_frecv(
struct soap * soap,
char * a_pBuffer,
size_t a_uiBufferLen );
static int
winhttp_disconnect(
struct soap * soap );
void CALLBACK
winhttp_callback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength );
static BOOL
winhttp_have_connection(
struct soap * soap,
struct winhttp_data * a_pData );
static DWORD
winhttp_set_timeout(
struct soap * soap,
struct winhttp_data * a_pData,
const char * a_pszTimeout,
DWORD a_dwOption,
int a_nTimeout );
static BOOL
winhttp_find_cert(
struct soap * soap,
HANDLE hStoreHandle,
const _TCHAR * certName,
PCCERT_CONTEXT *pCertContext );
#ifdef SOAP_DEBUG
/* this is only used for DBGLOG output */
static const char *
winhttp_error_message(
struct soap * a_pData,
DWORD a_dwErrorMsgId );
static void
winhttp_free_error_message(
struct winhttp_data * a_pData );
#else
#define winhttp_free_error_message(x)
#endif
static void
winhttp_set_headers(
struct soap* soap,
const WCHAR *headers);
static wchar_t *
convert_to_unicode(
const char * str, int strLen);
DWORD
select_scheme(
DWORD suppSchemes);
/* plugin registration */
int
winhttp_plugin(
struct soap * soap,
struct soap_plugin * a_pPluginData,
void * a_dwRequestFlags )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: plugin registration\n", soap ));
a_pPluginData->id = winhttp_id;
a_pPluginData->fcopy = winhttp_copy;
a_pPluginData->fdelete = winhttp_delete;
a_pPluginData->data = (void*) malloc( sizeof(struct winhttp_data) );
if ( !a_pPluginData->data )
{
return SOAP_EOM;
}
if ( !winhttp_init( soap,
(struct winhttp_data *) a_pPluginData->data,
(DWORD) a_dwRequestFlags ) )
{
free( a_pPluginData->data );
return SOAP_EOM;
}
#ifdef SOAP_DEBUG
if ( (soap->omode & SOAP_IO) == SOAP_IO_STORE )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: use of SOAP_IO_STORE is not recommended\n", soap ));
}
#endif
return SOAP_OK;
}
/* initialize private data */
int winhttp_init(
struct soap * soap,
struct winhttp_data * a_pData,
DWORD a_dwRequestFlags )
{
BOOL bResult;
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: init private data\n", soap ));
memset( a_pData, 0, sizeof(struct winhttp_data) );
a_pData->dwRequestFlags = a_dwRequestFlags;
/* start our internet session */
//Conversion Notes:
//WINHTTP_ACCESS_TYPE_DEFAULT_PROXY retrieves proxy from WINHTTP registry entry
//it is not the same as browser proxy configuration (see MSDN Help)
a_pData->hInternet = WinHttpOpen(
L"gSOAP", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 );
if ( !a_pData->hInternet )
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: init, error %d (%s) in InternetOpen\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
winhttp_free_error_message( a_pData );
return FALSE;
}
//set connection context
bResult = WinHttpSetOption(a_pData->hInternet,
WINHTTP_OPTION_CONTEXT_VALUE,
(void*)soap, sizeof(void*));
if( !bResult )
{
WinHttpCloseHandle( a_pData->hInternet );
a_pData->hInternet = NULL;
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: init, error %d (%s) in WinHttpSetOption\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
winhttp_free_error_message( a_pData );
return FALSE;
}
/* set the timeouts, if any of these fail the error isn't fatal */
winhttp_set_timeout( soap, a_pData, "connect",
WINHTTP_OPTION_CONNECT_TIMEOUT, soap->connect_timeout );
winhttp_set_timeout( soap, a_pData, "receive",
WINHTTP_OPTION_RECEIVE_TIMEOUT, soap->recv_timeout );
winhttp_set_timeout( soap, a_pData, "send",
WINHTTP_OPTION_SEND_TIMEOUT, soap->send_timeout );
/* set up the callback function so we get notifications */
WinHttpSetStatusCallback( a_pData->hInternet, winhttp_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL );
/* set all of our callbacks */
soap->fopen = winhttp_connect;
soap->fposthdr = winhttp_post_header;
soap->fsend = winhttp_fsend;
soap->frecv = winhttp_frecv;
soap->fclose = winhttp_disconnect;
return TRUE;
}
/* copy the private data structure */
static int
winhttp_copy(
struct soap * soap,
struct soap_plugin * a_pDst,
struct soap_plugin * a_pSrc )
{
UNUSED_ARG( soap );
UNUSED_ARG( a_pDst );
UNUSED_ARG( a_pSrc );
_ASSERTE( !"winhttp doesn't support copy" );
return SOAP_FATAL_ERROR;
}
/* deallocate of our private structure */
static void
winhttp_delete(
struct soap * soap,
struct soap_plugin * a_pPluginData )
{
struct winhttp_data * pData =
(struct winhttp_data *) a_pPluginData->data;
UNUSED_ARG( soap );
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: delete private data\n", soap ));
/* force a disconnect of any existing connection */
pData->bDisconnect = TRUE;
winhttp_have_connection( soap, pData );
/* close down the internet */
if ( pData->hInternet )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: closing internet handle\n", soap));
WinHttpCloseHandle( pData->hInternet );
pData->hInternet = NULL;
}
/* free our data */
winhttp_free_error_message( pData );
free( a_pPluginData->data );
/* clean up credentials */
//Conversion Notes: TBD Did not find appropriate options in WinHttp
//InternetSetOption(0,INTERNET_OPTION_DIGEST_AUTH_UNLOAD,0,0);
}
/* gsoap documentation:
Called from a client proxy to open a connection to a Web Service located
at endpoint. Input parameters host and port are micro-parsed from endpoint.
Should return a valid file descriptor, or SOAP_INVALID_SOCKET and
soap->error set to an error code. Built-in gSOAP function: tcp_connect
*/
static SOAP_SOCKET
winhttp_connect(
struct soap * soap,
const char * a_pszEndpoint,
const char * a_pszHost,
int a_nPort )
{
//Needed for ATL conversion macros
URL_COMPONENTS urlComponents;
wchar_t szUrlPath[MAX_PATH];
wchar_t szHost[MAX_PATH];
DWORD dwFlags;
HINTERNET hConnection = NULL;
HINTERNET hHttpRequest = NULL;
BOOL bResult = FALSE;
struct winhttp_data * pData =
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
soap->error = SOAP_OK;
/* we parse the URL ourselves so we don't use these parameters */
UNUSED_ARG( a_pszHost );
UNUSED_ARG( a_nPort );
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: connect, endpoint = '%s'\n", soap, a_pszEndpoint ));
/* we should be initialized but not connected */
_ASSERTE( pData->hInternet );
_ASSERTE( !pData->hConnection );
_ASSERTE( soap->socket == SOAP_INVALID_SOCKET );
/* parse out the url path */
memset( &urlComponents, 0, sizeof(urlComponents) );
urlComponents.dwStructSize = sizeof(urlComponents);
urlComponents.lpszHostName = szHost;
urlComponents.dwHostNameLength = MAX_PATH;
urlComponents.lpszUrlPath = szUrlPath;
urlComponents.dwUrlPathLength = MAX_PATH;
//Sending The maximum length of a URL in the address bar = 2048 characters.
wchar_t * a_pszEndpointW = convert_to_unicode(a_pszEndpoint, 2048);
bResult = WinHttpCrackUrl( a_pszEndpointW, 0, 0, &urlComponents );
free(a_pszEndpointW);
if ( !bResult )
{
//Conversion Notes: does nothing here
//WinHttpCloseHandle( hConnection );
soap->error = GetLastError();
DBGLOG( TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: connect, error %d (%s) in WinHttpCrackUrl\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
return SOAP_INVALID_SOCKET;
}
hConnection = WinHttpConnect( pData->hInternet,
szHost, urlComponents.nPort, 0);
if ( !hConnection )
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: connect, error %d (%s) in WinHttpConnect\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
return SOAP_INVALID_SOCKET;
}
/*
Note that although we specify HTTP/1.1 for the connection here, the
actual connection may be HTTP/1.0 depending on the settings in the
control panel. See the "Internet Options", "HTTP 1.1 settings".
*/
dwFlags = pData->dwRequestFlags;
if ( soap->omode & SOAP_IO_KEEPALIVE )
{
//Converion Notes: not found such a flag
//dwFlags |= INTERNET_FLAG_KEEP_CONNECTION;
}
if ( urlComponents.nScheme == INTERNET_SCHEME_HTTPS )
{
dwFlags |= WINHTTP_FLAG_SECURE;
}
hHttpRequest = WinHttpOpenRequest(
hConnection, L"POST", szUrlPath, L"HTTP/1.1",
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
dwFlags );
if ( !hHttpRequest )
{
WinHttpCloseHandle( hConnection );
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: connect, error %d (%s) in WinHttpOpenRequest\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
return SOAP_INVALID_SOCKET;
}
if (soap->proxy_userid && soap->proxy_passwd)
{
wchar_t* proxyUsernameW = convert_to_unicode(soap->proxy_userid, 32);
wchar_t* proxyPasswordW = convert_to_unicode(soap->proxy_passwd, 32);
int proxyUsername_len = strnlen_s(soap->proxy_userid, 32);
int proxyPassword_len = strnlen_s(soap->proxy_passwd, 32);
bool returnStatus=true;
if(!WinHttpSetCredentials(hHttpRequest,WINHTTP_AUTH_TARGET_PROXY,
WINHTTP_AUTH_SCHEME_BASIC,proxyUsernameW,proxyPasswordW,NULL))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpSetCredentials\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
returnStatus=false;
}
wmemset(proxyUsernameW,0,proxyUsername_len);
wmemset(proxyPasswordW,0,proxyPassword_len);
free(proxyUsernameW);
free(proxyPasswordW);
if (!returnStatus)
{
return SOAP_INVALID_SOCKET;
}
}
/* save the connection handle in our data structure */
pData->hConnection = hConnection;
/* return the http request handle as our file descriptor. */
_ASSERTE( sizeof(soap->socket) >= sizeof(HINTERNET) );
return (SOAP_SOCKET) hHttpRequest;
}
/* gsoap documentation:
Called by http_post and http_response (through the callbacks). Emits HTTP
key: val header entries. Should return SOAP_OK, or a gSOAP error code.
Built-in gSOAP function: http_post_header.
*/
static int
winhttp_post_header(
struct soap * soap,
const char * a_pszKey,
const char * a_pszValue )
{
HINTERNET hHttpRequest = (HINTERNET) soap->socket;
char szHeader[MAX_PATH];
int nLen;
BOOL bResult = FALSE;
struct winhttp_data * pData =
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
soap->error = SOAP_OK;
/* ensure that our connection hasn't been disconnected */
if ( !winhttp_have_connection( soap, pData ) )
{
return SOAP_EOF;
}
/* if this is the initial POST header then we initialize our send buffer */
if ( a_pszKey && !a_pszValue )
{
_ASSERTE( !pData->pBuffer );
pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
pData->uiBufferLen = 0;
/* Initialize username and password and remove them from SOAP structure
to disable gsoap from adding a Basic authentication header
with the username and password automatically */
pData->username = soap->userid;
pData->password = soap->passwd;
soap->userid = NULL;
soap->passwd = NULL;
/* if we are using chunk output then we start with a chunk size */
pData->bIsChunkSize = ( (soap->omode & SOAP_IO) == SOAP_IO_CHUNK );
}
else if ( a_pszValue )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: post_header, adding '%s: %s'\n",
soap, a_pszKey, a_pszValue ));
/* determine the maximum length of this message so that we can
correctly determine when we have completed the send */
if (!a_pszKey || !strcmp( a_pszKey, "Content-Length" ) )
{
_ASSERTE( pData->uiBufferLenMax == INVALID_BUFFER_LENGTH );
pData->uiBufferLenMax = strtoul( a_pszValue, NULL, 10 );
}
nLen = _snprintf( szHeader, MAX_PATH, "%s: %s\r\n", a_pszKey, a_pszValue );
if ( nLen < 0 )
{
return SOAP_EOM;
}
wchar_t * szHeaderW = convert_to_unicode(szHeader, MAX_PATH - 1);
bResult = WinHttpAddRequestHeaders( hHttpRequest,
szHeaderW,
nLen,
WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
free( szHeaderW );
#ifdef SOAP_DEBUG
/*
we don't return an error if this fails because it isn't
(or shouldn't be) critical.
*/
if ( !bResult )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: post_header, error %d (%s) in WinHttpAddRequestHeaders\n",
soap, soap->error, winhttp_error_message(soap,GetLastError()) ));
}
#endif
}
return SOAP_OK;
}
/* gsoap documentation:
Called for all send operations to emit contents of s of length n.
Should return SOAP_OK, or a gSOAP error code. Built-in gSOAP
function: fsend
Notes:
I do a heap of buffering here because we need the entire message available
in a single buffer in order to iterate through the sending loop. I had
hoped that the SOAP_IO_STORE flag would have worked to do the same, however
this still breaks the messages up into blocks. Although there were a number
of ways this could've been implemented, this works and supports all of the
possible SOAP_IO flags, even though the entire message is still buffered
the same as if SOAP_IO_STORE was used.
*/
static int
winhttp_fsend(
struct soap * soap,
const char * a_pBuffer,
size_t a_uiBufferLen )
{
#if defined(__cplusplus) && !defined(WITH_LEAN)
if (soap->os)
{ soap->os->write(a_pBuffer, a_uiBufferLen);
if (soap->os->good())
return SOAP_OK;
return SOAP_EOF;
}
#endif
/* char * tmp = new char[a_uiBufferLen + 1];
tmp[a_uiBufferLen] = 0;
memcpy(tmp, a_pBuffer, a_uiBufferLen);
fprintf(stdout, "*******************\nsending\n**************\n%s\n*************\n\n", tmp);*/
HINTERNET hHttpRequest = (HINTERNET) soap->socket;
BOOL bResult;
BOOL bRetryPost;
DWORD dwStatusCode;
DWORD dwStatusCodeLen;
int nResult = SOAP_OK;
HANDLE hStoreHandle = NULL;
wchar_t *proxy_username = NULL;
wchar_t *proxy_password = NULL;
struct winhttp_data * pData =
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
soap->error = SOAP_OK;
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, data len = %lu bytes\n", soap, a_uiBufferLen ));
_ASSERTE( a_uiBufferLen > 0 );
/* ensure that our connection hasn't been disconnected */
if ( !winhttp_have_connection( soap, pData ) )
{
return SOAP_EOF;
}
/* initialize on our first time through. pData->pBuffer will always be
non-null if this is not the first call. */
if ( !pData->pBuffer )
{
/*
If we are using chunked sending, then we don't know how big the
buffer will need to be. So we start with a 0 length buffer and
grow it later to ensure that it is always large enough.
uiBufferLenMax = length of the allocated memory
uiBufferLen = length of the data in the buffer
*/
if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK )
{
/* we make the initial allocation large enough for this chunksize
buffer, plus the next chunk of actual data, and a few extra
bytes for the final "0" chunksize block. */
size_t uiChunkSize = strtoul( a_pBuffer, NULL, 16 );
pData->uiBufferLenMax = uiChunkSize + a_uiBufferLen + 16;
}
else if ( a_uiBufferLen == pData->uiBufferLenMax )
{
/*
If the currently supplied buffer from gsoap holds the entire
message then we just use their buffer and avoid any memory
allocation. This will only be true when (1) we are not using
chunked send (so uiBufferLenMax has been previously set to
the Content-Length header length), and (2) gsoap is sending
the entire message at one time.
*/
pData->pBuffer = (char *) a_pBuffer;
pData->uiBufferLen = a_uiBufferLen;
}
_ASSERTE( pData->uiBufferLenMax != INVALID_BUFFER_LENGTH );
}
/*
If we can't use the gsoap buffer, then we need to allocate our own
buffer for the entire message. This is because authentication may
require the entire message to be sent multiple times. Since this send
is only a part of the message, we need to buffer until we have the
entire message.
*/
if ( pData->pBuffer != a_pBuffer )
{
/*
We already have a buffer pointer, this means that it isn't the
first time we have been called. We have allocated a buffer and
are current filling it.
If we don't have enough room in the our buffer to add this new
data, then we need to reallocate. This case will only occur with
chunked sends.
*/
size_t uiNewBufferLen = pData->uiBufferLen + a_uiBufferLen;
if ( !pData->pBuffer || uiNewBufferLen > pData->uiBufferLenMax )
{
while ( uiNewBufferLen > pData->uiBufferLenMax )
{
pData->uiBufferLenMax = pData->uiBufferLenMax * 2;
}
pData->pBuffer = (char *) realloc( pData->pBuffer, pData->uiBufferLenMax );
if ( !pData->pBuffer )
{
return SOAP_EOM;
}
}
if (memcpy_s(pData->pBuffer + pData->uiBufferLen, pData->uiBufferLenMax - pData->uiBufferLen,
a_pBuffer, a_uiBufferLen))
{
return SOAP_ERR;
}
pData->uiBufferLen = uiNewBufferLen;
/*
if we are doing chunked transfers, and this is a chunk size block,
and it is "0", then this is the last block in the transfer and we
can set the maximum size now to continue to the actual send.
*/
if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK
&& pData->bIsChunkSize
&& a_pBuffer[2] == '0' && !isalnum(a_pBuffer[3]) )
{
pData->uiBufferLenMax = pData->uiBufferLen;
}
}
/*
if we haven't got the entire length of the message yet, then
we return to gsoap and let it continue
*/
if ( pData->uiBufferLen < pData->uiBufferLenMax )
{
/* toggle our chunk size marker if we are chunking */
pData->bIsChunkSize =
((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
&& !pData->bIsChunkSize;
return SOAP_OK;
}
_ASSERTE( pData->uiBufferLen == pData->uiBufferLenMax );
/* we've now got the entire message, now we can enter our sending loop */
bRetryPost = TRUE;
int is_emulator = 0;
if (strstr(soap->path, ".asmx") == (soap->path+ strnlen_s(soap->path, SOAP_TAGLEN - 1) -5))
{
is_emulator = 1;
}
if(((winhttp_data*)soap->plugins->data)->krb == TRUE)
{
DWORD d = WINHTTP_ENABLE_SPN_SERVER_PORT;
if(!WinHttpSetOption(
hHttpRequest,
WINHTTP_OPTION_SPN,
(LPVOID) (&d),
sizeof(DWORD)))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpSetOption\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
}
}
/*
Allow only 3 attempts.
First attempt: No client certificate
Second attempt: No credentials
Third attempt: Success
*/
int attemptNum = 0;
while ( bRetryPost && attemptNum < 3 )
{
nResult = SOAP_OK;
bRetryPost = FALSE;
bResult = WinHttpSendRequest(
hHttpRequest, WINHTTP_NO_ADDITIONAL_HEADERS , 0,
pData->pBuffer, (DWORD) pData->uiBufferLen,
(DWORD) pData->uiBufferLen, (DWORD_PTR)soap);
if (bResult)
bResult = WinHttpReceiveResponse(hHttpRequest, NULL );
/*
get the headers from the response and store them in plugin data accessible by the calling
application
*/
if (bResult)
{
DWORD dwSize;
BOOL bHeadersResult = WinHttpQueryHeaders( hHttpRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize,
WINHTTP_NO_HEADER_INDEX);
DWORD error = GetLastError();
// Allocate memory for the buffer.
if (error== ERROR_INSUFFICIENT_BUFFER)
{
// Allocate memory for the buffer.
WCHAR *lpHeaders = new WCHAR[dwSize/sizeof(WCHAR)];
// Use HttpQueryInfo to obtain the header buffer.
bHeadersResult = WinHttpQueryHeaders( hHttpRequest,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
lpHeaders,
&dwSize,
WINHTTP_NO_HEADER_INDEX);
if (bHeadersResult)
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, HTTP Headers = %S\n",
soap, lpHeaders));
winhttp_set_headers(soap,lpHeaders);
}
else
{
error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpQueryHeaders\n",
soap, error, winhttp_error_message(soap,error) ));
}
delete[] lpHeaders;
}
else
{
error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpQueryHeaders\n",
soap, error, winhttp_error_message(soap,error) ));
}
}
/*
get the status code from the response to determine if we need
to authorize
*/
if (bResult )
{
dwStatusCodeLen = sizeof(dwStatusCode);
bResult = WinHttpQueryHeaders(
hHttpRequest,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&dwStatusCode,
&dwStatusCodeLen,
WINHTTP_NO_HEADER_INDEX );
if (bResult)
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, HTTP status code = %lu\n",
soap, dwStatusCode));
}
}
if ( !bResult )
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpSendRequest\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
/* Client certificate required */
if(soap->error == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED)
{
soap->error = HTTP_STATUS_FORBIDDEN;
bRetryPost = FALSE;
/* Choose which personal store to use : Current User or Local Machine*/
DWORD flags;
if (((winhttp_data*)soap->plugins->data)->localMachineStore)
{
flags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
}
else
{
flags = CERT_SYSTEM_STORE_CURRENT_USER;
}
/* Open the personal store*/
if ( !(hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM, // The store provider type
0, // The encoding type is not needed
NULL, // Use the default HCRYPTPROV
flags, // Set the store location in a registry location
L"MY" // The store name as a Unicode string
)))
{
/* Cannot open the certificates store - exit immediately */
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in CertOpenSystemStore\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_SSL_ERROR;
break;
}
if (pData->certificate == NULL) {
if (!winhttp_find_cert(soap,hStoreHandle,pData->certificateName,&pData->certificate))
{
/* Cannot find requested certificate - exit immediately */
nResult = SOAP_SSL_ERROR;
break;
}
}
if(!WinHttpSetOption(hHttpRequest,WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
(LPVOID)pData->certificate,sizeof(CERT_CONTEXT)))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpSetOption\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
break;
}
attemptNum ++;
bRetryPost = TRUE;
continue;
}
else
{
if(((winhttp_data*)soap->plugins->data)->krb == FALSE)
{
DWORD suppSchemes;
DWORD firstScheme;
DWORD authTarget;
DWORD actualScheme;
if ( !WinHttpQueryAuthSchemes( hHttpRequest,
&suppSchemes,
&firstScheme,
&authTarget))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpQueryAuthSchemes\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
break;
}
wchar_t* usernameW = convert_to_unicode(pData->username, 32);
wchar_t* passwordW = convert_to_unicode(pData->password, 32);
int username_len = strnlen_s(pData->username, 32);
int password_len = strnlen_s(pData->password, 32);
if(is_emulator)
{
actualScheme = WINHTTP_AUTH_SCHEME_BASIC;
}
else
{
actualScheme = WINHTTP_AUTH_SCHEME_DIGEST;
}
if(!WinHttpSetCredentials(hHttpRequest,authTarget,
actualScheme,usernameW,passwordW,NULL))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpSetCredentials\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
wmemset(usernameW,0,username_len);
wmemset(passwordW,0,password_len);
free(usernameW);
free(passwordW);
break;
}
wmemset(usernameW,0,username_len);
wmemset(passwordW,0,password_len);
free(usernameW);
free(passwordW);
bRetryPost = TRUE;
pData->bDisconnect = FALSE;
attemptNum ++;
continue;
}
else
{
nResult = SOAP_HTTP_ERROR;
bRetryPost = FALSE;
break;
}
}
}
/*
if we need authentication, then request the user for the
appropriate data. Their reply is saved into the request so
that we can use it later.
*/
switch ( dwStatusCode )
{
case HTTP_STATUS_DENIED:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, user authenication required\n",
soap ));
{
/* clean up the HTML formatted data */
LPSTR buffer;
DWORD dwBufSize = 0;
DWORD dwDataReaded = 0;
do
{
if(!WinHttpQueryDataAvailable(hHttpRequest,&dwBufSize))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpQueryDataAvailable\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
dwBufSize = 0;
}
buffer = new char[dwBufSize + 1];
if(!buffer)
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, cannot allocate memory\n",
soap ));
nResult = SOAP_HTTP_ERROR;
dwBufSize = 0;
}
else
{
memset(buffer,0,dwBufSize + 1);
if(!WinHttpReadData(hHttpRequest, (LPVOID)buffer, dwBufSize,
&dwDataReaded))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpReadData\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
dwBufSize = 0;
}
delete [] buffer;
}
}while(dwBufSize > 0);
if(nResult == SOAP_HTTP_ERROR)
{
break;
}
/*
Query HTTP header for an authentication schemes
supported by the server side
*/
DWORD suppSchemes;
DWORD firstScheme;
DWORD authTarget;
DWORD actualScheme;
if ( !WinHttpQueryAuthSchemes( hHttpRequest,
&suppSchemes,
&firstScheme,
&authTarget))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpQueryAuthSchemes\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
break;
}
int is_emulator = 0;
if (strstr(soap->path, ".asmx") == (soap->path+ strnlen_s(soap->path, SOAP_TAGLEN - 1) -5))
{
is_emulator = 1;
}
/*
Support the basic authentication scheme only if we are working
over Intel(R) AMT Emulator.
*/
if( is_emulator == 0 && (suppSchemes & WINHTTP_AUTH_SCHEME_BASIC))
{
nResult = SOAP_HTTP_ERROR;
break;
}
/* Select required authentication scheme */
actualScheme = select_scheme(suppSchemes);
if(actualScheme == 0)
{
nResult = SOAP_HTTP_ERROR;
break;
}
if(actualScheme == WINHTTP_AUTH_SCHEME_NEGOTIATE &&
((winhttp_data*)soap->plugins->data)->krb == TRUE)
{
BOOL res;
DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
DWORD dataSize = sizeof(DWORD);
res = WinHttpSetOption(
hHttpRequest,
WINHTTP_OPTION_AUTOLOGON_POLICY,
&data,
dataSize);
}
else
{
wchar_t* usernameW = convert_to_unicode(pData->username, 32);
wchar_t* passwordW = convert_to_unicode(pData->password, 32);
int username_len = strnlen_s(pData->username, 32);
int password_len = strnlen_s(pData->password, 32);
if(is_emulator)
{
actualScheme = WINHTTP_AUTH_SCHEME_BASIC;
}
else
{
actualScheme = WINHTTP_AUTH_SCHEME_DIGEST;
}
if(!WinHttpSetCredentials(hHttpRequest,authTarget,
actualScheme,usernameW,passwordW,NULL))
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in WinHttpSetCredentials\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
nResult = SOAP_HTTP_ERROR;
wmemset(usernameW,0,username_len);
wmemset(passwordW,0,password_len);
free(usernameW);
free(passwordW);
break;
}
wmemset(usernameW,0,username_len);
wmemset(passwordW,0,password_len);
free(usernameW);
free(passwordW);
}
/*
we may have been disconnected by the error. Since we
are going to try again, we will automatically be
reconnected. Therefore we want to disregard any previous
disconnection messages.
*/
pData->bDisconnect = FALSE;
/*
indicate the request failed and try again if this is the
first failing request
*/
attemptNum++;
/* set default response in case retry limit exceeded */
nResult = SOAP_HTTP_ERROR;
bRetryPost = TRUE;
continue;
}
case HTTP_STATUS_OK:
nResult = SOAP_OK;
break;
/* the client certificate was not accepted */
case HTTP_STATUS_FORBIDDEN:
nResult = SOAP_SSL_ERROR;
continue;
case 400:
nResult = SOAP_OK;
break;
case 500:
nResult = SOAP_OK;
break;
default:
nResult = SOAP_HTTP_ERROR;
break;
}
}
/* if we have an allocated buffer then we can deallocate it now */
if ( pData->pBuffer != a_pBuffer )
{
free( pData->pBuffer );
}
pData->pBuffer = 0;
pData->uiBufferLen = 0;
pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
return nResult;
}
/* gsoap documentation:
Called for all receive operations to fill buffer s of maximum length n.
Should return the number of bytes read or 0 in case of an error, e.g. EOF.
Built-in gSOAP function: frecv
*/
static size_t
winhttp_frecv(
struct soap * soap,
char * a_pBuffer,
size_t a_uiBufferLen )
{
#if defined(__cplusplus) && !defined(WITH_LEAN)
if (soap->is)
{ if (soap->is->good())
return soap->is->read(a_pBuffer, a_uiBufferLen).gcount();
return 0;
}
#endif
HINTERNET hHttpRequest = (HINTERNET) soap->socket;
DWORD dwBytesRead = 0;
size_t uiTotalBytesRead = 0;
BOOL bResult;
soap->error = SOAP_OK;
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: frecv, available buffer len = %lu\n",
soap, a_uiBufferLen ));
/*
NOTE: we do not check here that our connection hasn't been
disconnected because in HTTP/1.0 connections, it will always have been
disconnected by now. This is because the response is checked by the
winhttp_fsend function to ensure that we didn't need any special
authentication. At that time the connection would have been
disconnected. This is ok however as we can still read the response
from the request handle.
*/
do
{
/* read from the connection up to our maximum amount of data */
_ASSERTE( a_uiBufferLen <= ULONG_MAX );
bResult = WinHttpReadData(
hHttpRequest,
&a_pBuffer[uiTotalBytesRead],
(DWORD) a_uiBufferLen - (DWORD) uiTotalBytesRead,
&dwBytesRead );
if ( bResult )
{
uiTotalBytesRead += dwBytesRead;
}
else
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: frecv, error %d (%s) in WinHttpReadData\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
}
}
while ( bResult && dwBytesRead && uiTotalBytesRead < a_uiBufferLen );
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: recv, received %lu bytes\n", soap, uiTotalBytesRead ));
//char * tmp = new char[uiTotalBytesRead + 1];
// tmp[uiTotalBytesRead] = 0;
// memcpy(tmp, a_pBuffer, uiTotalBytesRead);
// fprintf(stdout, "*******************\nreceieved\n**************\n%s\n*************\n\n", tmp);
return uiTotalBytesRead;
}
/* gsoap documentation:
Called by client proxy multiple times, to close a socket connection before
a new socket connection is established and at the end of communications
when the SOAP_IO_KEEPALIVE flag is not set and soap.keep_alive = 0
(indicating that the other party supports keep alive). Should return
SOAP_OK, or a gSOAP error code. Built-in gSOAP function: tcp_disconnect
*/
static int
winhttp_disconnect(
struct soap * soap )
{
struct winhttp_data * pData =
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
soap->error = SOAP_OK;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "winhttp %p: disconnect\n", soap ));
/* force a disconnect by setting the disconnect flag to TRUE */
pData->bDisconnect = TRUE;
winhttp_have_connection( soap, pData );
return SOAP_OK;
}
/* this is mostly for debug tracing */
void CALLBACK
winhttp_callback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength )
{
struct soap * soap = (struct soap *) dwContext;
UNUSED_ARG( hInternet );
UNUSED_ARG( lpvStatusInformation );
UNUSED_ARG( dwStatusInformationLength );
switch ( dwInternetStatus )
{
case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
DBGLOG( TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED\n", soap ));
if( soap )
{
/* the connection has been closed, so we close the handle here */
struct winhttp_data * pData =
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
if ( pData->hConnection )
{
/*
we only mark this for disconnection otherwise we get
errors when reading the data from the handle. In every
function that we use the connection we will check first to
see if it has been disconnected.
*/
pData->bDisconnect = TRUE;
}
}
break;
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_HANDLE_CREATED\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_NAME_RESOLVED\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_READ_COMPLETE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_REDIRECT:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_REDIRECT, new url = %s\n",
soap, (char*) lpvStatusInformation ));
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_REQUEST_ERROR\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_REQUEST_SENT, bytes sent = %lu\n",
soap, *(DWORD*)lpvStatusInformation ));
break;
case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_RESOLVING_NAME\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, bytes received = %lu\n",
soap, *(DWORD *)lpvStatusInformation ));
break;
case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_SECURE_FAILURE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_SENDING_REQUEST\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE\n", soap ));
break;
case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE\n", soap ));
break;
}
}
/*
check to ensure that our connection hasn't been disconnected
and disconnect remaining handles if necessary.
*/
static BOOL
winhttp_have_connection(
struct soap * soap,
struct winhttp_data * a_pData )
{
/* close the http request if we don't have a connection */
BOOL bCloseRequest = a_pData->bDisconnect || !a_pData->hConnection;
if ( bCloseRequest && soap->socket != SOAP_INVALID_SOCKET )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: closing request\n", soap));
WinHttpCloseHandle( (HINTERNET) soap->socket );
soap->socket = SOAP_INVALID_SOCKET;
}
/* close the connection if we don't have a request */
if ( soap->socket == SOAP_INVALID_SOCKET && a_pData->hConnection )
{
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: closing connection\n", soap));
WinHttpCloseHandle( a_pData->hConnection );
a_pData->hConnection = NULL;
}
a_pData->bDisconnect = FALSE;
/* clean up the send details if we don't have a request */
if ( soap->socket == SOAP_INVALID_SOCKET )
{
if ( a_pData->pBuffer )
{
free( a_pData->pBuffer );
a_pData->pBuffer = 0;
}
a_pData->uiBufferLen = 0;
a_pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
}
/* we now either still have both request and connection, or neither */
return (a_pData->hConnection != NULL);
}
static DWORD
winhttp_set_timeout(
struct soap * soap,
struct winhttp_data * a_pData,
const char * a_pszTimeout,
DWORD a_dwOption,
int a_nTimeout )
{
UNUSED_ARG( soap );
UNUSED_ARG( a_pszTimeout );
if ( a_nTimeout > 0 )
{
DWORD dwTimeout = a_nTimeout * MILLISECONDS;
if ( !WinHttpSetOption( a_pData->hInternet,
a_dwOption, &dwTimeout, sizeof(DWORD) ) )
{
DWORD dwErrorCode = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: failed to set %s timeout, error %d (%s)\n",
soap, a_pszTimeout, dwErrorCode,
winhttp_error_message(soap,dwErrorCode) ));
return dwErrorCode;
}
}
return 0;
}
/* Intel(R) documentation:
Called by winhttp_fsend() function. Searches the certificate store
pointed by the hStoreHandle for the client certificate purposed
for an Intel(R) AMT authorization.
If such certificate was found and its Common Name (CN) match
the string pointed by the certName (or certName is NULL), pCertContext
will point to the structure that represents the certificate and this
function will return TRUE, otherwise the function will return FALSE.
*/
static BOOL
winhttp_find_cert(
struct soap * soap,
HANDLE hStoreHandle,
const _TCHAR * certName,
PCCERT_CONTEXT *pCertContext )
{
_TCHAR pszNameString[CERT_MAX_STR_LEN];
LPSTR *oids = new LPSTR[NUM_OF_OIDS];
if (!oids) {
return false;
}
oids[0] = OID_CLIENT;
if(((winhttp_data*)soap->plugins->data)->local == TRUE)
{
oids[1] = OID_LOCAL;
}
else
{
oids[1] = OID_REMOTE;
}
CERT_ENHKEY_USAGE amtOID = {NUM_OF_OIDS, oids};
bool certSuccess = false;
/*
Search for the first client certificate
purposed for an Intel(R) AMT authorization.
*/
*pCertContext = CertFindCertificateInStore(
hStoreHandle,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
CERT_FIND_ENHKEY_USAGE,
&amtOID,
NULL);
/*
If certificate was found - determinate its name. Keep search
while the certificate's Common Name doesn't match the name
defined by the user
*/
while(*pCertContext != NULL)
{
if(!CertGetNameString( *pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
pszNameString,
CERT_MAX_STR_LEN-1))
{
/* obtaining certificate name failed */
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in CertGetNameString\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
break;
}
if (certName == NULL ||
_tcscmp(pszNameString,certName) == 0)
{
certSuccess = true;
break;
}
*pCertContext =
CertFindCertificateInStore(hStoreHandle,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
CERT_FIND_ENHKEY_USAGE,
&amtOID,
*pCertContext);
}//while(pCertContext != NULL && certSuccess == true)
if(*pCertContext == NULL)
{
soap->error = GetLastError();
DBGLOG(TEST, SOAP_MESSAGE(fdebug,
"winhttp %p: fsend, error %d (%s) in CertFindCertificateInStore\n",
soap, soap->error, winhttp_error_message(soap,soap->error) ));
}
// clean up
if(oids)
{
delete [] oids;
}
return certSuccess;
}
void
winhttp_set_local(
struct soap* soap,
BOOL local)
{
((winhttp_data*)soap->plugins->data)->local = local;
}
void
winhttp_set_auth_scheme(
struct soap* soap,
BOOL krb)
{
((winhttp_data*)soap->plugins->data)->krb = krb;
}
bool
winhttp_set_proxy(struct soap* soap)
{
// winhttp proxy info
WINHTTP_PROXY_INFO info;
BOOL result;
info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
wchar_t *proxy_target = NULL;
string uri = string(soap->proxy_host);
uri += ":";
ostringstream buffer;
buffer << soap->proxy_port;
uri += buffer.str();
//Sending The maximum length of a URI in the address bar = 2048 characters
proxy_target = convert_to_unicode(uri.c_str(), 2048);
info.lpszProxy = (LPWSTR)proxy_target;
info.lpszProxyBypass = L"";
do{
result = WinHttpSetOption(
((winhttp_data*)soap->plugins->data)->hInternet,
WINHTTP_OPTION_PROXY,
&info,
sizeof(info));
free(proxy_target);
if(!result)
break;
}while(0);
return (result == TRUE);
}
void
winhttp_set_local_machine_store(
struct soap* soap,
BOOL localMachineStore)
{
((winhttp_data*)soap->plugins->data)->localMachineStore = localMachineStore;
}
/*Intel(R) documentation:
Function intended for an external use. Application that uses
gSOAP with WinHTTP plug-in can perform certificate search
and pass a certificate that was found into this plug-in.
*/
void
winhttp_set_certificate(
struct soap* soap,
PCCERT_CONTEXT certificate)
{
((winhttp_data*)soap->plugins->data)->certificate = certificate;
}
/*Intel(R) documentation:
Function intended for an external use. Application that uses gSOAP
with WinHTTP plug-in sets the certificate's Common Name
to the WinHTTP plug-in structure.
*/
void
winhttp_set_certificate_name(
struct soap* soap,
const _TCHAR* certificateName)
{
((winhttp_data*)soap->plugins->data)->certificateName = certificateName;
}
static void
winhttp_set_headers(
struct soap* soap,
const WCHAR *headers)
{
((winhttp_data*)soap->plugins->data)->headers = headers;
}
const WCHAR *
winhttp_get_headers(
struct soap* soap)
{
return ((winhttp_data*)soap->plugins->data)->headers.c_str();
}
/*Intel(R) documentation:
Function converts the array of the regular one byte size characters
to the array of the wide characters.
*/
static wchar_t *
convert_to_unicode(
const char * str, int strLen)
{
if( !str )
{
return 0;
}
wchar_t *unicode_str = (wchar_t *)malloc((strnlen_s(str, strLen)+1)* sizeof( wchar_t ));
if( unicode_str )
{
size_t charsConverted = 0;
if(mbstowcs_s(&charsConverted, unicode_str, strnlen_s(str, strLen) + 1, str, strnlen_s(str, strLen) + 1) <= 0 )
{
free(unicode_str);
return 0;
}
}
return unicode_str;
}
/*Intel(R) documentation:
Function returns the most strict authentication scheme from those that
the server request. In case of error (unsupported scheme) zero is returned.
*/
DWORD
select_scheme(DWORD suppSchemes)
{
if(suppSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE)
{
return WINHTTP_AUTH_SCHEME_NEGOTIATE;
}
if(suppSchemes & WINHTTP_AUTH_SCHEME_NTLM)
{
return WINHTTP_AUTH_SCHEME_NTLM;
}
if(suppSchemes & WINHTTP_AUTH_SCHEME_PASSPORT)
{
return WINHTTP_AUTH_SCHEME_PASSPORT;
}
if(suppSchemes & WINHTTP_AUTH_SCHEME_DIGEST)
{
return WINHTTP_AUTH_SCHEME_DIGEST;
}
if(suppSchemes & WINHTTP_AUTH_SCHEME_BASIC)
{
return WINHTTP_AUTH_SCHEME_BASIC;
}
return 0;
}
#ifdef SOAP_DEBUG
static const char *
winhttp_error_message(
struct soap * soap,
DWORD a_dwErrorMsgId )
{
HINSTANCE hModule;
DWORD dwResult;
DWORD dwFormatFlags;
struct winhttp_data * pData =
(struct winhttp_data *) soap_lookup_plugin( soap, winhttp_id );
/* free any existing error message */
winhttp_free_error_message( pData );
dwFormatFlags =
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM;
/* load winhttp.dll for the error messages */
hModule = LoadLibraryExA( "winhttp.dll", NULL,
LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES );
if ( hModule )
{
dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
}
/* format the messages */
dwResult = FormatMessageA(
dwFormatFlags,
hModule,
a_dwErrorMsgId,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(LPSTR) &pData->pszErrorMessage,
0,
NULL );
/* free the library if we loaded it */
if ( hModule )
{
FreeLibrary( hModule );
}
/* remove the CR LF from the error message */
if ( dwResult > 2 )
{
pData->pszErrorMessage[dwResult-2] = 0;
return pData->pszErrorMessage;
}
else
{
const static char szUnknown[] = "(unknown)";
return szUnknown;
}
}
static void
winhttp_free_error_message(
struct winhttp_data * a_pData )
{
if ( a_pData->pszErrorMessage )
{
LocalFree( a_pData->pszErrorMessage );
a_pData->pszErrorMessage = 0;
}
}
#endif /* SOAP_DEBUG */