#include "http/http.h" #include "tier1/interface.h" #include "tier1/utlstring.h" #include "netdb.h" #include "unistd.h" #include "fcntl.h" #include "openssl/ssl.h" #include "openssl/err.h" abstract_class CHTTPClient: public IHTTPClient { public: virtual void Post( const char *szResource, HTTPHeader_t *pHeader, uint32_t uDataSize, const char *data ) override; virtual void Get( const char *szResource, HTTPHeader_t *pHeader ) override; virtual HTTPResponse_t GetResponse() override; HTTPResponse_t ParseResponse( const char *szMessage ); const char *GetVersion(); const char *m_szHostName; bool m_bIsSecure; SSL *m_pSSL; SSL_CTX *m_pSSLCtx; int m_iFileDescriptor; }; void CHTTPClient::Post( const char *szResource, HTTPHeader_t *pHeader, uint32_t uDataSize, const char *data ) { } void CHTTPClient::Get( const char *szResource, HTTPHeader_t *pHeader ) { if (pHeader == NULL) return; CUtlString szMessage = CUtlString( "GET %s HTTP/%s\r\n", szResource, GetVersion()); CUtlString szHeader = CUtlString( "Host: %s\r\n", m_szHostName ); CUtlString szCombined; int i = 0; for ( i = 0; i < pHeader->m_nParamCount; i++ ) { szHeader.AppendTail(CUtlString("%s: %s\r\n", pHeader->m_params[i].m_szParamName, pHeader->m_params[i].m_szValue)); } szCombined = szMessage; szCombined.AppendTail(szHeader); szCombined.AppendTail("\r\n"); if (m_bIsSecure) SSL_write(m_pSSL, szCombined.GetString(), szCombined.GetLenght()); else write(m_iFileDescriptor, szCombined.GetString(), szCombined.GetLenght()); } HTTPResponse_t CHTTPClient::GetResponse() { CUtlResizableBuffer szCharBuffer(0); char response[4096]; int n; int nPreviousSize = 0; if (m_bIsSecure) { secureRead: n = SSL_read(m_pSSL, response, sizeof(response)); szCharBuffer.Resize(nPreviousSize+n); V_memcpy((char*)szCharBuffer.GetMemory()+nPreviousSize, response, n); nPreviousSize += n; if (SSL_pending(m_pSSL)>0) goto secureRead; } else { httpRead: n = read(m_iFileDescriptor, response, sizeof(response)); V_printf("%i\n",n); szCharBuffer.Resize(nPreviousSize+n+1); szCharBuffer[nPreviousSize+n] = 0; V_memcpy((char*)szCharBuffer.GetMemory()+nPreviousSize, response, n); nPreviousSize += n; goto httpRead; } szCharBuffer.Resize(n+1); return ParseResponse(szCharBuffer); } HTTPResponse_t CHTTPClient::ParseResponse( const char *szMessage ) { char cPreviousCharacter = 0; char cCurrentCharacter = 0; const char *pcCurrentCharacter = szMessage; CUtlString szBuffer = ""; bool bIsMessage = true; HTTPResponse_t response = {}; CUtlVector headers = {}; CUtlBuffer data = {}; // Parse header while (*pcCurrentCharacter) { cCurrentCharacter = *pcCurrentCharacter; if ( cPreviousCharacter == '\r') { if ( cCurrentCharacter == '\n') { if (bIsMessage) { uint32_t uResult = 0; V_sscanf(szBuffer, "HTTP/1.1 %i", &uResult); response.m_uCode = uResult; } bIsMessage = false; if (szBuffer == "") break; szBuffer = ""; cPreviousCharacter = 0; cCurrentCharacter = *pcCurrentCharacter++; continue; } } if (cPreviousCharacter != 0) szBuffer.AppendTail(cPreviousCharacter); pcCurrentCharacter++; cPreviousCharacter = cCurrentCharacter; }; pcCurrentCharacter++; uint32_t nDataLen = V_strlen(pcCurrentCharacter); data = CUtlBuffer(nDataLen+1); V_strcpy(data.GetMemory(), pcCurrentCharacter); data[nDataLen] = 0; response.m_message = data; return response; } const char *CHTTPClient::GetVersion() { return "1.1"; } abstract_class CHTTPClientManager: public IHTTPClientManager { public: CHTTPClientManager(); virtual IHTTPClient *Connect( const char *szUrl, bool bSecure ) override; virtual void Disconnect( IHTTPClient *szConnection ) override; }; IHTTPClient *CHTTPClientManager::Connect( const char *szUrl, bool bSecure ) { struct hostent *pServerHostName = NULL; struct sockaddr_in serverAddress; int fd = 0; int err; CHTTPClient *pClient; SSL_CTX *ctx; SSL *ssl; pServerHostName = gethostbyname(szUrl); if (!pServerHostName) return NULL; fd = socket(AF_INET, SOCK_STREAM, 0); if ( fd < 0 ) return NULL; V_memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; V_memcpy(&serverAddress.sin_addr.s_addr, pServerHostName->h_addr, pServerHostName->h_length); // https if ( bSecure ) serverAddress.sin_port = htons(443); else serverAddress.sin_port = htons(80); err = connect(fd, (struct sockaddr *)&serverAddress, sizeof(serverAddress)); if (err < 0) return NULL; if (bSecure) { ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) return NULL; ssl =SSL_new(ctx); if (!ssl) return NULL; SSL_set_fd(ssl, fd); SSL_set_tlsext_host_name(ssl, szUrl); int r = SSL_connect(ssl); if (r <= 0) { ERR_print_errors_fp(stdout); SSL_free(ssl); SSL_CTX_free(ctx); return NULL; } }; pClient = new CHTTPClient(); pClient->m_iFileDescriptor = fd; pClient->m_szHostName = szUrl; if (bSecure) { pClient->m_bIsSecure = bSecure; pClient->m_pSSL = ssl; pClient->m_pSSLCtx = ctx; } return pClient; } void CHTTPClientManager::Disconnect( IHTTPClient *pClient ) { CHTTPClient *pC = (CHTTPClient*)pClient; close(pC->m_iFileDescriptor); } CHTTPClientManager::CHTTPClientManager() { SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); } CHTTPClientManager s_HttpClientManager; EXPOSE_INTERFACE_GLOBALVAR(IHTTPClientManager, CHTTPClientManager, HTTP_CLIENT_INTERFACE_VERSION, s_HttpClientManager);