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