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 <assert.h>
34 #include <stdio.h>
35 #include <Windows.h>
36 #include <WinInet.h>
37
38 #include <vector>
39
40 #include "tools/windows/converter_exe/http_download.h"
41 #include "tools/windows/converter_exe/winhttp_client.h"
42 #include "tools/windows/converter_exe/wininet_client.h"
43
44 namespace crash {
45 static const std::vector<char>::size_type kVectorChunkSize = 4096; // 4 KB
46
47 using std::vector;
48
49 // Class that atuo closes the contained HttpHandle when the object
50 // goes out of scope.
51 class AutoHttpHandle {
52 public:
AutoHttpHandle()53 AutoHttpHandle() : handle_(NULL) {}
AutoHttpHandle(HttpHandle handle)54 explicit AutoHttpHandle(HttpHandle handle) : handle_(handle) {}
~AutoHttpHandle()55 ~AutoHttpHandle() {
56 if (handle_) {
57 InternetCloseHandle(handle_);
58 }
59 }
60
get()61 HttpHandle get() { return handle_; }
get_handle_addr()62 HttpHandle* get_handle_addr () { return &handle_; }
63
64 private:
65 HttpHandle handle_;
66 };
67
68 // Template class for auto releasing the contained pointer when
69 // the object goes out of scope.
70 template<typename T>
71 class AutoPtr {
72 public:
AutoPtr(T * ptr)73 explicit AutoPtr(T* ptr) : ptr_(ptr) {}
~AutoPtr()74 ~AutoPtr() {
75 if (ptr_) {
76 delete ptr_;
77 }
78 }
79
get()80 T* get() { return ptr_; }
operator ->()81 T* operator -> () { return ptr_; }
82
83 private:
84 T* ptr_;
85 };
86
87 // CheckParameters ensures that the parameters in |parameters| are safe for
88 // use in an HTTP URL. Returns true if they are, false if unsafe characters
89 // are present.
CheckParameters(const map<wstring,wstring> * parameters)90 static bool CheckParameters(const map<wstring, wstring>* parameters) {
91 for (map<wstring, wstring>::const_iterator iterator = parameters->begin();
92 iterator != parameters->end();
93 ++iterator) {
94 const wstring& key = iterator->first;
95 if (key.empty()) {
96 // Disallow empty parameter names.
97 return false;
98 }
99 for (unsigned int i = 0; i < key.size(); ++i) {
100 wchar_t c = key[i];
101 if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {
102 return false;
103 }
104 }
105
106 const wstring& value = iterator->second;
107 for (unsigned int i = 0; i < value.size(); ++i) {
108 wchar_t c = value[i];
109 if (c < 32 || c == '"' || c == '?' || c == '&' || c > 127) {
110 return false;
111 }
112 }
113 }
114
115 return true;
116 }
117
CreateHttpClient(const wchar_t * url)118 HttpClient* HTTPDownload::CreateHttpClient(const wchar_t* url) {
119 const TCHAR* kHttpApiPolicyEnvironmentVariable = TEXT("USE_WINHTTP");
120 TCHAR buffer[2] = {0};
121 HttpClient* http_client = NULL;
122
123 if (::GetEnvironmentVariable(kHttpApiPolicyEnvironmentVariable,
124 buffer,
125 sizeof(buffer)/sizeof(buffer[0])) > 0) {
126 fprintf(stdout,
127 "Environment variable [%ws] is set, use WinHttp\n",
128 kHttpApiPolicyEnvironmentVariable);
129 http_client = CreateWinHttpClient(url);
130 if (http_client == NULL) {
131 fprintf(stderr, "WinHttpClient not created, Is the protocol HTTPS? "
132 "Fall back to WinInet API.\n");
133 }
134 } else {
135 fprintf(stderr,
136 "Environment variable [%ws] is NOT set, use WinInet API\n",
137 kHttpApiPolicyEnvironmentVariable);
138 }
139
140 if (http_client == NULL) {
141 return CreateWinInetClient(url);
142 }
143
144 return http_client;
145 }
146
147 // static
Download(const wstring & url,const map<wstring,wstring> * parameters,string * content,int * status_code)148 bool HTTPDownload::Download(const wstring& url,
149 const map<wstring, wstring>* parameters,
150 string *content, int *status_code) {
151 assert(content);
152 AutoPtr<HttpClient> http_client(CreateHttpClient(url.c_str()));
153
154 if (!http_client.get()) {
155 fprintf(stderr, "Failed to create any http client.\n");
156 return false;
157 }
158
159 if (status_code) {
160 *status_code = 0;
161 }
162
163 wchar_t scheme[16] = {0};
164 wchar_t host[256] = {0};
165 wchar_t path[256] = {0};
166 int port = 0;
167 if (!http_client->CrackUrl(url.c_str(),
168 0,
169 scheme,
170 sizeof(scheme)/sizeof(scheme[0]),
171 host,
172 sizeof(host)/sizeof(host[0]),
173 path,
174 sizeof(path)/sizeof(path[0]),
175 &port)) {
176 fprintf(stderr,
177 "HTTPDownload::Download: InternetCrackUrl: error %lu for %ws\n",
178 GetLastError(), url.c_str());
179 return false;
180 }
181
182 bool secure = false;
183 if (_wcsicmp(scheme, L"https") == 0) {
184 secure = true;
185 } else if (wcscmp(scheme, L"http") != 0) {
186 fprintf(stderr,
187 "HTTPDownload::Download: scheme must be http or https for %ws\n",
188 url.c_str());
189 return false;
190 }
191
192 AutoHttpHandle internet;
193 if (!http_client->Open(NULL, // user agent
194 HttpClient::ACCESS_TYPE_PRECONFIG,
195 NULL, // proxy name
196 NULL, // proxy bypass
197 internet.get_handle_addr())) {
198 fprintf(stderr,
199 "HTTPDownload::Download: Open: error %lu for %ws\n",
200 GetLastError(), url.c_str());
201 return false;
202 }
203
204 AutoHttpHandle connection;
205 if (!http_client->Connect(internet.get(),
206 host,
207 port,
208 connection.get_handle_addr())) {
209 fprintf(stderr,
210 "HTTPDownload::Download: InternetConnect: error %lu for %ws\n",
211 GetLastError(), url.c_str());
212 return false;
213 }
214
215 wstring request_string = path;
216 if (parameters) {
217 // TODO(mmentovai): escape bad characters in parameters instead of
218 // forbidding them.
219 if (!CheckParameters(parameters)) {
220 fprintf(stderr,
221 "HTTPDownload::Download: invalid characters in parameters\n");
222 return false;
223 }
224
225 bool added_parameter = false;
226 for (map<wstring, wstring>::const_iterator iterator = parameters->begin();
227 iterator != parameters->end();
228 ++iterator) {
229 request_string.append(added_parameter ? L"&" : L"?");
230 request_string.append(iterator->first);
231 request_string.append(L"=");
232 request_string.append(iterator->second);
233 added_parameter = true;
234 }
235 }
236
237 AutoHttpHandle request;
238 if (!http_client->OpenRequest(connection.get(),
239 L"GET",
240 request_string.c_str(),
241 NULL, // version
242 NULL, // referer
243 secure,
244 request.get_handle_addr())) {
245 fprintf(stderr,
246 "HttpClient::OpenRequest: error %lu for %ws, request: %ws\n",
247 GetLastError(), url.c_str(), request_string.c_str());
248 return false;
249 }
250
251 if (!http_client->SendRequest(request.get(), NULL, 0)) {
252 fprintf(stderr,
253 "HttpClient::SendRequest: error %lu for %ws\n",
254 GetLastError(), url.c_str());
255 return false;
256 }
257
258 if (!http_client->ReceiveResponse(request.get())) {
259 fprintf(stderr,
260 "HttpClient::ReceiveResponse: error %lu for %ws\n",
261 GetLastError(), url.c_str());
262 return false;
263 }
264
265 int http_status = 0;
266 if (!http_client->GetHttpStatusCode(request.get(), &http_status)) {
267 fprintf(stderr,
268 "HttpClient::GetHttpStatusCode: error %lu for %ws\n",
269 GetLastError(), url.c_str());
270 return false;
271 }
272 if (http_status != 200) {
273 fprintf(stderr,
274 "HTTPDownload::Download: HTTP status code %d for %ws\n",
275 http_status, url.c_str());
276 return false;
277 }
278
279 DWORD content_length = 0;
280 vector<char>::size_type buffer_size = 0;
281 http_client->GetContentLength(request.get(), &content_length);
282 if (content_length == HttpClient::kUnknownContentLength) {
283 buffer_size = kVectorChunkSize;
284 } else {
285 buffer_size = content_length;
286 }
287
288 if (content_length != 0) {
289 vector<char> response_buffer = vector<char>(buffer_size+1);
290 DWORD size_read;
291 DWORD total_read = 0;
292 bool read_result;
293 do {
294 if (content_length == HttpClient::kUnknownContentLength
295 && buffer_size == total_read) {
296 // The content length wasn't specified in the response header, so we
297 // have to keep growing the buffer until we're done reading.
298 buffer_size += kVectorChunkSize;
299 response_buffer.resize(buffer_size);
300 }
301 read_result = !!http_client->ReadData(
302 request.get(),
303 &response_buffer[total_read],
304 static_cast<DWORD>(buffer_size) - total_read,
305 &size_read);
306 total_read += size_read;
307 } while (read_result && (size_read != 0));
308
309 if (!read_result) {
310 fprintf(stderr,
311 "HttpClient::ReadData: error %lu for %ws\n",
312 GetLastError(),
313 url.c_str());
314 return false;
315 } else if (size_read != 0) {
316 fprintf(stderr,
317 "HttpClient::ReadData: error %lu/%lu for %ws\n",
318 total_read,
319 content_length,
320 url.c_str());
321 return false;
322 }
323 content->assign(&response_buffer[0], total_read);
324 } else {
325 content->clear();
326 }
327 return true;
328 }
329
330 } // namespace crash
331