1 // Copyright 2019 Google LLC
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 LLC 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 #ifdef HAVE_CONFIG_H
30 #include <config.h> // Must come first
31 #endif
32
33 #include "tools/windows/converter_exe/winhttp_client.h"
34
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <windows.h>
38 #include <winhttp.h>
39 #include <vector>
40
41 namespace crash {
42
43 namespace internal {
44
45 // This class implements HttpClient based on WinInet APIs.
46 class WinHttpClient : public HttpClient {
47 public:
~WinHttpClient()48 virtual ~WinHttpClient() {}
49 virtual bool CrackUrl(const TCHAR* url,
50 DWORD flags,
51 TCHAR* scheme,
52 size_t scheme_buffer_length,
53 TCHAR* host,
54 size_t host_buffer_length,
55 TCHAR* uri,
56 size_t uri_buffer_length,
57 int* port) const;
58 virtual bool Open(const TCHAR* user_agent,
59 DWORD access_type,
60 const TCHAR* proxy_name,
61 const TCHAR* proxy_bypass,
62 HttpHandle* session_handle) const;
63 virtual bool Connect(HttpHandle session_handle,
64 const TCHAR* server,
65 int port,
66 HttpHandle* connection_handle) const;
67 virtual bool OpenRequest(HttpHandle connection_handle,
68 const TCHAR* verb,
69 const TCHAR* uri,
70 const TCHAR* version,
71 const TCHAR* referrer,
72 bool is_secure,
73 HttpHandle* request_handle) const;
74 virtual bool SendRequest(HttpHandle request_handle,
75 const TCHAR* headers,
76 DWORD headers_length) const;
77 virtual bool ReceiveResponse(HttpHandle request_handle) const;
78 virtual bool GetHttpStatusCode(HttpHandle request_handle,
79 int* status_code) const;
80 virtual bool GetContentLength(HttpHandle request_handle,
81 DWORD* content_length) const;
82 virtual bool ReadData(HttpHandle request_handle,
83 void* buffer,
84 DWORD buffer_length,
85 DWORD* bytes_read) const;
86 virtual bool Close(HttpHandle handle) const;
87
88 private:
89 static DWORD MapAccessType(DWORD access_type);
90 static HINTERNET ToHINTERNET(HttpHandle handle);
91 static HttpHandle FromHINTERNET(HINTERNET handle);
92 };
93
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) const94 bool WinHttpClient::CrackUrl(const TCHAR* url,
95 DWORD flags,
96 TCHAR* scheme,
97 size_t scheme_buffer_length,
98 TCHAR* host,
99 size_t host_buffer_length,
100 TCHAR* uri,
101 size_t uri_buffer_length,
102 int* port) const {
103 assert(url);
104 assert(scheme);
105 assert(host);
106 assert(uri);
107 assert(port);
108
109 URL_COMPONENTS url_comp = {0};
110 url_comp.dwStructSize = sizeof(url_comp);
111 url_comp.lpszScheme = scheme;
112 url_comp.dwSchemeLength = static_cast<DWORD>(scheme_buffer_length);
113 url_comp.lpszHostName = host;
114 url_comp.dwHostNameLength = static_cast<DWORD>(host_buffer_length);
115 url_comp.lpszUrlPath = uri;
116 url_comp.dwUrlPathLength = static_cast<DWORD>(uri_buffer_length);
117
118 bool result = !!::WinHttpCrackUrl(url, 0, flags, &url_comp);
119 if (result) {
120 *port = static_cast<int>(url_comp.nPort);
121 }
122 return result;
123 }
124
Open(const TCHAR * user_agent,DWORD access_type,const TCHAR * proxy_name,const TCHAR * proxy_bypass,HttpHandle * session_handle) const125 bool WinHttpClient::Open(const TCHAR* user_agent,
126 DWORD access_type,
127 const TCHAR* proxy_name,
128 const TCHAR* proxy_bypass,
129 HttpHandle* session_handle) const {
130 *session_handle = FromHINTERNET(::WinHttpOpen(user_agent,
131 MapAccessType(access_type),
132 proxy_name,
133 proxy_bypass,
134 0));
135
136 return !!(*session_handle);
137 }
138
Connect(HttpHandle session_handle,const TCHAR * server,int port,HttpHandle * connection_handle) const139 bool WinHttpClient::Connect(HttpHandle session_handle,
140 const TCHAR* server,
141 int port,
142 HttpHandle* connection_handle) const {
143 assert(server);
144
145 // Uses NULL user name and password to connect.
146 *connection_handle = FromHINTERNET(::WinHttpConnect(
147 ToHINTERNET(session_handle),
148 server,
149 static_cast<INTERNET_PORT>(port),
150 NULL));
151 return !!(*connection_handle);
152 }
153
OpenRequest(HttpHandle connection_handle,const TCHAR * verb,const TCHAR * uri,const TCHAR * version,const TCHAR * referrer,bool is_secure,HttpHandle * request_handle) const154 bool WinHttpClient::OpenRequest(HttpHandle connection_handle,
155 const TCHAR* verb,
156 const TCHAR* uri,
157 const TCHAR* version,
158 const TCHAR* referrer,
159 bool is_secure,
160 HttpHandle* request_handle) const {
161 assert(connection_handle);
162 assert(verb);
163 assert(uri);
164 assert(request_handle);
165
166 *request_handle = FromHINTERNET(::WinHttpOpenRequest(
167 ToHINTERNET(connection_handle),
168 verb,
169 uri,
170 version,
171 referrer,
172 WINHTTP_DEFAULT_ACCEPT_TYPES,
173 is_secure ? WINHTTP_FLAG_SECURE : 0));
174 return !!(*request_handle);
175 }
176
SendRequest(HttpHandle request_handle,const TCHAR * headers,DWORD headers_length) const177 bool WinHttpClient::SendRequest(HttpHandle request_handle,
178 const TCHAR* headers,
179 DWORD headers_length) const {
180 assert(request_handle);
181
182 return !!::WinHttpSendRequest(ToHINTERNET(request_handle),
183 headers,
184 headers_length,
185 NULL,
186 0,
187 WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
188 NULL);
189 }
190
ReceiveResponse(HttpHandle request_handle) const191 bool WinHttpClient::ReceiveResponse(HttpHandle request_handle) const {
192 assert(request_handle);
193
194 return !!::WinHttpReceiveResponse(ToHINTERNET(request_handle), NULL);
195 }
196
GetHttpStatusCode(HttpHandle request_handle,int * status_code) const197 bool WinHttpClient::GetHttpStatusCode(HttpHandle request_handle,
198 int* status_code) const {
199 TCHAR http_status_string[4] = {0};
200 DWORD http_status_string_size = sizeof(http_status_string);
201 if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),
202 WINHTTP_QUERY_STATUS_CODE,
203 WINHTTP_HEADER_NAME_BY_INDEX,
204 static_cast<void*>(&http_status_string),
205 &http_status_string_size, 0)) {
206 return false;
207 }
208
209 *status_code = static_cast<DWORD>(_tcstol(http_status_string, NULL, 10));
210 return true;
211 }
212
GetContentLength(HttpHandle request_handle,DWORD * content_length) const213 bool WinHttpClient::GetContentLength(HttpHandle request_handle,
214 DWORD* content_length) const {
215 assert(request_handle);
216 assert(content_length);
217
218 TCHAR content_length_string[11] = {0};
219 DWORD content_length_string_size = sizeof(content_length_string);
220 if (!::WinHttpQueryHeaders(ToHINTERNET(request_handle),
221 WINHTTP_QUERY_CONTENT_LENGTH,
222 WINHTTP_HEADER_NAME_BY_INDEX,
223 static_cast<void*>(&content_length_string),
224 &content_length_string_size, 0)) {
225 *content_length = kUnknownContentLength;
226 } else {
227 *content_length =
228 static_cast<DWORD>(wcstol(content_length_string, NULL, 10));
229 }
230 return true;
231 }
232
ReadData(HttpHandle request_handle,void * buffer,DWORD buffer_length,DWORD * bytes_read) const233 bool WinHttpClient::ReadData(HttpHandle request_handle,
234 void* buffer,
235 DWORD buffer_length,
236 DWORD* bytes_read) const {
237 assert(request_handle);
238 assert(buffer);
239 assert(bytes_read);
240
241 DWORD bytes_read_local = 0;
242 if (!::WinHttpReadData(ToHINTERNET(request_handle),
243 buffer,
244 buffer_length,
245 &bytes_read_local)) {
246 return false;
247 }
248 *bytes_read = bytes_read_local;
249 return true;
250 }
251
Close(HttpHandle handle) const252 bool WinHttpClient::Close(HttpHandle handle) const {
253 assert(handle);
254 return !!::WinHttpCloseHandle(ToHINTERNET(handle));
255 }
256
MapAccessType(DWORD access_type)257 DWORD WinHttpClient::MapAccessType(DWORD access_type) {
258 switch (static_cast<AccessType>(access_type)) {
259 case ACCESS_TYPE_PRECONFIG:
260 default:
261 return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
262 case ACCESS_TYPE_DIRECT:
263 return WINHTTP_ACCESS_TYPE_NO_PROXY;
264 case ACCESS_TYPE_PROXY:
265 return WINHTTP_ACCESS_TYPE_NAMED_PROXY;
266 }
267 }
268
269
ToHINTERNET(HttpHandle handle)270 HINTERNET WinHttpClient::ToHINTERNET(HttpHandle handle) {
271 return static_cast<HINTERNET>(handle);
272 }
273
FromHINTERNET(HINTERNET handle)274 HttpHandle WinHttpClient::FromHINTERNET(HINTERNET handle) {
275 return static_cast<HttpHandle>(handle);
276 }
277
278 } // namespace internal
279
CreateWinHttpClient(const TCHAR * url)280 HttpClient* CreateWinHttpClient(const TCHAR* url) {
281 assert(url);
282
283 internal::WinHttpClient winhttp;
284 wchar_t scheme[16] = {0};
285 wchar_t host[256] = {0};
286 wchar_t path[256] = {0};
287 int port = 0;
288
289 if (!winhttp.CrackUrl(url,
290 0,
291 scheme,
292 sizeof(scheme)/sizeof(scheme[0]),
293 host,
294 sizeof(host)/sizeof(host[0]),
295 path,
296 sizeof(path)/sizeof(path[0]),
297 &port)) {
298 return NULL;
299 }
300
301 if (_wcsicmp(scheme, L"https") == 0) {
302 // Winhttp under WINE doesn't support wildcard certificates, so avoid
303 // to use it if the scheme is https. The caller should fall back to
304 // use wininet if NULL is returned.
305 return NULL;
306 }
307
308 return new internal::WinHttpClient();
309 }
310
311 } // namespace crash
312