/////////////////////////////////////////////////////////////////////////////// // // Module Name: // HTTPClient.c // // Abstract: Partially Implements the client side of the HTTP 1.1 Protocol as // Defined in RFC 2616,2617 // Platform: Any that supports standard C calls and Berkeley sockets // // Author: Eitan Michaelson // Version: 1.0 // Date 7/1/2006 // Opens: HTTP Proxy authentication support (not fully tested). // HTTP Keep Alive not impliamented // HTTPS not implemented // The HTTP counters are not 100% correct (will be fixed) // /////////////////////////////////////////////////////////////////////////////// #include "HTTPClient.h" #include "HTTPClientAuth.h" // Crypto support (Digest, MD5) #include "HTTPClientString.h" // String utilities #include "wm_mem.h" #include "wm_debug.h" #if TLS_CONFIG_HTTP_CLIENT /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSetLocalConnection // Purpose : TBD // Gets : // Returns : // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSetLocalConnection (HTTP_SESSION_HANDLE pSession, UINT32 nPort) { return 0; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientOpenRequest // Purpose : Allocate memory for a new HTTP Session // Gets : FLAGS // Returns : HTTP Session handle // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// HTTP_SESSION_HANDLE HTTPClientOpenRequest (HTTP_CLIENT_SESSION_FLAGS Flags) { P_HTTP_SESSION pHTTPSession = NULL; // Handle to the session pointer UINT32 nAllocationSize; // Size of the dynamically allocated buffer // Attempt to allocate the buffer pHTTPSession = (P_HTTP_SESSION)tls_mem_alloc(ALIGN(sizeof(HTTP_SESSION))); // Did we succeed? if(!pHTTPSession) { // Null pointer is returned upon error return 0; } // Reset the allocated space to zeros memset(pHTTPSession,0x00,sizeof(HTTP_SESSION)); // Allocate space for the incoming and outgoing headers // Check if the headers buffer is resizable if(HTTP_CLIENT_MEMORY_RESIZABLE) { // Memory is resizable, so use the init defined size or the maximum buffer size (which ever is smaller) nAllocationSize = MIN(HTTP_CLIENT_MAX_SEND_RECV_HEADERS,HTTP_CLIENT_INIT_SEND_RECV_HEADERS); } else { // Memory is not resizable so simply use the maximum defined size nAllocationSize = HTTP_CLIENT_MAX_SEND_RECV_HEADERS; } nAllocationSize = ALIGN(nAllocationSize); // Allocate the headers buffer pHTTPSession->HttpHeaders.HeadersBuffer.pParam = (CHAR*)tls_mem_alloc(nAllocationSize); // Check the returned pointer if(!pHTTPSession->HttpHeaders.HeadersBuffer.pParam) { // tls_mem_alloc() error, free the containing structure and exit. tls_mem_free(pHTTPSession); return 0; } // Reset the headers allocated memory memset(pHTTPSession->HttpHeaders.HeadersBuffer.pParam ,0x00,nAllocationSize); // Set the buffer length pHTTPSession->HttpHeaders.HeadersBuffer.nLength = nAllocationSize; // Set default values in the session structure HTTPClientSetVerb((UINT32)pHTTPSession,(HTTP_VERB)HTTP_CLIENT_DEFAULT_VERB); // Default HTTP verb pHTTPSession->HttpUrl.nPort = HTTP_CLIENT_DEFAULT_PORT; // Default TCP port pHTTPSession->HttpConnection.HttpSocket = HTTP_INVALID_SOCKET; // Invalidate the socket // Set the outgoing headers pointers pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam; // Set our state pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_INIT; // Save the flags pHTTPSession->HttpFlags = Flags; // Reset the status pHTTPSession->HttpHeadersInfo.nHTTPStatus = 0; // Return an allocated session pointer (cast to UINT32 first) return (HTTP_SESSION_HANDLE)pHTTPSession; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientReset // Purpose : Reset the HTTP session and make it ready for another call // Gets : FLAGS // Returns : HTTP Session handle // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientReset (HTTP_SESSION_HANDLE pSession) { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } return HTTPIntrnSessionReset(pHTTPSession,TRUE); } /////////////////////////////////////////////////////////////////////////////// // Function : HTTPClientCloseRequest // Purpose : Closes any active connection and free any used memory // Gets : a Session handle // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientCloseRequest (HTTP_SESSION_HANDLE *pSession) { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)*(pSession); if(!pHTTPSession) { // User passed a bad pointer return HTTP_CLIENT_ERROR_INVALID_HANDLE; } if(pHTTPSession->HttpUrl.Url != NULL) { TLS_DBGPRT_INFO("free pHTTPSession->HttpUrl.Url = %s\n", pHTTPSession->HttpUrl.Url); tls_mem_free(pHTTPSession->HttpUrl.Url); pHTTPSession->HttpUrl.Url = NULL; } // Check for a valid pointer to the HTTP headers if(pHTTPSession->HttpHeaders.HeadersBuffer.pParam) { // Release the used memory tls_mem_free(pHTTPSession->HttpHeaders.HeadersBuffer.pParam); pHTTPSession->HttpHeaders.HeadersBuffer.pParam = NULL; } // Close any active socket connection HTTPIntrnConnectionClose(pHTTPSession); // free the session structure tls_mem_free(pHTTPSession); pHTTPSession = 0; // NULL the pointer *(pSession) = 0; return HTTP_CLIENT_SUCCESS; } #ifdef _HTTP_DEBUGGING_ /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSetDebugHook // Purpose : Sets the HTTP debug function pointer // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSetDebugHook (HTTP_SESSION_HANDLE pSession, E_HTTPDebug *pDebug) // [IN] Function pointer to the the caller debugging function) { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } pHTTPSession->pDebug = pDebug; #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientSetDebugHook",NULL,0,"Debugging hook set, return code is %d",HTTP_CLIENT_SUCCESS); } #endif return HTTP_CLIENT_SUCCESS; } #endif /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSetVerb // Purpose : Sets the HTTP verb for the outgoing request // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSetVerb (HTTP_SESSION_HANDLE pSession, HTTP_VERB HttpVerb) // [IN] Http verb (GET POST HEAD act') { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientSetVerb",NULL,0,"Selected Verb is %d",(INT32)HttpVerb); } #endif // Cache the verb (as integer) for later use pHTTPSession->HttpHeaders.HttpVerb = HttpVerb; // Convert the Verb parameter into its equivalent string representation switch (HttpVerb) { case VerbGet: strcpy(pHTTPSession->HttpHeaders.Verb,"GET"); break; case VerbHead: if(!HTTP_CLIENT_ALLOW_HEAD_VERB) { return HTTP_CLIENT_ERROR_BAD_VERB; } #if HTTP_CLIENT_ALLOW_HEAD_VERB strcpy(pHTTPSession->HttpHeaders.Verb,"HEAD"); break; #endif case VerbPost: strcpy(pHTTPSession->HttpHeaders.Verb,"POST"); break; case VerbPut: strcpy(pHTTPSession->HttpHeaders.Verb,"PUT"); break; default: // Unknown verb return HTTP_CLIENT_ERROR_BAD_VERB; }; return HTTP_CLIENT_SUCCESS; } #if TLS_CONFIG_HTTP_CLIENT_AUTH /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSetVerb // Purpose : Sets the HTTP authentication schema // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSetAuth (HTTP_SESSION_HANDLE pSession, HTTP_AUTH_SCHEMA AuthSchema, // The type of the authentication to perform void *pReserved) { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientSetAuth",NULL,0,"Selected authentication is %d",(INT32)AuthSchema); } #endif switch(AuthSchema) { #if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC case(AuthSchemaBasic): strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"basic"); break; #endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC #if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST case(AuthSchemaDigest): strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"digest"); break; #endif //TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST case(AuthSchemaKerberos): strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"negotiate"); break; }; if(AuthSchema >= AuthNotSupported) { return HTTP_CLIENT_ERROR_BAD_AUTH; } pHTTPSession->HttpCredentials.CredAuthSchema = AuthSchema; return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSetCredentials // Purpose : Sets credentials for the target host // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSetCredentials (HTTP_SESSION_HANDLE pSession, CHAR *pUserName, // [IN] Null terminated string containing the sessions user name CHAR *pPassword) // [IN] Null terminated string containing the sessions password { UINT32 nLength; P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // Get the length of the user name string and see if it's not too long nLength = strlen(pUserName); if(nLength > HTTP_CLIENT_MAX_USERNAME_LENGTH) { return HTTP_CLIENT_ERROR_LONG_INPUT; } // Get the length of the password string and see if it's not too long nLength = strlen(pPassword); if(nLength > HTTP_CLIENT_MAX_PASSWORD_LENGTH) { return HTTP_CLIENT_ERROR_LONG_INPUT; } // Copy them into our internal buffer pHTTPSession->HttpCredentials.CredUser = pUserName; pHTTPSession->HttpCredentials.CredPassword = pPassword; // Set the authentication Boolean flag pHTTPSession->HttpHeadersInfo.HaveCredentials = TRUE; return HTTP_CLIENT_SUCCESS; } #endif //TLS_CONFIG_HTTP_CLIENT_AUTH #if TLS_CONFIG_HTTP_CLIENT_PROXY /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSetProxy // Purpose : Sets all the proxy related parameters // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSetProxy (HTTP_SESSION_HANDLE pSession, CHAR *pProxyHost, // [IN] Null terminated string containing the host name UINT16 nPort, // [IN] The proxy port number CHAR *pUserName, // [IN] User name for proxy authentication (can be null) CHAR *pPassword) // [IN] User password for proxy authentication (can be null) { P_HTTP_SESSION pHTTPSession = NULL; UINT32 nLength; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientSetProxy",NULL,0,"Proxy host %s,Port %d, Username %s,Password %s",pProxyHost,nPort,pUserName,pPassword); } #endif nLength = strlen(pProxyHost); if(nLength > HTTP_CLIENT_MAX_PROXY_HOST_LENGTH) { return HTTP_CLIENT_ERROR_LONG_INPUT; } // Cache the user supplied information in the internal session structure pHTTPSession->HttpProxy.ProxyHost = pProxyHost; if(pUserName) { nLength = strlen(pUserName); if(nLength > HTTP_CLIENT_MAX_USERNAME_LENGTH) { return HTTP_CLIENT_ERROR_LONG_INPUT; } // Proxy user name (for Proxy server authentication) pHTTPSession->HttpProxy.ProxtUser = pUserName; } if(pPassword) { // Get the length of the password string and see if it's not too long nLength = strlen(pPassword); if(nLength > HTTP_CLIENT_MAX_PASSWORD_LENGTH) { return HTTP_CLIENT_ERROR_LONG_INPUT; } // Proxy password (for proxy server authentication) pHTTPSession->HttpProxy.ProxyPassword = pPassword; } // Proxy TCP port pHTTPSession->HttpProxy.nProxyPort = nPort; // Set the Proxy flag in the connection structure pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_USINGPROXY; // Set the proxy auyjentication schema if(pPassword && pUserName) { pHTTPSession->HttpProxy.ProxyAuthSchema = HTTP_CLIENT_DEFAULT_PROXY_AUTH; } return HTTP_CLIENT_SUCCESS; } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientAddRequestHeaders // Purpose : Add headers to the outgoing request // Gets : // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientAddRequestHeaders (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderName, // [IN] The Headers name CHAR *pHeaderData, // [IN] The headers data BOOL nInsert) // [IN] Reserved could be any { UINT32 nRetCode; UINT32 nHeaderLength,nDataLength; P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientAddRequestHeaders",NULL,0,"Adding Header %s: %s",pHeaderName,pHeaderData); } #endif // Get the elements length nHeaderLength = strlen(pHeaderName); nDataLength = strlen(pHeaderData); // Call the internal function to add the headers to our session buffer nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,pHeaderName,nHeaderLength,pHeaderData,nDataLength); return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientSendRequest // Purpose : This function builds the request headers, performs a DNS resolution , // opens the connection (if it was not opened yet by a previous request or if it has closed) // and sends the request headers. // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientSendRequest (HTTP_SESSION_HANDLE pSession, CHAR *pUrl, // [IN] The requested URL VOID *pData, // [IN] Data to post to the server UINT32 nDataLength, // [IN] Length of posted data BOOL TotalLength, // [IN] UINT32 nTimeout, // [IN] Operation timeout UINT32 nClientPort) // [IN] Client side port 0 for none { UINT32 nRetCode; // Function call return code UINT32 nBytes; // Bytes counter (socket operations) UINT32 nUrlLength; // Length of the given Url P_HTTP_SESSION pHTTPSession = NULL; // Session pointer CHAR ContentLength[32]; do { // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientSendRequest",NULL,0,"Url: %s",pUrl); } #endif // Set the operation timeout counters pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); // 0 makes us use the default defined value pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); // Store the cliebt port for later usage pHTTPSession->HttpConnection.HttpClientPort = nClientPort; // Parse the URL nUrlLength = strlen(pUrl); nRetCode = HTTPIntrnSetURL(pHTTPSession,pUrl,nUrlLength); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } // Create the default headers // Add the "Host" header. we should handle a special case of port incorporated within the host name. if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLANDPORT) != HTTP_CLIENT_FLAG_URLANDPORT) { // The case where we don't have the port if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession, "Host",4, pHTTPSession->HttpUrl.UrlHost.pParam, pHTTPSession->HttpUrl.UrlHost.nLength)) != HTTP_CLIENT_SUCCESS) { break; } } else { // We have the port so use a deferent element if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Host",4,pHTTPSession->HttpUrl.UrlHost.pParam, (pHTTPSession->HttpUrl.UrlPort.pParam - pHTTPSession->HttpUrl.UrlHost.pParam) -1 )) != HTTP_CLIENT_SUCCESS) { break; } } // We are in a post request without knowing the total length in advance so return error or use chunking if(pHTTPSession->HttpHeaders.HttpVerb == VerbPost && TotalLength == FALSE) { // If the user specified the chunked flag if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) { // Add the Transfer-Encoding: header if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Transfer-Encoding",17,"chunked",7))!= HTTP_CLIENT_SUCCESS) { break;; } } else if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_MULTIPART_FORM) == HTTP_CLIENT_FLAG_MULTIPART_FORM) { } else { // Not a supported operation - unknown length nRetCode = HTTP_CLIENT_ERROR_HEADER_NO_LENGTH; break; } } // Add the "User-Agent" header if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"User-Agent",10,HTTP_CLIENT_DEFAULT_AGENT,strlen(HTTP_CLIENT_DEFAULT_AGENT)))!= HTTP_CLIENT_SUCCESS) { break; } // Add the "Keep-Alive" header (if requested by the caller) if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_KEEP_ALIVE) == HTTP_CLIENT_FLAG_KEEP_ALIVE) { if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) { // No proxy keep alive: if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Connection",10,"Keep-Alive",10))!= HTTP_CLIENT_SUCCESS) { break; } } #if TLS_CONFIG_HTTP_CLIENT_PROXY else // proxy keep alive { if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Proxy-Connection",15,"Keep-Alive",10))!= HTTP_CLIENT_SUCCESS) { break; } } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY } // If we know the total length in advance (only when posting) if((pHTTPSession->HttpHeaders.HttpVerb == VerbPost || pHTTPSession->HttpHeaders.HttpVerb == VerbPut)&& TotalLength == TRUE) { // set the total content length header pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength = nDataLength; // Store for later usage memset(ContentLength,0,32); IToA(ContentLength,nDataLength); // Convert the buffer length to a string value if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Length",14,ContentLength,strlen(ContentLength)))!= HTTP_CLIENT_SUCCESS) { break; } if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_MULTIPART_FORM) != HTTP_CLIENT_FLAG_MULTIPART_FORM) { if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Type",12,"application/x-www-form-urlencoded",33))!= HTTP_CLIENT_SUCCESS) { break; } } } // Add the "Cache control" header (if requested by the caller) if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_NO_CACHE) == HTTP_CLIENT_FLAG_NO_CACHE) { if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Cache-Control",13,"no-cache",8))!= HTTP_CLIENT_SUCCESS) { break; } } // Now we can connect to the remote server and send the leading request followed by the HTTP headers // Check for timeout if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; break; } // Handle connection close message (reconnect) if(pHTTPSession->HttpHeadersInfo.Connection == FALSE) { // Gracefully close the connection and set the socket as invalid if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) { HTTPIntrnConnectionClose(pHTTPSession); } // Connect to the remote server (or proxy) nRetCode = HTTPIntrnConnectionOpen(pHTTPSession); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } } #if TLS_CONFIG_HTTP_CLIENT_AUTH // Send the request along with the rest of the headers if(pHTTPSession->HttpCredentials.CredAuthSchema != AuthSchemaNone #if TLS_CONFIG_HTTP_CLIENT_PROXY ||pHTTPSession->HttpProxy.ProxyAuthSchema != AuthSchemaNone #endif //TLS_CONFIG_HTTP_CLIENT_PROXY ) // If we have to authenticate we should use the HEAD verb { if(HTTP_CLIENT_ALLOW_HEAD_VERB) // HEAD should not be ussed if not defined { if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,VerbHead)) != HTTP_CLIENT_SUCCESS) { break; } // Set the state flag pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEAD_SENT; } else { // Simply use the verb that was set by the caller without changing to HEAD if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTP_CLIENT_SUCCESS) { break; } // This the case where the caller know the total length to receive in advance // and he wishes to send the data right away if(pHTTPSession->HttpHeaders.HttpVerb == VerbPost && TotalLength == TRUE) { // Send the data nBytes = nDataLength; if((nRetCode = HTTPIntrnSend(pHTTPSession,pData,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the session state pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_POST_SENT; } } // Retrive and analyze the Headers if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTP_CLIENT_SUCCESS) { break; } if(pHTTPSession->HttpHeadersInfo.nHTTPStatus != 401 && pHTTPSession->HttpHeadersInfo.nHTTPStatus != 407) { nRetCode = HTTP_CLIENT_ERROR_AUTH_MISMATCH; break; } // Authenticate if((nRetCode = HTTPIntrnAuthenticate(pHTTPSession)) != HTTP_CLIENT_SUCCESS) { break; } } else #endif //TLS_CONFIG_HTTP_CLIENT_AUTH { // No authentication use the verb that was requested by the caller if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTP_CLIENT_SUCCESS) { break; } } // This the case where the caller know the total length to receive in advance // and he wishes to send the data right away if((pHTTPSession->HttpHeaders.HttpVerb == VerbPost || pHTTPSession->HttpHeaders.HttpVerb == VerbPut) && (TotalLength == TRUE || (pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_MULTIPART_FORM) == HTTP_CLIENT_FLAG_MULTIPART_FORM)) { // Send the data nBytes = nDataLength; if((nRetCode = HTTPIntrnSend(pHTTPSession,pData,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the session state pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_POST_SENT; } }while(0); return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientRecvResponse // Purpose : Receives the response header on the connection and parses it. // Performs any required authentication. // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientRecvResponse (HTTP_SESSION_HANDLE pSession, UINT32 nTimeout) // [IN] Timeout for the operation { UINT32 nRetCode; // Function return code P_HTTP_SESSION pHTTPSession = NULL; // Session pointer // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } do { if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession, nTimeout)) != HTTP_CLIENT_SUCCESS) { break; } } while(0); return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientWriteData // Purpose : Write data to the remote server // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientWriteData (HTTP_SESSION_HANDLE pSession, VOID *pBuffer, UINT32 nBufferLength, UINT32 nTimeout) { UINT32 nRetCode = HTTP_CLIENT_SUCCESS; UINT32 nBytes; CHAR Chunk[HTTP_CLIENT_MAX_CHUNK_HEADER]; P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointer validity pHTTPSession = (P_HTTP_SESSION)pSession; do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Set the operation timeout counters pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); // Did the caller specified chunked sending? if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) { // Prep the chunk Header and send it memset(Chunk,0x00,HTTP_CLIENT_MAX_CHUNK_HEADER); HTTPStrLToH(Chunk,nBufferLength); strcat(Chunk,HTTP_CLIENT_CRLF); // Send the leading CrLf (only after the first chunk) if(pHTTPSession->HttpCounters.nSentChunks >= 1) { nBytes = 2;; nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF,&nBytes); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } } // Send the chunk header nBytes = strlen(Chunk); nRetCode = HTTPIntrnSend(pHTTPSession,Chunk,&nBytes); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } } // Send the data nBytes = nBufferLength; nRetCode = HTTPIntrnSend(pHTTPSession,pBuffer,&nBytes); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } // If we are using chunks then.. if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) { // Set the chunks count pHTTPSession->HttpCounters.nSentChunks++; // If it was the last chunk (0) we should re-get the headers from the server reply if(nBufferLength == 0) { // Send the trailing CrLf nBytes = 2;; nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF,&nBytes); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } // Get the remote headers (since the last chunk was transmitted we can expect the server to start the reply) if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTP_CLIENT_SUCCESS) { break; } } } } while(0); return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientReadData // Purpose : Read data from the server. Parse out the chunks data. // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientReadData (HTTP_SESSION_HANDLE pSession, VOID *pBuffer, // [IN OUT] a pointer to a buffer that will be filled with the servers response UINT32 nBytesToRead, // [IN] The size of the buffer (numbers of bytes to read) UINT32 nTimeout, // [IN] operation timeout in seconds UINT32 *nBytesRecived) // [OUT] Count of the bytes that ware received in this operation { UINT32 nBytes = 0; UINT32 nRetCode = 0 ; INT32 nProjectedBytes = 0; // Should support negative numbers CHAR *pNullPtr; BOOL EndOfStream = FALSE; P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // If the last verb that was used was HEAD there is no point to get this data (chanses are that we will endup with timeout) if(pHTTPSession->HttpHeaders.HttpVerb == VerbHead) { return HTTP_CLIENT_EOS; } // Set the operation timeout counters pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); nBytes = nBytesToRead - 1; // We will spare 1 byte for the trailing null termination *((CHAR*)pBuffer) = 0; // Null terminate the user supplied buffer *(nBytesRecived) = 0; // Set the return bytes count to 0 // We can read the data only if we got valid headers (and not authentication requests for example) if((pHTTPSession->HttpState & HTTP_CLIENT_STATE_HEADERS_PARSED) != HTTP_CLIENT_STATE_HEADERS_PARSED) { return HTTP_CLIENT_ERROR_BAD_STATE; } // Is it a chunked mode transfer? if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_CHUNKED) == HTTP_CLIENT_FLAG_CHUNKED) { // How many bytes left until the next chunk? if(pHTTPSession->HttpCounters.nBytesToNextChunk == 0) { // Read the chunk header and get its length if(HTTPIntrnGetRemoteChunkLength(pHTTPSession) != HTTP_CLIENT_SUCCESS) { // Could not parse the chunk parameter return HTTP_CLIENT_ERROR_CHUNK; } // 0 Bytes chunk, we should return end of stream if(pHTTPSession->HttpCounters.nRecivedChunkLength == 0) { return HTTP_CLIENT_EOS; } } // If we are about to read pass the next chunk, reduce the read bytes so we will read // non HTML data nProjectedBytes = pHTTPSession->HttpCounters.nBytesToNextChunk - nBytes; if ( nProjectedBytes <= 0) { // Set the correct bytes count we should read nBytes = pHTTPSession->HttpCounters.nBytesToNextChunk; } } // Do we have the content length? if(pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0) { // Length of the projected buffer nProjectedBytes = pHTTPSession->HttpCounters.nRecivedBodyLength + nBytes; // If we are going to read more then the known content length then.. if(nProjectedBytes >= (INT32)pHTTPSession->HttpHeadersInfo.nHTTPContentLength) { // Reduce the received bytes count to the correct size nBytes = pHTTPSession->HttpHeadersInfo.nHTTPContentLength - pHTTPSession->HttpCounters.nRecivedBodyLength; } } // Receive the data from the socket nRetCode = HTTPIntrnRecv(pHTTPSession,(CHAR*)pBuffer,&nBytes,FALSE); // Set the return bytes count *(nBytesRecived) = nBytes; // + 1; Fixed 11/9/2005 // Pointer to the end of the buffer pNullPtr = (CHAR*)pBuffer + nBytes ; // And null terminate *pNullPtr = 0; // Socket read went OK if(nRetCode == HTTP_CLIENT_SUCCESS) { #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPClientReadData",NULL,0,"Reading %d bytes",nBytes); } #endif // Set the HTTP counters pHTTPSession->HttpCounters.nRecivedBodyLength += nBytes; // If we know the total content length and.. if(pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0) { // If total received body is equal or greater then the known content length then.. if( pHTTPSession->HttpCounters.nRecivedBodyLength >= pHTTPSession->HttpHeadersInfo.nHTTPContentLength) { // Raise a flag to signal end of stream EndOfStream = TRUE; } } // Is it a chunked mode transfer? if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_CHUNKED) == HTTP_CLIENT_FLAG_CHUNKED) { // We are a little closer to the next chunk now pHTTPSession->HttpCounters.nBytesToNextChunk -= nBytes; } // Is it End of stream? if(EndOfStream == TRUE) { // So exit return HTTP_CLIENT_EOS; } } return nRetCode ; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientGetInfo // Purpose : Fill the users structure with the session information // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientGetInfo (HTTP_SESSION_HANDLE pSession, HTTP_CLIENT *HTTPClient) { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // Reset the users info structure memset(HTTPClient,0x00,sizeof(HTTP_CLIENT)); HTTPClient->HTTPStatusCode = pHTTPSession->HttpHeadersInfo.nHTTPStatus; HTTPClient->RequestBodyLengthSent = pHTTPSession->HttpCounters.nSentBodyBytes; HTTPClient->ResponseBodyLengthReceived = pHTTPSession->HttpCounters.nRecivedBodyLength; HTTPClient->TotalResponseBodyLength = pHTTPSession->HttpHeadersInfo.nHTTPContentLength; HTTPClient->HttpState = pHTTPSession->HttpState; return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientFindFirstHeader // Purpose : Initiate the headr searching functions // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientFindFirstHeader (HTTP_SESSION_HANDLE pSession, CHAR *pSearchClue,CHAR *pHeaderBuffer, UINT32 *nLength) { P_HTTP_SESSION pHTTPSession = NULL; UINT32 nClueLength; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } nClueLength = strlen(pSearchClue); // See if we are not to long if(nClueLength >= HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE) { return HTTP_CLIENT_ERROR_HEADER_BIG_CLUE; } else { pHTTPSession->HttpHeaders.SearchClue = pSearchClue; pHTTPSession->HttpHeaders.HeaderSearch.nLength = 0; pHTTPSession->HttpHeaders.HeaderSearch.pParam = NULL; } return HTTPClientGetNextHeader(pSession, pHeaderBuffer, nLength); } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientFindCloseHeader // Purpose : Terminate a headers search session // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientFindCloseHeader (HTTP_SESSION_HANDLE pSession) { P_HTTP_SESSION pHTTPSession = NULL; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } pHTTPSession->HttpHeaders.HeaderSearch.nLength = 0; pHTTPSession->HttpHeaders.HeaderSearch.pParam = NULL; return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPClientGetNextHeader // Purpose : Terminate a headers search session // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPClientGetNextHeader (HTTP_SESSION_HANDLE pSession, CHAR *pHeaderBuffer, UINT32 *nLength) { P_HTTP_SESSION pHTTPSession = NULL; UINT32 nOffset = 0; UINT32 nRetCode; HTTP_PARAM HttpHeader; CHAR *pPtr; // Cast the handle to our internal structure and check the pointers validity pHTTPSession = (P_HTTP_SESSION)pSession; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } if(pHTTPSession->HttpHeaders.HeaderSearch.nLength > 0) // We must adjust the search offset since it is not the fierst iteration { nOffset = pHTTPSession->HttpHeaders.HeaderSearch.pParam - pHTTPSession->HttpHeaders.HeadersIn.pParam; } // Search for the next header nRetCode = HTTPIntrnHeadersFind(pHTTPSession,pHTTPSession->HttpHeaders.SearchClue,&HttpHeader,TRUE,nOffset); if(nRetCode == HTTP_CLIENT_SUCCESS) { if(HttpHeader.nLength > *(nLength)) // Check for sufficiant length { *(nLength) = HttpHeader.nLength; pHeaderBuffer[0] = 0; // Reset the users buffer return HTTP_CLIENT_ERROR_NO_MEMORY; } pPtr = HttpHeader.pParam; nOffset = 0; if(*pPtr == 0x0d) { nOffset++; pPtr++; } if(*pPtr == 0x0a) { nOffset++; pPtr++; } strncpy(pHeaderBuffer,pPtr,HttpHeader.nLength - nOffset); pHeaderBuffer[HttpHeader.nLength - nOffset] = 0; *(nLength) = HttpHeader.nLength - nOffset; pHTTPSession->HttpHeaders.HeaderSearch.pParam = HttpHeader.pParam + HttpHeader.nLength; pHTTPSession->HttpHeaders.HeaderSearch.nLength++; return HTTP_CLIENT_SUCCESS; } return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnSetURL // Purpose : Parse the user's URL // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnSetURL (P_HTTP_SESSION pHTTPSession, CHAR *pUrl, // [IN] a null terminated string containing the Url we should retrieve UINT32 nUrlLength) // [IN] The length the Url string { UINT32 nUrlOffset; // Offset in bytes within the Url string HTTP_URL *pUrlPtr; // a Pointer to the Url structure (within the global session structure) CHAR *pPtr; // a Pointer for the Url port (Used in the parsing process) CHAR UrlPort[16]; // a temporary byte array for the Url port conversion operation (string to number) // Check for the session pointer validity if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // Get the length of the Url nUrlLength = strlen(pUrl); // Check if it is not longer than the permitted length if((nUrlLength + 1) > HTTP_CLIENT_MAX_URL_LENGTH) { return HTTP_CLIENT_ERROR_LONG_INPUT; } if(pHTTPSession->HttpUrl.Url != NULL) { TLS_DBGPRT_INFO("free pHTTPSession->HttpUrl.Url = %x\n", pHTTPSession->HttpUrl.Url); tls_mem_free(pHTTPSession->HttpUrl.Url); pHTTPSession->HttpUrl.Url = NULL; } pHTTPSession->HttpUrl.Url = tls_mem_alloc(nUrlLength+2); if(pHTTPSession->HttpUrl.Url == NULL) return HTTP_CLIENT_ERROR_NO_MEMORY; memset(pHTTPSession->HttpUrl.Url, 0, nUrlLength+2); //TLS_DBGPRT_INFO("HttpUrl.Url=%x, size=%d\n", pHTTPSession->HttpUrl.Url, nUrlLength+2); // Point the local pointer on the global structure pUrlPtr = &pHTTPSession->HttpUrl; // Copy to the internal buffer MEMCPY(pUrlPtr->Url, pUrl, nUrlLength); nUrlOffset = 0; // Get the Url base ("http" or "https") if(HTTPStrSearch(pUrlPtr->Url,":",nUrlOffset,nUrlLength,&pUrlPtr->UrlBsee) == FALSE) { return HTTP_CLIENT_ERROR_BAD_URL; } // Increase the offset parameter nUrlOffset += pUrlPtr->UrlBsee.nLength; #if TLS_CONFIG_HTTP_CLIENT_SECURE // If we can parse the string "HTTPS" we can assume a secured session if(HTTPStrInsensitiveCompare(pUrlPtr->UrlBsee.pParam,"https",pUrlPtr->UrlBsee.nLength) == TRUE) { // Set the secured flags on the session pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_URLHTTPS; pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_SECURE; // ToDo: Init TLS (GetProtocol) #ifdef _HTTP_BUILD_AMT // OS_GET_CLIENT_SUBSET_PROTOCOL(TRUE,&pHTTPSession->pSecProtocol); #endif #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnSetURL",NULL,0,"SSL Protected Url %s",pUrl); } #endif } else // it should be "http" #endif //TLS_CONFIG_HTTP_CLIENT_SECURE { if(HTTPStrInsensitiveCompare(pUrlPtr->UrlBsee.pParam,"http",pUrlPtr->UrlBsee.nLength) == FALSE) { return HTTP_CLIENT_ERROR_BAD_URL; // cOULD NOT DETECT http or https prefix } } // Look for standard Url elements if(HTTPStrSearch(pUrlPtr->Url,"://",nUrlOffset,3,0) == FALSE) { return HTTP_CLIENT_ERROR_BAD_URL; // Could not detect "://" } // Increase the offset parameter nUrlOffset += 3; // Get the host name if(HTTPStrSearch(pUrlPtr->Url,"/",nUrlOffset,(nUrlLength - nUrlOffset),&pUrlPtr->UrlHost) == FALSE) { pUrlPtr->Url[nUrlLength] = '/'; nUrlLength++; if(HTTPStrSearch(pUrlPtr->Url,"/",nUrlOffset,(nUrlLength - nUrlOffset),&pUrlPtr->UrlHost) == FALSE) { return HTTP_CLIENT_ERROR_BAD_URL; } } nUrlOffset += pUrlPtr->UrlHost.nLength; // Do we have the port within the hostname? if(HTTPStrSearch(pUrlPtr->Url,":", (nUrlOffset - pUrlPtr->UrlHost.nLength), pUrlPtr->UrlHost.nLength, &pUrlPtr->UrlPort) == TRUE) { if((pUrlPtr->UrlHost.nLength - pUrlPtr->UrlPort.nLength) < 10) { // To-Do: check the actual port length before the memcpy pUrlPtr->UrlPort.pParam += pUrlPtr->UrlPort.nLength + 1; MEMCPY(UrlPort,pUrlPtr->UrlPort.pParam,15); pUrlPtr->UrlPort.nLength = 0; pPtr = UrlPort; while(*pPtr && pPtr++) { pUrlPtr->UrlPort.nLength++; if(*pPtr == '/') { *pPtr = 0; pUrlPtr->nPort = (UINT16)atol(UrlPort); pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_URLANDPORT; break; } } } else { // Port too big return HTTP_CLIENT_ERROR_BAD_URL; } } // Get the request body pUrlPtr->UrlRequest.pParam = pUrlPtr->Url + nUrlOffset; pUrlPtr->UrlRequest.nLength = nUrlLength - nUrlOffset; #if TLS_CONFIG_HTTP_CLIENT_SECURE // If we got SSL url with no port we should set the default ssl port if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLHTTPS) == HTTP_CLIENT_FLAG_URLHTTPS) { if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLANDPORT) != HTTP_CLIENT_FLAG_URLANDPORT) { pHTTPSession->HttpUrl.nPort = HTTP_CLIENT_DEFAULT_SSL_PORT; } } #endif //TLS_CONFIG_HTTP_CLIENT_SECURE // Set the state flag pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_URL_PARSED; return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnResizeBuffer // Purpose : Resize the HTTP headers buffer and reset the pointers // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnResizeBuffer (P_HTTP_SESSION pHTTPSession, UINT32 nNewBufferSize) // [IN] The new (and larger) buffer size { CHAR *pPtr = NULL; UINT32 nCurrentBufferSize; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // If the new buffer size is less or equal to the current buffer size then.. if(pHTTPSession->HttpHeaders.HeadersBuffer.nLength >= nNewBufferSize) { // Return an error (bad buffer) return HTTP_CLIENT_ERROR_BUFFER_RSIZE; } // If the new buffer size is bigger then the defined maximum buffer size then.. if(nNewBufferSize > HTTP_CLIENT_MAX_SEND_RECV_HEADERS) { // Return an error (bad buffer) return HTTP_CLIENT_ERROR_BUFFER_RSIZE; } // Current buffer size is the sum of the incoming and outgoing headers strings lengths nCurrentBufferSize = pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength; // Allocate a new buffer with the requested buffer size nNewBufferSize=ALIGN(nNewBufferSize); //TLS_DBGPRT_INFO("nNewBufferSize=%d\n", nNewBufferSize); pPtr = (CHAR*)tls_mem_alloc(nNewBufferSize); if(!pPtr) { // malloc() error return HTTP_CLIENT_ERROR_NO_MEMORY; } // Copy the memory only if there is data to copy if(nCurrentBufferSize > 0) { MEMCPY(pPtr,pHTTPSession->HttpHeaders.HeadersBuffer.pParam,nCurrentBufferSize); // Reset the rest of the buffer memset(pPtr + nCurrentBufferSize, 0x00,(nNewBufferSize - nCurrentBufferSize)); } else { // Reset the entire buffer (no previous buffer was copied) memset(pPtr,0x00,nNewBufferSize); } tls_mem_free(pHTTPSession->HttpHeaders.HeadersBuffer.pParam); pHTTPSession->HttpHeaders.HeadersBuffer.pParam = pPtr; pHTTPSession->HttpHeaders.HeadersBuffer.nLength = nNewBufferSize; // Refresh the pointers pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam; if(pHTTPSession->HttpHeaders.HeadersIn.pParam) { pHTTPSession->HttpHeaders.HeadersIn.pParam =pHTTPSession->HttpHeaders.HeadersBuffer.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength; } return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnRemoveHeader // Purpose : Removes an HTTP headers by its name // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnHeadersRemove (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName) // [IN] The header's name { HTTP_PARAM HttpParam; UINT32 nRetCode = HTTP_CLIENT_SUCCESS; UINT32 nBytes; if(!pHTTPSession) // Pointer validation check { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // First see if we have that header in our outgoing headers buffer do { if((nRetCode = HTTPIntrnHeadersFind(pHTTPSession,pHeaderName,&HttpParam,FALSE,0)) != HTTP_CLIENT_SUCCESS) { // Could not find this header break; } // Calculate the new headers length nBytes = (HttpParam.pParam - pHTTPSession->HttpHeaders.HeadersOut.pParam); nBytes -= HttpParam.nLength; // Copy the memory MEMCPY(HttpParam.pParam, HttpParam.pParam + HttpParam.nLength,nBytes); // Set the new length pHTTPSession->HttpHeaders.HeadersOut.nLength -= HttpParam.nLength; // Reset the buffer from it's modified end to it's previous end memset(pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength,0x00,HttpParam.nLength); } while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnHeadersRemove",NULL,0,"Removing Header %",pHeaderName); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnHeadersAdd // Purpose : Add HTTP headers to the outgoing headers buffers // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnHeadersAdd (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName, // [IN] The header's name UINT32 nNameLength, // [IN] Name length CHAR *pHeaderData, // [IN] The Header's data UINT32 nDataLength) // [IN] Data length { CHAR *pPtr; UINT32 nProjectedHeaderLength; UINT32 nProjectedBufferLength; INT32 nCurrentfreeSpace; INT32 nProjectedfreeSpace; UINT32 nRetCode; if(!pHTTPSession) // pointer validation check { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } nProjectedHeaderLength = nNameLength + nDataLength + 4; nProjectedBufferLength = nProjectedHeaderLength + pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength; nCurrentfreeSpace = pHTTPSession->HttpHeaders.HeadersBuffer.nLength - (pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength); nProjectedfreeSpace = nCurrentfreeSpace - nProjectedHeaderLength; // Check total size limit if(nProjectedBufferLength > HTTP_CLIENT_MAX_SEND_RECV_HEADERS) { return HTTP_CLIENT_ERROR_NO_MEMORY; } if((INT32)nProjectedfreeSpace < 0) { if(HTTP_CLIENT_MEMORY_RESIZABLE == FALSE) { // Need more space but we can't grow beyond the current size return HTTP_CLIENT_ERROR_NO_MEMORY; } else { // We can resizes so.. nRetCode = HTTPIntrnResizeBuffer(pHTTPSession,nProjectedBufferLength + HTTP_CLIENT_MEMORY_RESIZE_FACTOR); if(nRetCode != HTTP_CLIENT_SUCCESS) { return nRetCode; } } } // Move the incoming headers data within the buffer so we will have space for the added headers if(pHTTPSession->HttpHeaders.HeadersIn.pParam) { // Move the data and reset the data in the offset. MEMCPY(pHTTPSession->HttpHeaders.HeadersIn.pParam + nProjectedHeaderLength , pHTTPSession->HttpHeaders.HeadersIn.pParam, pHTTPSession->HttpHeaders.HeadersIn.nLength); // Reset the space created memset(pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength, 0x00, nProjectedHeaderLength); } pPtr = pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength; // Create the new header MEMCPY(pPtr,pHeaderName,nNameLength); pPtr += nNameLength; MEMCPY(pPtr,": ",2); pPtr += 2; MEMCPY(pPtr,pHeaderData,nDataLength); pPtr += nDataLength; MEMCPY(pPtr,HTTP_CLIENT_CRLF,2); pPtr += 2; // Set the new length pHTTPSession->HttpHeaders.HeadersOut.nLength += nProjectedHeaderLength; #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnHeadersAdd",NULL,0,"Adding Header %s: %s",pHeaderName,pHeaderData); } #endif return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnConnectionClose // Purpose : Closes an active socket connection and invalidate the socket handle // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// UINT32 HTTPIntrnConnectionClose (P_HTTP_SESSION pHTTPSession) { INT32 nRetCode = HTTP_CLIENT_SUCCESS; do { if(!pHTTPSession) // Validate the session pointer { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // If we have a valid socket then.. if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET)// INVALID_SOCKET { #if TLS_CONFIG_HTTP_CLIENT_SECURE if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) { // TLS Close nRetCode = HTTPWrapperSSLClose(pHTTPSession->ssl, pHTTPSession->HttpConnection.HttpSocket); } #endif //TLS_CONFIG_HTTP_CLIENT_SECURE // Gracefully close it shutdown(pHTTPSession->HttpConnection.HttpSocket,0x02); closesocket(pHTTPSession->HttpConnection.HttpSocket); // And invalidate the socket pHTTPSession->HttpConnection.HttpSocket = HTTP_INVALID_SOCKET; break;; } else { // Not a valid socket error nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; break; } } while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnConnectionClose",NULL,0,""); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnConnectionOpen // Purpose : Opens a socket connection to the remote host or proxy server // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnConnectionOpen (P_HTTP_SESSION pHTTPSession) { INT32 nRetCode = HTTP_CLIENT_SUCCESS; // a function return code value UINT32 nNullOffset; // a helper value to null terminate a given string //int nNonBlocking = 1; // non blocking mode parameter CHAR Backup; // a container for a char value (helps in temporary null termination) // HTTP_HOSTNET *HostEntry; // Socket host entry pointer ULONG Address = 0; HTTP_SOCKADDR_IN ServerAddress; // Socket address structure HTTP_SOCKADDR_IN LoaclAddress; // Socket address structure (for client binding) do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Use an existing connection if valid if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) { // Set the state flag pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HOST_CONNECTED; return HTTP_CLIENT_SUCCESS; } // Zero the socket events FD_ZERO(&pHTTPSession->HttpConnection.FDRead); FD_ZERO(&pHTTPSession->HttpConnection.FDWrite); FD_ZERO(&pHTTPSession->HttpConnection.FDError); if(pHTTPSession->HttpConnection.HttpSocket == HTTP_INVALID_SOCKET) { // Create a TCP/IP stream socket pHTTPSession->HttpConnection.HttpSocket = socket(AF_INET, // Address family SOCK_STREAM, // Socket type IPPROTO_TCP); // Protocol TLS_DBGPRT_INFO("pHTTPSession->HttpConnection.HttpSocket=%d\n", pHTTPSession->HttpConnection.HttpSocket); } // Exit if we don't have a valid socket if(pHTTPSession->HttpConnection.HttpSocket == HTTP_INVALID_SOCKET) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; break; } // Set non blocking socket /* nRetCode = ioctlsocket(pHTTPSession->HttpConnection.HttpSocket, FIONBIO, &nNonBlocking); if(nRetCode != 0) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_CANT_SET; break; } */ // Resolve the target host name if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) { // No proxy, directly resolving the host name // Prep the parameter if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_URLANDPORT) == HTTP_CLIENT_FLAG_URLANDPORT) { nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength - pHTTPSession->HttpUrl.UrlPort.nLength - 1; } else { nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength; } Backup = HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,0); // Resolve the host name nRetCode = HostByName(pHTTPSession->HttpUrl.UrlHost.pParam,&Address); // Restore from backup (fix the buffer) HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,Backup); } #if TLS_CONFIG_HTTP_CLIENT_PROXY else { // Using a Proxy server so resolve the proxy host name nRetCode = HostByName(pHTTPSession->HttpProxy.ProxyHost,&Address); } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY // See if we have a valid response from the net resolve operation /* if(nRetCode) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_RESOLVE; break; } */ // Reset the address structures memset(&ServerAddress, 0, sizeof(HTTP_SOCKADDR_IN)); memset(&LoaclAddress, 0, sizeof(HTTP_SOCKADDR_IN)); // Fill in the address structure ServerAddress.sin_family = AF_INET; #ifdef _HTTP_BUILD_AMT ServerAddress.sin_len = sizeof(HTTP_SOCKADDR_IN); ServerAddress.sin_addr.s_addr = htonl(Address); // Server's address #endif ServerAddress.sin_addr.s_addr = Address; // Server's address if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) { // Use the remote web server port ServerAddress.sin_port = htons(pHTTPSession->HttpUrl.nPort); // Host Port number } #if TLS_CONFIG_HTTP_CLIENT_PROXY else { // Use the proxy port ServerAddress.sin_port = htons(pHTTPSession->HttpProxy.nProxyPort); // Proxy Port number } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY // Client-side Binding if(pHTTPSession->HttpConnection.HttpClientPort != 0) { LoaclAddress.sin_family = AF_INET; #ifdef _HTTP_BUILD_AMT LoaclAddress.sin_len = sizeof(HTTP_SOCKADDR_IN); #endif LoaclAddress.sin_port = htons((unsigned short)pHTTPSession->HttpConnection.HttpClientPort); nRetCode = bind(pHTTPSession->HttpConnection.HttpSocket, (HTTP_SOCKADDR*)&LoaclAddress, sizeof(HTTP_SOCKADDR_IN)); if(nRetCode != 0) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_BIND; } } #if TLS_CONFIG_HTTP_CLIENT_SECURE // Connect using TLS or otherwise clear connection if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) { // Is it a TLS connection? nRetCode = HTTPWrapperSSLConnect(&pHTTPSession->ssl,pHTTPSession->HttpConnection.HttpSocket, // Socket (HTTP_SOCKADDR*)&ServerAddress, // Server address sizeof(HTTP_SOCKADDR), // Length of server address structure "desktop"); // Hostname (ToDo: Fix this) } else // Non TLS so.. #endif //TLS_CONFIG_HTTP_CLIENT_SECURE { nRetCode = connect(pHTTPSession->HttpConnection.HttpSocket, // Socket (HTTP_SOCKADDR*)&ServerAddress, // Server address sizeof(HTTP_SOCKADDR)); // Length of server address structure } // The socket was set to be asyn so we should check the error being returned from connect() nRetCode = SocketGetErr(pHTTPSession->HttpConnection.HttpSocket); if(nRetCode == 0 || nRetCode == HTTP_EWOULDBLOCK || nRetCode == HTTP_EINPROGRESS) { // Set TLS Nego flag to flase pHTTPSession->HttpConnection.TlsNego = FALSE; // Set the Write fd_sets for a socket connection event FD_SET(pHTTPSession->HttpConnection.HttpSocket, &pHTTPSession->HttpConnection.FDWrite); // Set the state flag pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HOST_CONNECTED; // We have connected so set the return value to success nRetCode = HTTP_CLIENT_SUCCESS; break; } else { // Socket connection problem nRetCode = HTTP_CLIENT_ERROR_SOCKET_CONNECT; break; } }while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnConnectionOpen",NULL,0,""); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnSend // Purpose : Send data to the remote server (Asynchronous sockets) // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnSend (P_HTTP_SESSION pHTTPSession, CHAR *pData, // [IN] a pointer to the data to be sent UINT32 *nLength) // [IN OUT] Length of data to send and the transmitted bytes count { INT32 nSocketEvents; // Socket events center INT32 nRetCode = HTTP_CLIENT_SUCCESS; // a function return code value HTTP_TIMEVAL Timeval = { 1 , 0 }; // Timeout value for the socket() method HTTP_CONNECTION *pConnection = NULL; // Pointer for the connection structure do { // Validate the session pointer if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Have a pointer on the internal connection structure for simplifying code reading pConnection = &pHTTPSession->HttpConnection; while(1) { // Check for timeout if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; break; } if(pConnection->HttpSocket == HTTP_INVALID_SOCKET) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; break; } // Reset socket events , only Error, since we don't want to get // a repeated Write events (socket is connected) FD_SET(pConnection->HttpSocket, &pConnection->FDError); // See if we got any events on the socket nSocketEvents = select((pConnection->HttpSocket + 1), 0, &pConnection->FDWrite, &pConnection->FDError, &Timeval); if(nSocketEvents < 0) // No events on the socket { *(nLength) = 0; break; // To-Do: This might be an error } if(nSocketEvents == 0) // No new events so { continue; // restart this loop } // Socket is writable (we are connected) so send the data if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDWrite)) { #if TLS_CONFIG_HTTP_CLIENT_SECURE // Send the data if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) { // TLS Protected connection if(pConnection->TlsNego == FALSE) { nRetCode = HTTPWrapperSSLNegotiate((HTTP_SESSION_HANDLE)pHTTPSession,pConnection->HttpSocket,0,0,"desktop"); if(nRetCode != 0) { // TLS Error nRetCode = HTTP_CLIENT_ERROR_TLS_NEGO; break; } pConnection->TlsNego = TRUE; } nRetCode = HTTPWrapperSSLSend(pHTTPSession->ssl,pConnection->HttpSocket,pData,*(nLength),0); } else #endif //TLS_CONFIG_HTTP_CLIENT_SECURE { nRetCode = send(pConnection->HttpSocket,pData,*(nLength),0); } if(nRetCode == SOCKET_ERROR) { nRetCode = SocketGetErr(pHTTPSession->HttpConnection.HttpSocket); nRetCode = HTTP_CLIENT_ERROR_SOCKET_SEND; break; } //TLS_DBGPRT_INFO("%s", pData); // The data was sent to the remote server *(nLength) = nRetCode; nRetCode = HTTP_CLIENT_SUCCESS; break; } // We had a socket related error if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDError)) { FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDError); *(nLength) = 0; // To-Do: Handle this case nRetCode = HTTP_CLIENT_ERROR_SOCKET_SEND; break; } } } while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnSend",pData,*(nLength),""); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnRecv // Purpose : Receive data from the connected socket using asynchronous sockets // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnRecv (P_HTTP_SESSION pHTTPSession, CHAR *pData, // [IN] a pointer for a buffer that receives the data UINT32 *nLength, // [IN OUT] Length of the buffer and the count of the received bytes BOOL PeekOnly) // [IN] State if we should only peek the socket (default is no) { INT32 nSocketEvents; #if TLS_CONFIG_HTTP_CLIENT_SECURE INT32 nRetCode = pHTTPSession->ssl_more_data; #else INT32 nRetCode = 0; #endif HTTP_TIMEVAL Timeval = { 0, 50000 }; HTTP_CONNECTION *pConnection = NULL; do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Set a pointer on the session internal connection structure (simplify code reading) pConnection = &pHTTPSession->HttpConnection; while(1) { // Check for timeout if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_TIME_OUT; break; } if(pConnection->HttpSocket == HTTP_INVALID_SOCKET) { nRetCode = HTTP_CLIENT_ERROR_SOCKET_INVALID; break; } if(nRetCode != SOCKET_SSL_MORE_DATA){ // Reset socket events FD_SET(pConnection->HttpSocket, &pConnection->FDRead); FD_SET(pConnection->HttpSocket, &pConnection->FDError); // See if we got any events on the socket nSocketEvents = select(pConnection->HttpSocket + 1, &pConnection->FDRead, 0, &pConnection->FDError, &Timeval); } else { nSocketEvents = 1; } if(nSocketEvents < 0) // Error or no new socket events { *(nLength) = 0; break; } if(nSocketEvents == 0) { ////////////////////////////////////////////////////////////////////////////////////////////////////////// // This is a simple bypass for the TSL session (for some reason the socket read event is not set so // The pending bytes on the socket are being checked manualy. // TLS hack: /* if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) { nRetCode = HTTPWrapperSSLRecvPending(pConnection->HttpSocket); if(nRetCode > 0) { // Recive without being notified by the socket event if((nRetCode = HTTPWrapperSSLRecv(pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR) { // Socket error nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; break; } *(nLength) = nRetCode; // Break on no data or server connection reset if ( nRetCode == 0 || nRetCode == HTTP_ECONNRESET) { // Connection closed, simply break - this is not an error nRetCode = HTTP_CLIENT_EOS; // Signal end of stream break; } // We have successfully got the data from the server nRetCode = HTTP_CLIENT_SUCCESS; break; } } */ // End Of the TLS bypass section // //////////////////////////////////////////////////////////////////////////////////////////////// continue; // select() timed out - restart this loop } if(nRetCode == SOCKET_SSL_MORE_DATA || FD_ISSET(pConnection->HttpSocket ,&pConnection->FDRead)) // Are there any read events on the socket ? { if(nRetCode != SOCKET_SSL_MORE_DATA){ // Clear the event FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDRead); } // Socket is readable so so read the data if(PeekOnly == FALSE) { #if TLS_CONFIG_HTTP_CLIENT_SECURE // Get the data (secuure) if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SECURE) == HTTP_CLIENT_FLAG_SECURE) { if((nRetCode = HTTPWrapperSSLRecv(pHTTPSession->ssl,pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR) { // Socket error nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; break; } pHTTPSession->ssl_more_data = (nRetCode == SOCKET_SSL_MORE_DATA ? SOCKET_SSL_MORE_DATA : 0); } else // Get the data (non secuure) #endif //TLS_CONFIG_HTTP_CLIENT_SECURE { if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR) { // Socket error nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; break; } } } else { // Only peek te socket if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),MSG_PEEK)) == SOCKET_ERROR) { // Socket error nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; break; } } if(nRetCode != SOCKET_SSL_MORE_DATA) *(nLength) = nRetCode; // Break on no data or server connection reset // MSDN: If the connection has been gracefully closed, the return value is zero. if ( nRetCode == 0 || HTTPWrapperGetSocketError(pConnection->HttpSocket)== HTTP_ECONNRESET) { // Connection closed, simply break - this is not an error nRetCode = HTTP_CLIENT_EOS; // Signal end of stream break; } // We have successfully got the data from the server nRetCode = HTTP_CLIENT_SUCCESS; break; } // We had a socket related error if(nRetCode != SOCKET_SSL_MORE_DATA && FD_ISSET(pConnection->HttpSocket ,&pConnection->FDError)) { FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDError); *(nLength) = 0; // To-Do: Handle this case nRetCode = HTTP_CLIENT_ERROR_SOCKET_RECV; break; } } }while(0); return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnGetRemoteChunkLength // Purpose : Receive (byte by byte) the chunk parameter (while in chunk mode receive) and // Convert the HEX string into an integer // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnGetRemoteChunkLength (P_HTTP_SESSION pHTTPSession) { UINT32 nBytesRead = 1; UINT32 nRetCode = HTTP_CLIENT_SUCCESS; UINT32 nBytesCount = 0; CHAR ChunkHeader[HTTP_CLIENT_MAX_CHUNK_HEADER]; CHAR *pPtr; do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Read byte by byte until we get a CrLf pPtr = ChunkHeader; // Get a pointer to the received buffer *pPtr = 0; // Terminate with null while(nBytesRead > 0) { // Receive a single byte nRetCode = HTTPIntrnRecv(pHTTPSession,pPtr,&nBytesRead,FALSE); // Did we succeed? if(nRetCode == HTTP_CLIENT_SUCCESS && nBytesRead > 0) { // Increment the bytes count nBytesCount += nBytesRead; if(nBytesRead > HTTP_CLIENT_MAX_CHUNK_HEADER) { // Error chunk buffer is full nRetCode = HTTP_CLIENT_ERROR_CHUNK_TOO_BIG; break; } // Don't Process if the fist 2 bytes are CrLf. if(! ((nBytesCount == 1 && *pPtr == 0x0d) || (nBytesCount == 2 && *pPtr == 0x0a))) { // Advance the pointer by the received data length pPtr += nBytesRead; // Look for CrLf in the last 2 bytes if(memcmp(pPtr - 2,HTTP_CLIENT_CRLF,2) == 0) { // Chunk Header was received *pPtr = 0; // null terminate the chunk parameter pHTTPSession->HttpCounters.nRecivedChunkLength = HTTPStrHToL(ChunkHeader); // Convert to a number // Set the HTTP counters pHTTPSession->HttpCounters.nBytesToNextChunk = pHTTPSession->HttpCounters.nRecivedChunkLength; break; } } } else // Socket Error { nRetCode = 0; break; } } } while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnGetRemoteChunkLength",NULL,0,"Next chunk is %d bytes",pHTTPSession->HttpCounters.nRecivedChunkLength); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnGetRemoteHeaders // Purpose : Perform a socket receive (byte by byte) until all the HTTP headers are received // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnGetRemoteHeaders (P_HTTP_SESSION pHTTPSession) { UINT32 nBytesRead = 1; UINT32 nRetCode = HTTP_CLIENT_SUCCESS; UINT32 nProjectedHeaderLength; UINT32 nProjectedBufferLength; INT32 nCurrentfreeSpace; INT32 nProjectedfreeSpace; CHAR *pPtr; do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Read byte by byte until we get CrLf followed by CrLf // Set the incoming headers pointer if(!pHTTPSession->HttpHeaders.HeadersIn.pParam) { /// The incoming headers starts where the outgoing headers ends pHTTPSession->HttpHeaders.HeadersIn.pParam = pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength; } // Receive until we get all the headers or any other error event while(nBytesRead > 0) { // Size of the projected buffer we are going to receive nProjectedHeaderLength = nBytesRead; // Size of the projected total incoming buffer nProjectedBufferLength = nProjectedHeaderLength + pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength; // Current free space on the incoming headers buffer nCurrentfreeSpace = pHTTPSession->HttpHeaders.HeadersBuffer.nLength - (pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength); // Projected free space after the completion of the receive nProjectedfreeSpace = nCurrentfreeSpace - nProjectedHeaderLength; // Check total size limit if(nProjectedBufferLength > HTTP_CLIENT_MAX_SEND_RECV_HEADERS) { return HTTP_CLIENT_ERROR_NO_MEMORY; } if((INT32)nProjectedfreeSpace < 0) { if(HTTP_CLIENT_MEMORY_RESIZABLE == FALSE) { // Need more space but we can't grow beyond the current size nRetCode = HTTP_CLIENT_ERROR_NO_MEMORY; break; } else { // We can resizes so.. nRetCode = HTTPIntrnResizeBuffer(pHTTPSession,nProjectedBufferLength + HTTP_CLIENT_MEMORY_RESIZE_FACTOR); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } } } // Jump to the beginning of the incoming headers (just after the end of the outgoing headers) pPtr = pHTTPSession->HttpHeaders.HeadersIn.pParam + pHTTPSession->HttpHeaders.HeadersIn.nLength; // Read a single byte nRetCode = HTTPIntrnRecv(pHTTPSession,pPtr,&nBytesRead,FALSE); // ToDo: Break if not getting HTTP on the first 4 bytes if(nRetCode == HTTP_CLIENT_SUCCESS && nBytesRead > 0) { // Advance the pointer by 1 byte pPtr += nBytesRead; // Increase the total receive length pHTTPSession->HttpHeaders.HeadersIn.nLength++; // Set the HTTP counters pHTTPSession->HttpCounters.nRecivedHeaderLength++; if(memcmp(pPtr - 4,HTTP_CLIENT_CRLFX2,4) == 0) { // Headers were received break; } } else { TLS_DBGPRT_INFO("HTTPIntrnRecv nRetCode = %d\n", nRetCode); if(nRetCode != HTTP_CLIENT_ERROR_SOCKET_TIME_OUT) nRetCode = HTTP_CLIENT_ERROR_HEADER_RECV; // This was marked out for some reason break; } } }while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnGetRemoteHeaders",NULL,0,"Got %d bytes",pHTTPSession->HttpHeaders.HeadersIn.nLength); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnHeadersFind // Purpose : Look for a header (insensitive search) by its name // Gets : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnHeadersFind (P_HTTP_SESSION pHTTPSession,CHAR *pHeaderName, HTTP_PARAM *pParam, // [OUT] HTTP parameter structure that holds the search results BOOL IncommingHeaders, // [IN] Indicate if we are to search in the incoming or outgoing headers UINT32 nOffset) // [IN] Optionaly privide an offset to start looking from { CHAR *pHeaderEnd; CHAR *Header; // To-Do: Use pointers insted of fixed length UINT32 nLength; UINT32 nRetCode = HTTP_CLIENT_ERROR_HEADER_NOT_FOUND; do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Reset the input parameter structure pParam->pParam = NULL; pParam->nLength = 0; // Get the requested header length nLength = strlen(pHeaderName); if(nLength > (HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE - 3)) { // Error : header search clue too big nRetCode = HTTP_CLIENT_ERROR_HEADER_BIG_CLUE; break; } // Build the searched header name , add a leading CrLf before the header name and trailing ":" Header = tls_mem_alloc(HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE); if(Header == NULL) return HTTP_CLIENT_ERROR_NO_MEMORY; memset(Header,0x00,HTTP_CLIENT_MAX_HEADER_SEARCH_CLUE); strcpy(Header,HTTP_CLIENT_CRLF); strcat(Header,pHeaderName); strcat(Header,":"); // Case insensitive search for the header name (search the incoming headers) if(IncommingHeaders == TRUE) { pParam->pParam = HTTPStrCaseStr(pHTTPSession->HttpHeaders.HeadersIn.pParam + nOffset, pHTTPSession->HttpHeaders.HeadersIn.nLength, Header); } else { // Optionally search the outgoing headers pParam->pParam = HTTPStrCaseStr(pHTTPSession->HttpHeaders.HeadersOut.pParam + nOffset, pHTTPSession->HttpHeaders.HeadersOut.nLength, Header); } tls_mem_free(Header); if(pParam->pParam) // Did we find it? { // Search for the token end (trailing CrLf) pHeaderEnd = strstr(pParam->pParam + 2,HTTP_CLIENT_CRLF); if(pHeaderEnd) { // Get the length (up to the CrLf) pParam->nLength = pHeaderEnd - pParam->pParam; nRetCode = HTTP_CLIENT_SUCCESS; break; } } }while(0); // Could not find the header return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnHeadersSend // Purpose : Build and send the HTTP request. this includes the HTTP headers // and any required authentication data // Gets : // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnHeadersSend(P_HTTP_SESSION pHTTPSession, HTTP_VERB HttpVerb) // [IN] Argument that can bypass the requested verb // Can be used for evaluating a HEAD request { UINT32 nBytes; UINT32 nRetCode = HTTP_CLIENT_SUCCESS; CHAR RequestCmd[16]; CHAR ContentLength[32]; BOOL RestoreHeadersFlag = FALSE; HTTP_VERB HttpCachedVerb; if(!pHTTPSession) { // Bad session pointer error return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnHeadersSend",NULL, 0,"Using Verb: %d",(INT32)HttpVerb); } #endif // Cache the original VERB HttpCachedVerb = pHTTPSession->HttpHeaders.HttpVerb; do { // Set the verb (temporarily) if(pHTTPSession->HttpHeaders.HttpVerb != HttpVerb) { if((nRetCode = HTTPClientSetVerb((HTTP_SESSION_HANDLE)pHTTPSession,HttpVerb)) != HTTP_CLIENT_SUCCESS) { break; } } // Remeber this state for later usage pHTTPSession->HttpHeaders.HttpLastVerb = pHTTPSession->HttpHeaders.HttpVerb; // If this is a head request we should temporary remove the chunking header and the content length header if(pHTTPSession->HttpHeaders.HttpVerb == VerbHead) { // If send in chunks flag was set if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) { // Chunking if((nRetCode = HTTPIntrnHeadersRemove(pHTTPSession,"Transfer-Encoding")) != HTTP_CLIENT_SUCCESS) { break; } } // Content-Length if(pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength > 0) // Attempt to remove only if it was previusly set { if((nRetCode = HTTPIntrnHeadersRemove(pHTTPSession,"Content-Length")) != HTTP_CLIENT_SUCCESS) { break; } } RestoreHeadersFlag = TRUE; // So it would be restored later } // Request Verb nBytes = strlen(pHTTPSession->HttpHeaders.Verb) + 1; memset(RequestCmd,0x00,16); strcpy(RequestCmd,pHTTPSession->HttpHeaders.Verb); strcat(RequestCmd," "); if((nRetCode = HTTPIntrnSend(pHTTPSession,RequestCmd,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; // Request URI if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_USINGPROXY) != HTTP_CLIENT_FLAG_USINGPROXY) { nBytes = pHTTPSession->HttpUrl.UrlRequest.nLength; if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.UrlRequest.pParam,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; } #if TLS_CONFIG_HTTP_CLIENT_PROXY else { nBytes = strlen(pHTTPSession->HttpUrl.Url); if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.Url,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY // Request HTTP Version memset(RequestCmd,0x00,16); strcpy(RequestCmd," "); strcat(RequestCmd,HTTP_CLIENT_DEFAULT_VER); strcat(RequestCmd,HTTP_CLIENT_CRLF); nBytes = strlen(RequestCmd); if((nRetCode = HTTPIntrnSend(pHTTPSession,RequestCmd,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; // Request headers nBytes = pHTTPSession->HttpHeaders.HeadersOut.nLength; if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpHeaders.HeadersOut.pParam,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; #if TLS_CONFIG_HTTP_CLIENT_AUTH // Optionally add authentication headers and send them (for host or proxy authentication) if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_UNAUTHORIZED #if TLS_CONFIG_HTTP_CLIENT_PROXY || pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED #endif //TLS_CONFIG_HTTP_CLIENT_PROXY ) { if((nRetCode = HTTPIntrnAuthHandler(pHTTPSession)) != HTTP_CLIENT_SUCCESS) { break; } } #endif //TLS_CONFIG_HTTP_CLIENT_AUTH // Request terminating CrLf nBytes = strlen(HTTP_CLIENT_CRLF); if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF,&nBytes)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes; // Restore the verb if(pHTTPSession->HttpHeaders.HttpVerb != HttpCachedVerb) { if((nRetCode = HTTPClientSetVerb((HTTP_SESSION_HANDLE)pHTTPSession,HttpCachedVerb)) != HTTP_CLIENT_SUCCESS) { break; } } if(RestoreHeadersFlag == TRUE) { // Restore chunking header (since it was temporarily removed for the head request // Add the Transfer-Encoding: header if((pHTTPSession->HttpFlags & HTTP_CLIENT_FLAG_SEND_CHUNKED) == HTTP_CLIENT_FLAG_SEND_CHUNKED) { if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Transfer-Encoding",17,"chunked",7))!= HTTP_CLIENT_SUCCESS) { break; } } // Restore the content length if(pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength > 0) // Attempt to remove only if it was previusly set { IToA(ContentLength,pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength); // Convert the buffer length to a string value if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Length",14,ContentLength,strlen(ContentLength)))!= HTTP_CLIENT_SUCCESS) { return nRetCode; } } } // Set the session stage pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_REQUEST_SENT; } while(0); return nRetCode; // end of HTTPIntrnSendHeaders() } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnHeadersParse // Purpose : Parse the HTTP incoming headers. // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnHeadersParse (P_HTTP_SESSION pHTTPSession) { CHAR *pPtr; // a pointer that points on the incoming headers UINT32 nTokenLength = 0; // Length of the parsed token UINT32 nRetCode = HTTP_CLIENT_SUCCESS; // a function return code value #if TLS_CONFIG_HTTP_CLIENT_AUTH UINT32 nOffset = 0; // Bytes offset (strings comperision) BOOL AuthHeaders = FALSE; // While we are searching the authentication methods #endif CHAR *HTTPToken; // Buffer for the parsed HTTP token HTTP_PARAM HTTPParam; // A generic pointer\length parameter for parsing do { // Validate the session pointer if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnHeadersParse",pHTTPSession->HttpHeaders.HeadersIn.pParam,pHTTPSession->HttpHeaders.HeadersIn.nLength,"[Incomming Headers]"); } #endif // Set a pointer on the incoming headers pPtr = pHTTPSession->HttpHeaders.HeadersIn.pParam; // Detect the leading HTTP string if(HTTPStrInsensitiveCompare(pPtr,"http",4) != TRUE) { nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; break; } // Get the HTTP Version while ((*pPtr) && (*pPtr != 0x20)) { nTokenLength++; pPtr++; // Move to the first space } strncpy(pHTTPSession->HttpHeadersInfo.HTTPVersion, pPtr - nTokenLength, MIN(15,nTokenLength)); pPtr++; // Get the HTTP status code HTTPToken = tls_mem_alloc(HTTP_CLIENT_MAX_TOKEN_LENGTH); if(HTTPToken == NULL) return HTTP_CLIENT_ERROR_NO_MEMORY; memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); nTokenLength = 0; while ((*pPtr) && (*pPtr != 0x20)) { nTokenLength++; pPtr++; // Move to the next space } strncpy(HTTPToken,(pPtr - nTokenLength),MIN(HTTP_CLIENT_MAX_TOKEN_LENGTH,nTokenLength)); pHTTPSession->HttpHeadersInfo.nHTTPStatus = atol(HTTPToken); // Search for content length pHTTPSession->HttpHeadersInfo.nHTTPContentLength = 0; // Default no unknown length // Look for the token if(HTTPIntrnHeadersFind(pHTTPSession,"content-length",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS) { memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); // Reset the token buffer nTokenLength = HTTP_CLIENT_MAX_TOKEN_LENGTH; // Set the buffer length // Attempt to extract the token if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength)) { // Convert the content-length into an integer. pHTTPSession->HttpHeadersInfo.nHTTPContentLength = atol(HTTPToken); } } // Search for connection status pHTTPSession->HttpHeadersInfo.Connection = TRUE; // Default status where no server connection header was detected // Look for token (can be standard connection or a proxy connection) if( (HTTPIntrnHeadersFind(pHTTPSession,"connection",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS) || (HTTPIntrnHeadersFind(pHTTPSession,"proxy-connection",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS)) { memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); nTokenLength = HTTP_CLIENT_MAX_TOKEN_LENGTH; // Attempt to extract the token if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength)) { // Is this a keep alive session? pHTTPSession->HttpHeadersInfo.Connection = HTTPStrInsensitiveCompare(HTTPToken,"keep-alive",0); // Is it a closed session if(HTTPStrInsensitiveCompare(HTTPToken,"close",0) == TRUE) { pHTTPSession->HttpHeadersInfo.Connection = FALSE; } } } // Search for chunking mode transfer pHTTPSession->HttpFlags = pHTTPSession->HttpFlags &~ HTTP_CLIENT_FLAG_CHUNKED; // Remove the flag if(HTTPIntrnHeadersFind(pHTTPSession,"transfer-encoding",&HTTPParam,TRUE,0) == HTTP_CLIENT_SUCCESS) { memset(HTTPToken,0x00,HTTP_CLIENT_MAX_TOKEN_LENGTH); nTokenLength = HTTP_CLIENT_MAX_TOKEN_LENGTH; if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength)) { // If the chunks token was find then set the session flag accordingly if(HTTPStrInsensitiveCompare(HTTPToken,"chunked",0) == TRUE) { pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTP_CLIENT_FLAG_CHUNKED; } } } tls_mem_free(HTTPToken); #if TLS_CONFIG_HTTP_CLIENT_AUTH // Look for the authentication header while(AuthHeaders == FALSE) // address multiple authentication methods presented by the server { if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_UNAUTHORIZED) { // Double check for the "www-authenticate" header token if(HTTPIntrnHeadersFind(pHTTPSession,"www-authenticate",&pHTTPSession->HttpAuthHeader.AuthHeader,TRUE,nOffset) != HTTP_CLIENT_SUCCESS) { if(nOffset > 0) // an authentication header was found but not the right one so adjust the error { nRetCode = HTTP_CLIENT_ERROR_AUTH_MISMATCH; } else { nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; } break; } // Make sure that we get an authentication header that maches the caller requested schema pPtr = HTTPStrCaseStr(pHTTPSession->HttpAuthHeader.AuthHeader.pParam, pHTTPSession->HttpAuthHeader.AuthHeader.nLength, pHTTPSession->HttpCredentials.AuthSchemaName); if(pPtr) { AuthHeaders = TRUE; } else { // Simply pass the point where the last "www" was found nOffset = (pHTTPSession->HttpAuthHeader.AuthHeader.pParam - pHTTPSession->HttpHeaders.HeadersIn.pParam) + 3; } } else { AuthHeaders = TRUE; } } #if TLS_CONFIG_HTTP_CLIENT_PROXY // Is this a proxy authentication header? if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { // Double check for the "Proxy-Authentication" header token if (HTTPIntrnHeadersFind(pHTTPSession,"proxy-authenticate",&pHTTPSession->HttpAuthHeader.AuthHeader,TRUE,0) != HTTP_CLIENT_SUCCESS) { nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; break; } } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY #endif //TLS_CONFIG_HTTP_CLIENT_AUTH // Do we have a redirection response? if( (pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OBJECT_MOVED) || (pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OBJECT_MOVED_PERMANENTLY)) { // Check for the "Location" header token if (HTTPIntrnHeadersFind(pHTTPSession,"location",&pHTTPSession->HttpHeadersInfo.HttpRedirectURL,TRUE,0) != HTTP_CLIENT_SUCCESS) { // Protocol violation, we got a redirect code without the host name to redirect to nRetCode = HTTP_CLIENT_ERROR_BAD_HEADER; break; } // Fix the pointers location (address the "Location: " prefix) pHTTPSession->HttpHeadersInfo.HttpRedirectURL.pParam += 12; pHTTPSession->HttpHeadersInfo.HttpRedirectURL.nLength -= 12; } }while(0); return nRetCode; } #if TLS_CONFIG_HTTP_CLIENT_AUTH /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnAuthenticate // Purpose : // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnAuthenticate(P_HTTP_SESSION pHTTPSession) { UINT32 nRetCode = HTTP_CLIENT_SUCCESS; // Function call return code UINT32 nBytes = 32; UINT32 nTotalBytes = 0; CHAR ErrorPage[32]; BOOL NewConnection = FALSE; do { // Validate the session pointer if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } // Handle connection close message (reconnect) if(pHTTPSession->HttpHeadersInfo.Connection == FALSE) { // Gracefully close the connection and set the socket as invalid if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) { HTTPIntrnConnectionClose(pHTTPSession); } // Connect to the remote server (or proxy) nRetCode = HTTPIntrnConnectionOpen(pHTTPSession); if(nRetCode != HTTP_CLIENT_SUCCESS) { break; } NewConnection = TRUE; } // Analyze the security headers and optionally build the authentication reply header if((nRetCode = HTTPIntrnParseAuthHeader(pHTTPSession))!= HTTP_CLIENT_SUCCESS) { break; } // We have to recive any HTML data here inorder to "Clear" the socket buffer for later usage // Note: We should skip this when the HEAD verb was used while(NewConnection == FALSE && pHTTPSession->HttpHeaders.HttpLastVerb != VerbHead && pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0 && nBytes > 0) { ErrorPage[0] = 0; if((nRetCode = HTTPIntrnRecv(pHTTPSession,ErrorPage,&nBytes,FALSE)) != HTTP_CLIENT_SUCCESS) { break; } nTotalBytes += nBytes; if(nTotalBytes >= pHTTPSession->HttpHeadersInfo.nHTTPContentLength) { break; } } // Re-Send the headers after having analyzed the authorizaton headers if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTP_CLIENT_SUCCESS) { break; } }while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnAuthenticate",NULL,0,""); } #endif return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnParseAuthHeader // Purpose : Parse the HTTP headers for the required authentication method // Gets : // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnParseAuthHeader(P_HTTP_SESSION pHTTPSession) { CHAR *pPtrStart, *pPtrEnd; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #if TLS_CONFIG_HTTP_CLIENT_PROXY if(pHTTPSession->HttpProxy.ProxyAuthSchema != AuthSchemaNone) { // for proxy authentication simply assume basic and exit return HTTP_CLIENT_SUCCESS; } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY // Advance the pointer in the string and break on the first space pPtrEnd = pHTTPSession->HttpAuthHeader.AuthHeader.pParam + pHTTPSession->HttpAuthHeader.AuthHeader.nLength; pPtrStart = pHTTPSession->HttpAuthHeader.AuthHeader.pParam; // Jump to the first space while ((pPtrEnd - pPtrStart) > 0 && *pPtrStart != 0x20) pPtrStart++; do { #if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC if(HTTPStrCaseStr(pPtrStart,8,"basic")) { pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA = AuthSchemaBasic; break; } #endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC #if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST if(HTTPStrCaseStr(pPtrStart,8,"digest")) { pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA = AuthSchemaDigest; break; } #endif if(HTTPStrCaseStr(pPtrStart,8,"negotiate")) // Note that this could be NLM negotiation as well (which is not supported) { pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA = AuthSchemaKerberos; break; } // To-Do: Add any other supported authentication method } while(0); #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnParseAuthHeader",pHTTPSession->HttpAuthHeader.AuthHeader.pParam, pHTTPSession->HttpAuthHeader.AuthHeader.nLength,"[Incomming Auth Headers: %d]",pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA); } #endif // If we could not detect the authentication schema return an error if(pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA == AuthSchemaNone) { return HTTP_CLIENT_ERROR_BAD_AUTH; } //Make sure we are going to authenticate with the method specified by the caller if(pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA != (UINT32)pHTTPSession->HttpCredentials.CredAuthSchema) { return HTTP_CLIENT_ERROR_AUTH_MISMATCH; } return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnAuthHandler // Purpose : Differentiate between the authenticate method that we have to implement and perform // the required operation. // Gets : // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnAuthHandler (P_HTTP_SESSION pHTTPSession) { UINT32 nRetCode = HTTP_CLIENT_SUCCESS; if(!pHTTPSession) { // Bad session pointer error return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #if TLS_CONFIG_HTTP_CLIENT_PROXY if(pHTTPSession->HttpProxy.ProxyAuthSchema != AuthSchemaNone) { // For proxy authentication simply assume basic and exit // Basic authentication nRetCode = HTTPIntrnAuthSendBasic(pHTTPSession); return nRetCode; } #endif //TLS_CONFIG_HTTP_CLIENT_PROXY #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnAuthHandler",NULL, 0,""); } #endif // Use the correct authentication method as requested by the server switch(pHTTPSession->HttpAuthHeader.HTTP_AUTH_SCHEMA) { #if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC case AuthSchemaBasic: { // Basic authentication nRetCode = HTTPIntrnAuthSendBasic(pHTTPSession); break; } #endif #if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST case AuthSchemaDigest: { // Digest authentication nRetCode = HTTPIntrnAuthSendDigest(pHTTPSession); break; } #endif case AuthSchemaKerberos: { // ToDo: impliament the Kerberos nego authentication here nRetCode = HTTP_CLIENT_ERROR_NOT_IMPLEMENTED; break; } default: { // Not supported method return HTTP_CLIENT_ERROR_BAD_AUTH; // Not implemented error } }; // This session requested an authentication so.. pHTTPSession->HttpCredentials.Authentication = TRUE; return nRetCode; } #if TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnAuthSendBasic // Purpose : Handle basic authentication for direst host connection and proxy authentication // Gets : // Returns : // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnAuthSendBasic (P_HTTP_SESSION pHTTPSession) { UINT32 nSegmentLength; UINT32 nRetCode; CHAR* Cred = NULL; // Credentials (Clear) CHAR* Cred64 = NULL; // Credentials (64 bit encoded) UINT32 nSrcLength, nDestLength; CHAR* pPtr; CHAR* INITIAL_HDR = "Authorization: Basic "; CHAR* INITIAL_PROXY_HDR = "Proxy-Authorization: Basic "; do { if(!pHTTPSession) { nRetCode = HTTP_CLIENT_ERROR_INVALID_HANDLE; break; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnAuthSendBasic",NULL, 0,""); } #endif Cred = tls_mem_alloc(HTTP_CLIENT_MAX_64_ENCODED_CRED/2); if(Cred == NULL) return HTTP_CLIENT_ERROR_NO_MEMORY; Cred64 = tls_mem_alloc(HTTP_CLIENT_MAX_64_ENCODED_CRED); if(Cred64 == NULL) { tls_mem_free(Cred); return HTTP_CLIENT_ERROR_NO_MEMORY; } memset(Cred,0x00,HTTP_CLIENT_MAX_64_ENCODED_CRED /2); memset(Cred64,0x00,HTTP_CLIENT_MAX_64_ENCODED_CRED); switch (pHTTPSession->HttpHeadersInfo.nHTTPStatus) { case( HTTP_STATUS_UNAUTHORIZED): // For host authentication { // Copy the clear text credentials to a format of user:password strcpy(Cred,pHTTPSession->HttpCredentials.CredUser); strcat(Cred,":"); strcat(Cred,pHTTPSession->HttpCredentials.CredPassword); nSrcLength = strlen(Cred); nDestLength = HTTP_CLIENT_MAX_64_ENCODED_CRED; nSegmentLength = strlen(INITIAL_HDR); // Build and send the data first the hard-coded static portion if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // Convert to base 64 HTTPBase64Encoder((unsigned char *)Cred64,(const unsigned char *)Cred,nSrcLength); nDestLength = strlen(Cred64); }; break; #if TLS_CONFIG_HTTP_CLIENT_PROXY case (HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED): // For Proxy authentication { // Copy the clear text credentials to a format of user:password strcpy(Cred,pHTTPSession->HttpProxy.ProxtUser); strcat(Cred,":"); strcat(Cred,pHTTPSession->HttpProxy.ProxyPassword); nSrcLength = strlen(Cred); nDestLength = HTTP_CLIENT_MAX_64_ENCODED_CRED; // Convert to base 64 HTTPBase64Encoder((unsigned char *)Cred64,(unsigned char *)Cred,nSrcLength); nDestLength = strlen(Cred64); nSegmentLength = strlen(INITIAL_PROXY_HDR); // Build and send the data first the hard-coded static portion if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_PROXY_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; }; break; #endif //TLS_CONFIG_HTTP_CLIENT_PROXY default: { tls_mem_free(Cred); tls_mem_free(Cred64); return HTTP_CLIENT_ERROR_BAD_AUTH; // Wrong status for this function }; }; // Send the base 64 encoded data pPtr = Cred64; if((nRetCode = HTTPIntrnSend(pHTTPSession,pPtr, &nDestLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nDestLength; // Terminating CRLF nSegmentLength = strlen(HTTP_CLIENT_CRLF); if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; } while (0); if(Cred != NULL) { tls_mem_free(Cred); tls_mem_free(Cred64); } return nRetCode; } #endif //TLS_CONFIG_HTTP_CLIENT_AUTH_BASIC #if TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnAuthSendDigest // Purpose : Handle digest authentication for direct host connection and proxy authentication // Gets : // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnAuthSendDigest (P_HTTP_SESSION pHTTPSession) { CHAR Cnonce[33]; UINT32 nSegmentLength; UINT32 nRetCode; UINT32 nAlgType = 0; // a flag for the algorithem type (default to MD5) HTTP_PARAM HttpParamOpq,HttpParamRealm,HttpParamNonce,HttpParamQop,HttpParamAlg; // Pointers and lengths of the dynamic sections // of the Digest response. // Fragments of the Digest client response (The hard coded text portion of the response) CHAR* INITIAL_HDR = "Authorization: Digest username=\""; CHAR* INITIAL_PROXY_HDR = "Proxy-Authorization: Digest username=\""; CHAR* REALEM_HDR = "\", realm=\""; CHAR* QOP_HDR = "\", qop=\""; CHAR* ALGO_HDR = "\", algorithm=\""; CHAR* URI_HDR = "\", uri=\""; CHAR* NONCE_HDR = "\", nonce=\""; CHAR* NC_HDR = "\", nc=00000001, cnonce=\""; // To-Do: This should be tested!! CHAR* RSP_HDR = "\", response=\""; CHAR* OPQ_HDR = "\", opaque=\""; // Digest Calculation related HASHHEX HA1; HASHHEX HA2 = ""; HASHHEX Response; if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnAuthSendDigest",NULL, 0,""); } #endif // Generate random Cnonce number HTTPDigestGenerateCNonce(Cnonce); switch (pHTTPSession->HttpHeadersInfo.nHTTPStatus) { case( HTTP_STATUS_UNAUTHORIZED): // For host authentication { // "Authorization: Digest username=" nSegmentLength = strlen(INITIAL_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { return nRetCode; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; }; break; #if TLS_CONFIG_HTTP_CLIENT_PROXY case (HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED): // For Proxy authentication { // "Proxy-Authorization: Digest username=" nSegmentLength = strlen(INITIAL_PROXY_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_PROXY_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { return nRetCode; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; }; break; #endif //TLS_CONFIG_HTTP_CLIENT_PROXY default: { return HTTP_CLIENT_ERROR_BAD_AUTH; // Wrong status for this function }; }; do { // "Authorization: Digest username="username nSegmentLength = strlen(pHTTPSession->HttpCredentials.CredUser); if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpCredentials.CredUser, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // "Authorization: Digest username="username", realm=" nSegmentLength = strlen(REALEM_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,REALEM_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // "Authorization: Digest username="username", realm="realm if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"realm", &HttpParamRealm)) != HTTP_CLIENT_SUCCESS) { break; } if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamRealm.pParam, &HttpParamRealm.nLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamRealm.nLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", nSegmentLength = strlen(QOP_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,QOP_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"qop", &HttpParamQop)) != HTTP_CLIENT_SUCCESS) { break; } if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamQop.pParam, &HttpParamQop.nLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamQop.nLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", nSegmentLength = strlen(ALGO_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,ALGO_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"algorithm", &HttpParamAlg)) != HTTP_CLIENT_SUCCESS) { // The server did not state its required algorithm so use the default HttpParamAlg.pParam = HTTP_CLIENT_DEFAULT_DIGEST_AUTH; HttpParamAlg.nLength = strlen(HTTP_CLIENT_DEFAULT_DIGEST_AUTH); } // Get the algorithem type if(HTTPStrInsensitiveCompare(HttpParamAlg.pParam ,"md5",3 ) == TRUE) { if(HTTPStrInsensitiveCompare(HttpParamAlg.pParam ,"md5-sess", HttpParamAlg.nLength) == TRUE) { nAlgType = 1; } } else { // Error algorithem not supported nRetCode = HTTP_CLIENT_ERROR_NO_DIGEST_ALG; break; } // Send the algorithem if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamAlg.pParam, &HttpParamAlg.nLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamAlg.nLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri=" nSegmentLength = strlen(URI_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,URI_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service nSegmentLength = strlen(pHTTPSession->HttpUrl.UrlRequest.pParam); if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.UrlRequest.pParam, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service", nonce=" nSegmentLength = strlen(NONCE_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,NONCE_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service", nonce="7a5c... if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"nonce", &HttpParamNonce)) != HTTP_CLIENT_SUCCESS) { break; } if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamNonce.pParam, &HttpParamNonce.nLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamNonce.nLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce=" nSegmentLength = strlen(NC_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,NC_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341... nSegmentLength = strlen(Cnonce); if((nRetCode = HTTPIntrnSend(pHTTPSession,Cnonce, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // Send the opaque data if we got it from the server if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"opaque", &HttpParamOpq)) == HTTP_CLIENT_SUCCESS) { nSegmentLength = strlen(OPQ_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,OPQ_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamOpq.pParam, &HttpParamOpq.nLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamOpq.nLength; } // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...", response=" nSegmentLength = strlen(RSP_HDR); if((nRetCode = HTTPIntrnSend(pHTTPSession,RSP_HDR, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // Calculate response HTTPDigestCalcHA1(nAlgType, pHTTPSession->HttpCredentials.CredUser, HttpParamRealm.pParam,HttpParamRealm.nLength , pHTTPSession->HttpCredentials.CredPassword , HttpParamNonce.pParam, HttpParamNonce.nLength, Cnonce, HA1); HTTPDigestCalcResponse(HA1, HttpParamNonce.pParam, HttpParamNonce.nLength, "00000001", Cnonce, HttpParamQop.pParam,HttpParamQop.nLength, pHTTPSession->HttpHeaders.Verb, pHTTPSession->HttpUrl.UrlRequest.pParam,pHTTPSession->HttpUrl.UrlRequest.nLength, HA2, Response); // "Authorization: Digest username="username", realm="myRealm", qop="auth", // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...", response="8bbf2... nSegmentLength = strlen(Response); if((nRetCode = HTTPIntrnSend(pHTTPSession,Response, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // Terminate 0x24 (") nSegmentLength = 1; if((nRetCode = HTTPIntrnSend(pHTTPSession,"\"", &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; // Terminating CRLF nSegmentLength = strlen(HTTP_CLIENT_CRLF); if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTP_CLIENT_CRLF, &nSegmentLength)) != HTTP_CLIENT_SUCCESS) { break; } // Set the counters pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength; } while(0); return nRetCode; // End of digest respobse sending } #endif //TLS_CONFIG_HTTP_CLIENT_AUTH_DIGEST #endif //TLS_CONFIG_HTTP_CLIENT_AUTH /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnSessionReset // Purpose : Reset the session data for the next operation // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnSessionReset (P_HTTP_SESSION pHTTPSession, BOOL EntireSession) { UINT32 nActionTimeout; // For restoring a parameter after this reset UINT32 nAllocationSize; // Validate the pointer if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { pHTTPSession->pDebug("HTTPIntrnSessionReset",NULL, 0,""); } #endif memset(pHTTPSession->HttpHeaders.HeadersIn.pParam,0x00,pHTTPSession->HttpHeaders.HeadersIn.nLength); pHTTPSession->HttpHeaders.HeadersIn.nLength = 0; // Reset the HTTP counters nActionTimeout = pHTTPSession->HttpCounters.nActionTimeout; memset(&pHTTPSession->HttpCounters,0x00,sizeof(HTTP_COUNTERS)); pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime(); // Restore the parameter pHTTPSession->HttpCounters.nActionTimeout = nActionTimeout; // Reset the authentication flag pHTTPSession->HttpCredentials.Authentication = FALSE; if(EntireSession == TRUE) // Partial reset, clear only the incoming headers { if(pHTTPSession->HttpUrl.Url != NULL) { tls_mem_free(pHTTPSession->HttpUrl.Url); pHTTPSession->HttpUrl.Url = NULL; } memset(&pHTTPSession->HttpUrl,0,sizeof(HTTP_URL)); nAllocationSize = pHTTPSession->HttpHeaders.HeadersBuffer.nLength; // Reset the headers allocated memory memset(pHTTPSession->HttpHeaders.HeadersBuffer.pParam ,0x00,nAllocationSize); // Set default values in the session structure HTTPClientSetVerb((UINT32)pHTTPSession,(HTTP_VERB)HTTP_CLIENT_DEFAULT_VERB); // Default HTTP verb pHTTPSession->HttpUrl.nPort = HTTP_CLIENT_DEFAULT_PORT; // Default TCP port // Set the outgoing headers pointers memset(&pHTTPSession->HttpHeaders.HeadersIn,0,sizeof(HTTP_PARAM)); memset(&pHTTPSession->HttpHeaders.HeadersOut,0,sizeof(HTTP_PARAM)); pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam; // Set our state pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_INIT; memset(&pHTTPSession->HttpHeadersInfo,0,sizeof(HTTP_HEADERS_INFO)); if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) { pHTTPSession->HttpHeadersInfo.Connection = TRUE; } memset(&pHTTPSession->HttpAuthHeader,0,sizeof(HTTP_AUTH_HEADER)); #if TLS_CONFIG_HTTP_CLIENT_PROXY memset(&pHTTPSession->HttpProxy,0,sizeof(HTTP_PROXY)); #endif //TLS_CONFIG_HTTP_CLIENT_PROXY } return HTTP_CLIENT_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnHeadersReceive // Purpose : Receives the response header on the connection and parses it. // Performs any required authentication. // Returns : HTTP Status // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnHeadersReceive (P_HTTP_SESSION pHTTPSession, UINT32 nTimeout) // [IN] Timeout for the operation { UINT32 nRetCode; // Function call return code UINT32 nCount = 0; if(!pHTTPSession) { // Bad session pointer error return HTTP_CLIENT_ERROR_INVALID_HANDLE; } do { // Set the operation time out if was set by the caller if(nTimeout > 0) { // 0 makes us use the default defined value pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout); } // Reset the incoming headers if((nRetCode = HTTPIntrnSessionReset(pHTTPSession,FALSE)) != HTTP_CLIENT_SUCCESS) { break; } // Get the server response if((nRetCode = HTTPIntrnGetRemoteHeaders(pHTTPSession)) != HTTP_CLIENT_SUCCESS) { break; } // Set the session state pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEADERS_RECIVED; // Parse the response headers if((nRetCode = HTTPIntrnHeadersParse(pHTTPSession)) != HTTP_CLIENT_SUCCESS) { break; } // Set the session state pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEADERS_PARSED; // Set the session stage upon seccess if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OK) { pHTTPSession->HttpState = pHTTPSession->HttpState | HTTP_CLIENT_STATE_HEADERS_OK; } // Handle 100 continue message if(pHTTPSession->HttpHeadersInfo.nHTTPStatus != HTTP_STATUS_CONTINUE) { nCount++; } #ifdef _HTTP_DEBUGGING_ if(pHTTPSession->pDebug) { if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_CONTINUE) { pHTTPSession->pDebug("HTTPIntrnHeadersReceive",NULL,0,"100 Continue Header"); } } #endif }while(nCount < 1); return nRetCode; } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnGetTicks // Purpose : Like GetTickCount() (implemented with time.h) // Gets : void // Returns : System ticks // Last updated : 01/09/200515/05/2005 // Author Name : Eitan Michaelson // Notes : Assuming 1000 ticks per sec, should be implemented by the OS // /////////////////////////////////////////////////////////////////////////////// static UINT32 HTTPIntrnSessionGetUpTime(VOID) { return GetUpTime(); } /////////////////////////////////////////////////////////////////////////////// // // Function : HTTPIntrnSessionEvalTimeout // Purpose : Check if we have to break the operation and return a time out error // Gets : a pointer to the session structure // Returns : BOOL, True if we have to break // Last updated : 01/09/2005 // /////////////////////////////////////////////////////////////////////////////// static BOOL HTTPIntrnSessionEvalTimeout(P_HTTP_SESSION pHTTPSession) { UINT32 nElapsedTime; // Integer for calculating the elapsed time // Validate the session pointer if(!pHTTPSession) { return HTTP_CLIENT_ERROR_INVALID_HANDLE; } // Calculate the elapsed time since the last call nElapsedTime = HTTPIntrnSessionGetUpTime() - pHTTPSession->HttpCounters.nActionStartTime; // If the elapsed time is greater then the time out value we should return true if(nElapsedTime >= pHTTPSession->HttpCounters.nActionTimeout) { return TRUE; } return FALSE; } #endif //TLS_CONFIG_HTTP_CLIENT