• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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