1 | // Copyright (c) 2006, Google Inc. |
2 | // All rights reserved. |
3 | // |
4 | // Redistribution and use in source and binary forms, with or without |
5 | // modification, are permitted provided that the following conditions are |
6 | // met: |
7 | // |
8 | // * Redistributions of source code must retain the above copyright |
9 | // notice, this list of conditions and the following disclaimer. |
10 | // * Redistributions in binary form must reproduce the above |
11 | // copyright notice, this list of conditions and the following disclaimer |
12 | // in the documentation and/or other materials provided with the |
13 | // distribution. |
14 | // * Neither the name of Google Inc. nor the names of its |
15 | // contributors may be used to endorse or promote products derived from |
16 | // this software without specific prior written permission. |
17 | // |
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | |
30 | #include "common/linux/http_upload.h" |
31 | |
32 | #include <assert.h> |
33 | #include <dlfcn.h> |
34 | #include "third_party/curl/curl.h" |
35 | |
36 | namespace { |
37 | |
38 | // Callback to get the response data from server. |
39 | static size_t WriteCallback(void* ptr, size_t size, |
40 | size_t nmemb, void* userp) { |
41 | if (!userp) |
42 | return 0; |
43 | |
44 | string* response = reinterpret_cast<string*>(userp); |
45 | size_t real_size = size * nmemb; |
46 | response->append(reinterpret_cast<char*>(ptr), real_size); |
47 | return real_size; |
48 | } |
49 | |
50 | } // namespace |
51 | |
52 | namespace google_breakpad { |
53 | |
54 | static const char kUserAgent[] = "Breakpad/1.0 (Linux)" ; |
55 | |
56 | // static |
57 | bool HTTPUpload::SendRequest(const string& url, |
58 | const map<string, string>& parameters, |
59 | const map<string, string>& files, |
60 | const string& proxy, |
61 | const string& proxy_user_pwd, |
62 | const string& ca_certificate_file, |
63 | string* response_body, |
64 | long* response_code, |
65 | string* error_description) { |
66 | if (response_code != NULL) |
67 | *response_code = 0; |
68 | |
69 | if (!CheckParameters(parameters)) |
70 | return false; |
71 | |
72 | // We may have been linked statically; if curl_easy_init is in the |
73 | // current binary, no need to search for a dynamic version. |
74 | void* curl_lib = dlopen(NULL, RTLD_NOW); |
75 | if (!CheckCurlLib(curl_lib)) { |
76 | fprintf(stderr, |
77 | "Failed to open curl lib from binary, use libcurl.so instead\n" ); |
78 | dlerror(); // Clear dlerror before attempting to open libraries. |
79 | dlclose(curl_lib); |
80 | curl_lib = NULL; |
81 | } |
82 | if (!curl_lib) { |
83 | curl_lib = dlopen("libcurl.so" , RTLD_NOW); |
84 | } |
85 | if (!curl_lib) { |
86 | if (error_description != NULL) |
87 | *error_description = dlerror(); |
88 | curl_lib = dlopen("libcurl.so.4" , RTLD_NOW); |
89 | } |
90 | if (!curl_lib) { |
91 | // Debian gives libcurl a different name when it is built against GnuTLS |
92 | // instead of OpenSSL. |
93 | curl_lib = dlopen("libcurl-gnutls.so.4" , RTLD_NOW); |
94 | } |
95 | if (!curl_lib) { |
96 | curl_lib = dlopen("libcurl.so.3" , RTLD_NOW); |
97 | } |
98 | if (!curl_lib) { |
99 | return false; |
100 | } |
101 | |
102 | CURL* (*curl_easy_init)(void); |
103 | *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init" ); |
104 | CURL* curl = (*curl_easy_init)(); |
105 | if (error_description != NULL) |
106 | *error_description = "No Error" ; |
107 | |
108 | if (!curl) { |
109 | dlclose(curl_lib); |
110 | return false; |
111 | } |
112 | |
113 | CURLcode err_code = CURLE_OK; |
114 | CURLcode (*curl_easy_setopt)(CURL*, CURLoption, ...); |
115 | *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt" ); |
116 | (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str()); |
117 | (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent); |
118 | // Support multithread by disabling timeout handling, would get SIGSEGV with |
119 | // Curl_resolv_timeout in stack trace otherwise. |
120 | // See https://curl.haxx.se/libcurl/c/threadsafe.html |
121 | (*curl_easy_setopt)(curl, CURLOPT_NOSIGNAL, 1); |
122 | // Set proxy information if necessary. |
123 | if (!proxy.empty()) |
124 | (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str()); |
125 | if (!proxy_user_pwd.empty()) |
126 | (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str()); |
127 | |
128 | if (!ca_certificate_file.empty()) |
129 | (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str()); |
130 | |
131 | struct curl_httppost* formpost = NULL; |
132 | struct curl_httppost* lastptr = NULL; |
133 | // Add form data. |
134 | CURLFORMcode (*curl_formadd)(struct curl_httppost**, struct curl_httppost**, ...); |
135 | *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd" ); |
136 | map<string, string>::const_iterator iter = parameters.begin(); |
137 | for (; iter != parameters.end(); ++iter) |
138 | (*curl_formadd)(&formpost, &lastptr, |
139 | CURLFORM_COPYNAME, iter->first.c_str(), |
140 | CURLFORM_COPYCONTENTS, iter->second.c_str(), |
141 | CURLFORM_END); |
142 | |
143 | // Add form files. |
144 | for (iter = files.begin(); iter != files.end(); ++iter) { |
145 | (*curl_formadd)(&formpost, &lastptr, |
146 | CURLFORM_COPYNAME, iter->first.c_str(), |
147 | CURLFORM_FILE, iter->second.c_str(), |
148 | CURLFORM_END); |
149 | } |
150 | |
151 | (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost); |
152 | |
153 | // Disable 100-continue header. |
154 | struct curl_slist* = NULL; |
155 | char buf[] = "Expect:" ; |
156 | struct curl_slist* (*curl_slist_append)(struct curl_slist*, const char*); |
157 | *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append" ); |
158 | headerlist = (*curl_slist_append)(headerlist, buf); |
159 | (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist); |
160 | |
161 | if (response_body != NULL) { |
162 | (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback); |
163 | (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA, |
164 | reinterpret_cast<void*>(response_body)); |
165 | } |
166 | |
167 | // Fail if 400+ is returned from the web server. |
168 | (*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1); |
169 | |
170 | CURLcode (*curl_easy_perform)(CURL*); |
171 | *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform" ); |
172 | err_code = (*curl_easy_perform)(curl); |
173 | if (response_code != NULL) { |
174 | CURLcode (*curl_easy_getinfo)(CURL*, CURLINFO, ...); |
175 | *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo" ); |
176 | (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code); |
177 | } |
178 | const char* (*curl_easy_strerror)(CURLcode); |
179 | *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror" ); |
180 | #ifndef NDEBUG |
181 | if (err_code != CURLE_OK) |
182 | fprintf(stderr, "Failed to send http request to %s, error: %s\n" , |
183 | url.c_str(), |
184 | (*curl_easy_strerror)(err_code)); |
185 | #endif |
186 | if (error_description != NULL) |
187 | *error_description = (*curl_easy_strerror)(err_code); |
188 | |
189 | void (*curl_easy_cleanup)(CURL*); |
190 | *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup" ); |
191 | (*curl_easy_cleanup)(curl); |
192 | if (formpost != NULL) { |
193 | void (*curl_formfree)(struct curl_httppost*); |
194 | *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree" ); |
195 | (*curl_formfree)(formpost); |
196 | } |
197 | if (headerlist != NULL) { |
198 | void (*curl_slist_free_all)(struct curl_slist*); |
199 | *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all" ); |
200 | (*curl_slist_free_all)(headerlist); |
201 | } |
202 | dlclose(curl_lib); |
203 | return err_code == CURLE_OK; |
204 | } |
205 | |
206 | // static |
207 | bool HTTPUpload::CheckCurlLib(void* curl_lib) { |
208 | return curl_lib && |
209 | dlsym(curl_lib, "curl_easy_init" ) && |
210 | dlsym(curl_lib, "curl_easy_setopt" ); |
211 | } |
212 | |
213 | // static |
214 | bool HTTPUpload::CheckParameters(const map<string, string>& parameters) { |
215 | for (map<string, string>::const_iterator pos = parameters.begin(); |
216 | pos != parameters.end(); ++pos) { |
217 | const string& str = pos->first; |
218 | if (str.size() == 0) |
219 | return false; // disallow empty parameter names |
220 | for (unsigned int i = 0; i < str.size(); ++i) { |
221 | int c = str[i]; |
222 | if (c < 32 || c == '"' || c > 127) { |
223 | return false; |
224 | } |
225 | } |
226 | } |
227 | return true; |
228 | } |
229 | |
230 | } // namespace google_breakpad |
231 | |