| // 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 <assert.h> | |
| #include <stdlib.h> | |
| #include <windows.h> | |
| #include <winhttp.h> | |
| #include <vector> | |
| 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<DWORD>(scheme_buffer_length); | |
| url_comp.lpszHostName = host; | |
| url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length); | |
| url_comp.lpszUrlPath = uri; | |
| url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length); | |
| bool result = !!::WinHttpCrackUrl(url, 0, flags, &url_comp); | |
| if (result) { | |
| *port = static_cast<int>(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<INTERNET_PORT>(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<void*>(&http_status_string), | |
| &http_status_string_size, 0)) { | |
| return false; | |
| } | |
| *status_code = static_cast<DWORD>(_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<void*>(&content_length_string), | |
| &content_length_string_size, 0)) { | |
| *content_length = kUnknownContentLength; | |
| } else { | |
| *content_length = | |
| static_cast<DWORD>(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<AccessType>(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<HINTERNET>(handle); | |
| } | |
| HttpHandle WinHttpClient::FromHINTERNET(HINTERNET handle) { | |
| return static_cast<HttpHandle>(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 |