| // Copyright 2019 Google LLC |
| // |
| // 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 LLC 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 |