/ src / common / linux / http_upload.cc
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