1 // Copyright 2019 Google Inc. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google Inc. nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "tools/windows/converter_exe/winhttp_client.h"
30 
31 #include <assert.h>
32 #include <stdlib.h>
33 #include <windows.h>
34 #include <winhttp.h>
35 #include <vector>
36 
37 namespace crash {
38 
39 namespace internal {
40 
41 // This class implements HttpClient based on WinInet APIs.
42 class WinHttpClient : public HttpClient {
43  public:
~WinHttpClient()44   virtual ~WinHttpClient() {}
45   virtual bool CrackUrl(const TCHAR* url,
46                         DWORD flags,
47                         TCHAR* scheme,
48                         size_t scheme_buffer_length,
49                         TCHAR* host,
50                         size_t host_buffer_length,
51                         TCHAR* uri,
52                         size_t uri_buffer_length,
53                         int* port) const;
54   virtual bool Open(const TCHAR* user_agent,
55                     DWORD access_type,
56                     const TCHAR* proxy_name,
57                     const TCHAR* proxy_bypass,
58                     HttpHandle* session_handle) const;
59   virtual bool Connect(HttpHandle session_handle,
60                        const TCHAR* server,
61                        int port,
62                        HttpHandle* connection_handle) const;
63   virtual bool OpenRequest(HttpHandle connection_handle,
64                            const TCHAR* verb,
65                            const TCHAR* uri,
66                            const TCHAR* version,
67                            const TCHAR* referrer,
68                            bool is_secure,
69                            HttpHandle* request_handle) const;
70   virtual bool SendRequest(HttpHandle request_handle,
71                            const TCHAR* headers,
72                            DWORD headers_length) const;
73   virtual bool ReceiveResponse(HttpHandle request_handle) const;
74   virtual bool GetHttpStatusCode(HttpHandle request_handle,
75                                  int* status_code) const;
76   virtual bool GetContentLength(HttpHandle request_handle,
77                                 DWORD* content_length) const;
78   virtual bool ReadData(HttpHandle request_handle,
79                         void* buffer,
80                         DWORD buffer_length,
81                         DWORD* bytes_read) const;
82   virtual bool Close(HttpHandle handle) const;
83 
84  private:
85   static DWORD MapAccessType(DWORD access_type);
86   static HINTERNET ToHINTERNET(HttpHandle handle);
87   static HttpHandle FromHINTERNET(HINTERNET handle);
88 };
89 
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) const90 bool WinHttpClient::CrackUrl(const TCHAR* url,
91                              DWORD flags,
92                              TCHAR* scheme,
93                              size_t scheme_buffer_length,
94                              TCHAR* host,
95                              size_t host_buffer_length,
96                              TCHAR* uri,
97                              size_t uri_buffer_length,
98                              int* port) const {
99   assert(url);
100   assert(scheme);
101   assert(host);
102   assert(uri);
103   assert(port);
104 
105   URL_COMPONENTS url_comp = {0};
106   url_comp.dwStructSize = sizeof(url_comp);
107   url_comp.lpszScheme = scheme;
108   url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);
109   url_comp.lpszHostName = host;
110   url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);
111   url_comp.lpszUrlPath = uri;
112   url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);
113 
114   bool result = !!::WinHttpCrackUrl(url, 0, flags, &url_comp);
115   if (result) {
116     *port = static_cast<int>(url_comp.nPort);
117   }
118   return result;
119 }
120 
Open(const TCHAR * user_agent,DWORD access_type,const TCHAR * proxy_name,const TCHAR * proxy_bypass,HttpHandle * session_handle) const121 bool WinHttpClient::Open(const TCHAR* user_agent,
122                          DWORD access_type,
123                          const TCHAR* proxy_name,
124                          const TCHAR* proxy_bypass,
125                          HttpHandle* session_handle)  const {
126   *session_handle = FromHINTERNET(::WinHttpOpen(user_agent,
127                                                 MapAccessType(access_type),
128                                                 proxy_name,
129                                                 proxy_bypass,
130                                                 0));
131 
132   return !!(*session_handle);
133 }
134 
Connect(HttpHandle session_handle,const TCHAR * server,int port,HttpHandle * connection_handle) const135 bool WinHttpClient::Connect(HttpHandle session_handle,
136                             const TCHAR* server,
137                             int port,
138                             HttpHandle* connection_handle) const {
139   assert(server);
140 
141   // Uses NULL user name and password to connect.
142   *connection_handle = FromHINTERNET(::WinHttpConnect(
143                                          ToHINTERNET(session_handle),
144                                          server,
145                                          static_cast<INTERNET_PORT>(port),
146                                          NULL));
147   return !!(*connection_handle);
148 }
149 
OpenRequest(HttpHandle connection_handle,const TCHAR * verb,const TCHAR * uri,const TCHAR * version,const TCHAR * referrer,bool is_secure,HttpHandle * request_handle) const150 bool WinHttpClient::OpenRequest(HttpHandle connection_handle,
151                                 const TCHAR* verb,
152                                 const TCHAR* uri,
153                                 const TCHAR* version,
154                                 const TCHAR* referrer,
155                                 bool is_secure,
156                                 HttpHandle* request_handle) const {
157   assert(connection_handle);
158   assert(verb);
159   assert(uri);
160   assert(request_handle);
161 
162   *request_handle = FromHINTERNET(::WinHttpOpenRequest(
163                                       ToHINTERNET(connection_handle),
164                                       verb,
165                                       uri,
166                                       version,
167                                       referrer,
168                                       WINHTTP_DEFAULT_ACCEPT_TYPES,
169                                       is_secure ? WINHTTP_FLAG_SECURE : 0));
170   return !!(*request_handle);
171 }
172 
SendRequest(HttpHandle request_handle,const TCHAR * headers,DWORD headers_length) const173 bool WinHttpClient::SendRequest(HttpHandle request_handle,
174                                 const TCHAR* headers,
175                                 DWORD headers_length) const {
176   assert(request_handle);
177 
178   return !!::WinHttpSendRequest(ToHINTERNET(request_handle),
179                                 headers,
180                                 headers_length,
181                                 NULL,
182                                 0,
183                                 WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
184                                 NULL);
185 }
186 
ReceiveResponse(HttpHandle request_handle) const187 bool WinHttpClient::ReceiveResponse(HttpHandle request_handle) const {
188   assert(request_handle);
189 
190   return !!::WinHttpReceiveResponse(ToHINTERNET(request_handle), NULL);
191 }
192 
GetHttpStatusCode(HttpHandle request_handle,int * status_code) const193 bool WinHttpClient::GetHttpStatusCode(HttpHandle request_handle,
194                                       int* status_code) const {
195   TCHAR http_status_string[4] = {0};
196   DWORD http_status_string_size = sizeof(http_status_string);
197   if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),
198                              WINHTTP_QUERY_STATUS_CODE,
199                              WINHTTP_HEADER_NAME_BY_INDEX,
200                              static_cast<void *>(&http_status_string),
201                              &http_status_string_size, 0)) {
202     return false;
203   }
204 
205   *status_code = static_cast<DWORD>(_tcstol(http_status_string, NULL, 10));
206   return true;
207 }
208 
GetContentLength(HttpHandle request_handle,DWORD * content_length) const209 bool WinHttpClient::GetContentLength(HttpHandle request_handle,
210                                      DWORD* content_length) const {
211   assert(request_handle);
212   assert(content_length);
213 
214   TCHAR content_length_string[11] = {0};
215   DWORD content_length_string_size = sizeof(content_length_string);
216   if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),
217                              WINHTTP_QUERY_CONTENT_LENGTH,
218                              WINHTTP_HEADER_NAME_BY_INDEX,
219                              static_cast<void *>(&content_length_string),
220                              &content_length_string_size, 0)) {
221     *content_length = kUnknownContentLength;
222   } else {
223     *content_length =
224         static_cast<DWORD>(wcstol(content_length_string, NULL, 10));
225   }
226   return true;
227 }
228 
ReadData(HttpHandle request_handle,void * buffer,DWORD buffer_length,DWORD * bytes_read) const229 bool WinHttpClient::ReadData(HttpHandle request_handle,
230                              void* buffer,
231                              DWORD buffer_length,
232                              DWORD* bytes_read) const {
233   assert(request_handle);
234   assert(buffer);
235   assert(bytes_read);
236 
237   DWORD bytes_read_local = 0;
238   if (!::WinHttpReadData(ToHINTERNET(request_handle),
239                          buffer,
240                          buffer_length,
241                          &bytes_read_local)) {
242     return false;
243   }
244   *bytes_read = bytes_read_local;
245   return true;
246 }
247 
Close(HttpHandle handle) const248 bool WinHttpClient::Close(HttpHandle handle) const {
249   assert(handle);
250   return !!::WinHttpCloseHandle(ToHINTERNET(handle));
251 }
252 
MapAccessType(DWORD access_type)253 DWORD WinHttpClient::MapAccessType(DWORD access_type) {
254   switch (static_cast<AccessType>(access_type)) {
255     case ACCESS_TYPE_PRECONFIG:
256     default:
257       return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
258     case ACCESS_TYPE_DIRECT:
259       return WINHTTP_ACCESS_TYPE_NO_PROXY;
260     case ACCESS_TYPE_PROXY:
261       return WINHTTP_ACCESS_TYPE_NAMED_PROXY;
262   }
263 }
264 
265 
ToHINTERNET(HttpHandle handle)266 HINTERNET WinHttpClient::ToHINTERNET(HttpHandle handle) {
267   return static_cast<HINTERNET>(handle);
268 }
269 
FromHINTERNET(HINTERNET handle)270 HttpHandle WinHttpClient::FromHINTERNET(HINTERNET handle) {
271   return static_cast<HttpHandle>(handle);
272 }
273 
274 }  // namespace internal
275 
CreateWinHttpClient(const TCHAR * url)276 HttpClient* CreateWinHttpClient(const TCHAR* url) {
277   assert(url);
278 
279   internal::WinHttpClient winhttp;
280   wchar_t scheme[16] = {0};
281   wchar_t host[256] = {0};
282   wchar_t path[256] = {0};
283   int port = 0;
284 
285   if (!winhttp.CrackUrl(url,
286                         0,
287                         scheme,
288                         sizeof(scheme)/sizeof(scheme[0]),
289                         host,
290                         sizeof(host)/sizeof(host[0]),
291                         path,
292                         sizeof(path)/sizeof(path[0]),
293                         &port)) {
294     return NULL;
295   }
296 
297   if (_wcsicmp(scheme, L"https") == 0) {
298     // Winhttp under WINE doesn't support wildcard certificates, so avoid
299     // to use it if the scheme is https. The caller should fall back to
300     // use wininet if NULL is returned.
301     return NULL;
302   }
303 
304   return new internal::WinHttpClient();
305 }
306 
307 }  // namespace crash
308