xref: /aosp_15_r20/external/google-breakpad/src/common/linux/http_upload.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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.
WriteCallback(void * ptr,size_t size,size_t nmemb,void * userp)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
SendRequest(const string & url,const map<string,string> & parameters,const map<string,string> & files,const string & proxy,const string & proxy_user_pwd,const string & ca_certificate_file,string * response_body,long * response_code,string * error_description)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
CheckCurlLib(void * curl_lib)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
CheckParameters(const map<string,string> & parameters)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
234