http_upload.cc
1 // Copyright 2006 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 "common/linux/http_upload.h" 34 35 #include <assert.h> 36 #include <dlfcn.h> 37 #include "third_party/curl/curl.h" 38 39 namespace { 40 41 // Callback to get the response data from server. 42 static size_t WriteCallback(void* ptr, size_t size, 43 size_t nmemb, void* userp) { 44 if (!userp) 45 return 0; 46 47 string* response = reinterpret_cast<string*>(userp); 48 size_t real_size = size * nmemb; 49 response->append(reinterpret_cast<char*>(ptr), real_size); 50 return real_size; 51 } 52 53 } // namespace 54 55 namespace google_breakpad { 56 57 static const char kUserAgent[] = "Breakpad/1.0 (Linux)"; 58 59 // static 60 bool HTTPUpload::SendRequest(const string& url, 61 const map<string, string>& parameters, 62 const map<string, string>& files, 63 const string& proxy, 64 const string& proxy_user_pwd, 65 const string& ca_certificate_file, 66 string* response_body, 67 long* response_code, 68 string* error_description) { 69 if (response_code != NULL) 70 *response_code = 0; 71 72 if (!CheckParameters(parameters)) 73 return false; 74 75 // We may have been linked statically; if curl_easy_init is in the 76 // current binary, no need to search for a dynamic version. 77 void* curl_lib = dlopen(NULL, RTLD_NOW); 78 if (!CheckCurlLib(curl_lib)) { 79 fprintf(stderr, 80 "Failed to open curl lib from binary, use libcurl.so instead\n"); 81 dlerror(); // Clear dlerror before attempting to open libraries. 82 dlclose(curl_lib); 83 curl_lib = NULL; 84 } 85 if (!curl_lib) { 86 curl_lib = dlopen("libcurl.so", RTLD_NOW); 87 } 88 if (!curl_lib) { 89 if (error_description != NULL) 90 *error_description = dlerror(); 91 curl_lib = dlopen("libcurl.so.4", RTLD_NOW); 92 } 93 if (!curl_lib) { 94 // Debian gives libcurl a different name when it is built against GnuTLS 95 // instead of OpenSSL. 96 curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW); 97 } 98 if (!curl_lib) { 99 curl_lib = dlopen("libcurl.so.3", RTLD_NOW); 100 } 101 if (!curl_lib) { 102 return false; 103 } 104 105 CURL* (*curl_easy_init)(void); 106 *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init"); 107 CURL* curl = (*curl_easy_init)(); 108 if (error_description != NULL) 109 *error_description = "No Error"; 110 111 if (!curl) { 112 dlclose(curl_lib); 113 return false; 114 } 115 116 CURLcode err_code = CURLE_OK; 117 CURLcode (*curl_easy_setopt)(CURL*, CURLoption, ...); 118 *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt"); 119 (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str()); 120 (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent); 121 // Support multithread by disabling timeout handling, would get SIGSEGV with 122 // Curl_resolv_timeout in stack trace otherwise. 123 // See https://curl.haxx.se/libcurl/c/threadsafe.html 124 (*curl_easy_setopt)(curl, CURLOPT_NOSIGNAL, 1); 125 // Set proxy information if necessary. 126 if (!proxy.empty()) 127 (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str()); 128 if (!proxy_user_pwd.empty()) 129 (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str()); 130 131 if (!ca_certificate_file.empty()) 132 (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str()); 133 134 struct curl_httppost* formpost = NULL; 135 struct curl_httppost* lastptr = NULL; 136 // Add form data. 137 CURLFORMcode (*curl_formadd)(struct curl_httppost**, struct curl_httppost**, ...); 138 *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd"); 139 map<string, string>::const_iterator iter = parameters.begin(); 140 for (; iter != parameters.end(); ++iter) 141 (*curl_formadd)(&formpost, &lastptr, 142 CURLFORM_COPYNAME, iter->first.c_str(), 143 CURLFORM_COPYCONTENTS, iter->second.c_str(), 144 CURLFORM_END); 145 146 // Add form files. 147 for (iter = files.begin(); iter != files.end(); ++iter) { 148 (*curl_formadd)(&formpost, &lastptr, 149 CURLFORM_COPYNAME, iter->first.c_str(), 150 CURLFORM_FILE, iter->second.c_str(), 151 CURLFORM_END); 152 } 153 154 (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost); 155 156 // Disable 100-continue header. 157 struct curl_slist* headerlist = NULL; 158 char buf[] = "Expect:"; 159 struct curl_slist* (*curl_slist_append)(struct curl_slist*, const char*); 160 *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append"); 161 headerlist = (*curl_slist_append)(headerlist, buf); 162 (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist); 163 164 if (response_body != NULL) { 165 (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback); 166 (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA, 167 reinterpret_cast<void*>(response_body)); 168 } 169 170 // Fail if 400+ is returned from the web server. 171 (*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1); 172 173 CURLcode (*curl_easy_perform)(CURL*); 174 *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform"); 175 err_code = (*curl_easy_perform)(curl); 176 if (response_code != NULL) { 177 CURLcode (*curl_easy_getinfo)(CURL*, CURLINFO, ...); 178 *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo"); 179 (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code); 180 } 181 const char* (*curl_easy_strerror)(CURLcode); 182 *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror"); 183 #ifndef NDEBUG 184 if (err_code != CURLE_OK) 185 fprintf(stderr, "Failed to send http request to %s, error: %s\n", 186 url.c_str(), 187 (*curl_easy_strerror)(err_code)); 188 #endif 189 if (error_description != NULL) 190 *error_description = (*curl_easy_strerror)(err_code); 191 192 void (*curl_easy_cleanup)(CURL*); 193 *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup"); 194 (*curl_easy_cleanup)(curl); 195 if (formpost != NULL) { 196 void (*curl_formfree)(struct curl_httppost*); 197 *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree"); 198 (*curl_formfree)(formpost); 199 } 200 if (headerlist != NULL) { 201 void (*curl_slist_free_all)(struct curl_slist*); 202 *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all"); 203 (*curl_slist_free_all)(headerlist); 204 } 205 dlclose(curl_lib); 206 return err_code == CURLE_OK; 207 } 208 209 // static 210 bool HTTPUpload::CheckCurlLib(void* curl_lib) { 211 return curl_lib && 212 dlsym(curl_lib, "curl_easy_init") && 213 dlsym(curl_lib, "curl_easy_setopt"); 214 } 215 216 // static 217 bool HTTPUpload::CheckParameters(const map<string, string>& parameters) { 218 for (map<string, string>::const_iterator pos = parameters.begin(); 219 pos != parameters.end(); ++pos) { 220 const string& str = pos->first; 221 if (str.size() == 0) 222 return false; // disallow empty parameter names 223 for (unsigned int i = 0; i < str.size(); ++i) { 224 int c = str[i]; 225 if (c < 32 || c == '"' || c > 127) { 226 return false; 227 } 228 } 229 } 230 return true; 231 } 232 233 } // namespace google_breakpad