// Copyright 2019 Google Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "tools/windows/converter_exe/winhttp_client.h" #include #include #include #include #include namespace crash { namespace internal { // This class implements HttpClient based on WinInet APIs. class WinHttpClient : public HttpClient { public: virtual ~WinHttpClient() {} virtual bool CrackUrl(const TCHAR* url, DWORD flags, TCHAR* scheme, size_t scheme_buffer_length, TCHAR* host, size_t host_buffer_length, TCHAR* uri, size_t uri_buffer_length, int* port) const; virtual bool Open(const TCHAR* user_agent, DWORD access_type, const TCHAR* proxy_name, const TCHAR* proxy_bypass, HttpHandle* session_handle) const; virtual bool Connect(HttpHandle session_handle, const TCHAR* server, int port, HttpHandle* connection_handle) const; virtual bool OpenRequest(HttpHandle connection_handle, const TCHAR* verb, const TCHAR* uri, const TCHAR* version, const TCHAR* referrer, bool is_secure, HttpHandle* request_handle) const; virtual bool SendRequest(HttpHandle request_handle, const TCHAR* headers, DWORD headers_length) const; virtual bool ReceiveResponse(HttpHandle request_handle) const; virtual bool GetHttpStatusCode(HttpHandle request_handle, int* status_code) const; virtual bool GetContentLength(HttpHandle request_handle, DWORD* content_length) const; virtual bool ReadData(HttpHandle request_handle, void* buffer, DWORD buffer_length, DWORD* bytes_read) const; virtual bool Close(HttpHandle handle) const; private: static DWORD MapAccessType(DWORD access_type); static HINTERNET ToHINTERNET(HttpHandle handle); static HttpHandle FromHINTERNET(HINTERNET handle); }; bool WinHttpClient::CrackUrl(const TCHAR* url, DWORD flags, TCHAR* scheme, size_t scheme_buffer_length, TCHAR* host, size_t host_buffer_length, TCHAR* uri, size_t uri_buffer_length, int* port) const { assert(url); assert(scheme); assert(host); assert(uri); assert(port); URL_COMPONENTS url_comp = {0}; url_comp.dwStructSize = sizeof(url_comp); url_comp.lpszScheme = scheme; url_comp.dwSchemeLength = static_cast(scheme_buffer_length); url_comp.lpszHostName = host; url_comp.dwHostNameLength = static_cast(host_buffer_length); url_comp.lpszUrlPath = uri; url_comp.dwUrlPathLength = static_cast(uri_buffer_length); bool result = !!::WinHttpCrackUrl(url, 0, flags, &url_comp); if (result) { *port = static_cast(url_comp.nPort); } return result; } bool WinHttpClient::Open(const TCHAR* user_agent, DWORD access_type, const TCHAR* proxy_name, const TCHAR* proxy_bypass, HttpHandle* session_handle) const { *session_handle = FromHINTERNET(::WinHttpOpen(user_agent, MapAccessType(access_type), proxy_name, proxy_bypass, 0)); return !!(*session_handle); } bool WinHttpClient::Connect(HttpHandle session_handle, const TCHAR* server, int port, HttpHandle* connection_handle) const { assert(server); // Uses NULL user name and password to connect. *connection_handle = FromHINTERNET(::WinHttpConnect( ToHINTERNET(session_handle), server, static_cast(port), NULL)); return !!(*connection_handle); } bool WinHttpClient::OpenRequest(HttpHandle connection_handle, const TCHAR* verb, const TCHAR* uri, const TCHAR* version, const TCHAR* referrer, bool is_secure, HttpHandle* request_handle) const { assert(connection_handle); assert(verb); assert(uri); assert(request_handle); *request_handle = FromHINTERNET(::WinHttpOpenRequest( ToHINTERNET(connection_handle), verb, uri, version, referrer, WINHTTP_DEFAULT_ACCEPT_TYPES, is_secure ? WINHTTP_FLAG_SECURE : 0)); return !!(*request_handle); } bool WinHttpClient::SendRequest(HttpHandle request_handle, const TCHAR* headers, DWORD headers_length) const { assert(request_handle); return !!::WinHttpSendRequest(ToHINTERNET(request_handle), headers, headers_length, NULL, 0, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, NULL); } bool WinHttpClient::ReceiveResponse(HttpHandle request_handle) const { assert(request_handle); return !!::WinHttpReceiveResponse(ToHINTERNET(request_handle), NULL); } bool WinHttpClient::GetHttpStatusCode(HttpHandle request_handle, int* status_code) const { TCHAR http_status_string[4] = {0}; DWORD http_status_string_size = sizeof(http_status_string); if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle), WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, static_cast(&http_status_string), &http_status_string_size, 0)) { return false; } *status_code = static_cast(_tcstol(http_status_string, NULL, 10)); return true; } bool WinHttpClient::GetContentLength(HttpHandle request_handle, DWORD* content_length) const { assert(request_handle); assert(content_length); TCHAR content_length_string[11] = {0}; DWORD content_length_string_size = sizeof(content_length_string); if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle), WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, static_cast(&content_length_string), &content_length_string_size, 0)) { *content_length = kUnknownContentLength; } else { *content_length = static_cast(wcstol(content_length_string, NULL, 10)); } return true; } bool WinHttpClient::ReadData(HttpHandle request_handle, void* buffer, DWORD buffer_length, DWORD* bytes_read) const { assert(request_handle); assert(buffer); assert(bytes_read); DWORD bytes_read_local = 0; if (!::WinHttpReadData(ToHINTERNET(request_handle), buffer, buffer_length, &bytes_read_local)) { return false; } *bytes_read = bytes_read_local; return true; } bool WinHttpClient::Close(HttpHandle handle) const { assert(handle); return !!::WinHttpCloseHandle(ToHINTERNET(handle)); } DWORD WinHttpClient::MapAccessType(DWORD access_type) { switch (static_cast(access_type)) { case ACCESS_TYPE_PRECONFIG: default: return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; case ACCESS_TYPE_DIRECT: return WINHTTP_ACCESS_TYPE_NO_PROXY; case ACCESS_TYPE_PROXY: return WINHTTP_ACCESS_TYPE_NAMED_PROXY; } } HINTERNET WinHttpClient::ToHINTERNET(HttpHandle handle) { return static_cast(handle); } HttpHandle WinHttpClient::FromHINTERNET(HINTERNET handle) { return static_cast(handle); } } // namespace internal HttpClient* CreateWinHttpClient(const TCHAR* url) { assert(url); internal::WinHttpClient winhttp; wchar_t scheme[16] = {0}; wchar_t host[256] = {0}; wchar_t path[256] = {0}; int port = 0; if (!winhttp.CrackUrl(url, 0, scheme, sizeof(scheme)/sizeof(scheme[0]), host, sizeof(host)/sizeof(host[0]), path, sizeof(path)/sizeof(path[0]), &port)) { return NULL; } if (_wcsicmp(scheme, L"https") == 0) { // Winhttp under WINE doesn't support wildcard certificates, so avoid // to use it if the scheme is https. The caller should fall back to // use wininet if NULL is returned. return NULL; } return new internal::WinHttpClient(); } } // namespace crash