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