xref: /aosp_15_r20/external/google-breakpad/src/common/linux/libcurl_wrapper.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2009 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 <dlfcn.h>
34 
35 #include <iostream>
36 #include <string>
37 
38 #include "common/linux/libcurl_wrapper.h"
39 #include "common/using_std_string.h"
40 
41 namespace google_breakpad {
LibcurlWrapper()42 LibcurlWrapper::LibcurlWrapper()
43     : init_ok_(false),
44       curl_lib_(nullptr),
45       last_curl_error_(""),
46       curl_(nullptr),
47       formpost_(nullptr),
48       lastptr_(nullptr),
49       headerlist_(nullptr) {}
50 
~LibcurlWrapper()51 LibcurlWrapper::~LibcurlWrapper() {
52   if (init_ok_) {
53     (*easy_cleanup_)(curl_);
54     (*global_cleanup_)();
55     dlclose(curl_lib_);
56   }
57 }
58 
SetProxy(const string & proxy_host,const string & proxy_userpwd)59 bool LibcurlWrapper::SetProxy(const string& proxy_host,
60                               const string& proxy_userpwd) {
61   if (!CheckInit()) return false;
62 
63   // Set proxy information if necessary.
64   if (!proxy_host.empty()) {
65     (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str());
66   } else {
67     std::cout << "SetProxy called with empty proxy host.";
68     return false;
69   }
70   if (!proxy_userpwd.empty()) {
71     (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str());
72   } else {
73     std::cout << "SetProxy called with empty proxy username/password.";
74     return false;
75   }
76   std::cout << "Set proxy host to " << proxy_host;
77   return true;
78 }
79 
AddFile(const string & upload_file_path,const string & basename)80 bool LibcurlWrapper::AddFile(const string& upload_file_path,
81                              const string& basename) {
82   if (!CheckInit()) return false;
83 
84   std::cout << "Adding " << upload_file_path << " to form upload.";
85   // Add form file.
86   (*formadd_)(&formpost_, &lastptr_,
87               CURLFORM_COPYNAME, basename.c_str(),
88               CURLFORM_FILE, upload_file_path.c_str(),
89               CURLFORM_END);
90 
91   return true;
92 }
93 
94 // Callback to get the response data from server.
WriteCallback(void * ptr,size_t size,size_t nmemb,void * userp)95 static size_t WriteCallback(void* ptr, size_t size,
96                             size_t nmemb, void* userp) {
97   if (!userp)
98     return 0;
99 
100   string* response = reinterpret_cast<string*>(userp);
101   size_t real_size = size * nmemb;
102   response->append(reinterpret_cast<char*>(ptr), real_size);
103   return real_size;
104 }
105 
SendRequest(const string & url,const std::map<string,string> & parameters,long * http_status_code,string * http_header_data,string * http_response_data)106 bool LibcurlWrapper::SendRequest(const string& url,
107                                  const std::map<string, string>& parameters,
108                                  long* http_status_code,
109                                  string* http_header_data,
110                                  string* http_response_data) {
111   if (!CheckInit()) return false;
112 
113   std::map<string, string>::const_iterator iter = parameters.begin();
114   for (; iter != parameters.end(); ++iter)
115     (*formadd_)(&formpost_, &lastptr_,
116                 CURLFORM_COPYNAME, iter->first.c_str(),
117                 CURLFORM_COPYCONTENTS, iter->second.c_str(),
118                 CURLFORM_END);
119 
120   (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_);
121 
122   return SendRequestInner(url, http_status_code, http_header_data,
123                           http_response_data);
124 }
125 
SendGetRequest(const string & url,long * http_status_code,string * http_header_data,string * http_response_data)126 bool LibcurlWrapper::SendGetRequest(const string& url,
127                                     long* http_status_code,
128                                     string* http_header_data,
129                                     string* http_response_data) {
130   if (!CheckInit()) return false;
131 
132   (*easy_setopt_)(curl_, CURLOPT_HTTPGET, 1L);
133 
134   return SendRequestInner(url, http_status_code, http_header_data,
135                           http_response_data);
136 }
137 
SendPutRequest(const string & url,const string & path,long * http_status_code,string * http_header_data,string * http_response_data)138 bool LibcurlWrapper::SendPutRequest(const string& url,
139                                     const string& path,
140                                     long* http_status_code,
141                                     string* http_header_data,
142                                     string* http_response_data) {
143   if (!CheckInit()) return false;
144 
145   FILE* file = fopen(path.c_str(), "rb");
146   (*easy_setopt_)(curl_, CURLOPT_UPLOAD, 1L);
147   (*easy_setopt_)(curl_, CURLOPT_PUT, 1L);
148   (*easy_setopt_)(curl_, CURLOPT_READDATA, file);
149 
150   bool success = SendRequestInner(url, http_status_code, http_header_data,
151                                   http_response_data);
152 
153   fclose(file);
154   return success;
155 }
156 
SendSimplePostRequest(const string & url,const string & body,const string & content_type,long * http_status_code,string * http_header_data,string * http_response_data)157 bool LibcurlWrapper::SendSimplePostRequest(const string& url,
158                                            const string& body,
159                                            const string& content_type,
160                                            long* http_status_code,
161                                            string* http_header_data,
162                                            string* http_response_data) {
163   if (!CheckInit()) return false;
164 
165   (*easy_setopt_)(curl_, CURLOPT_POSTFIELDSIZE, body.size());
166   (*easy_setopt_)(curl_, CURLOPT_COPYPOSTFIELDS, body.c_str());
167 
168   if (!content_type.empty()) {
169     string content_type_header = "Content-Type: " + content_type;
170     headerlist_ = (*slist_append_)(
171         headerlist_,
172         content_type_header.c_str());
173   }
174 
175   return SendRequestInner(url, http_status_code, http_header_data,
176                           http_response_data);
177 }
178 
Init()179 bool LibcurlWrapper::Init() {
180   // First check to see if libcurl was statically linked:
181   curl_lib_ = dlopen(nullptr, RTLD_NOW);
182   if (curl_lib_ &&
183       (!dlsym(curl_lib_, "curl_easy_init") ||
184       !dlsym(curl_lib_, "curl_easy_setopt"))) {
185     // Not statically linked, try again below.
186     dlerror();  // Clear dlerror before attempting to open libraries.
187     dlclose(curl_lib_);
188     curl_lib_ = nullptr;
189   }
190   if (!curl_lib_) {
191     curl_lib_ = dlopen("libcurl.so", RTLD_NOW);
192   }
193   if (!curl_lib_) {
194     curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW);
195   }
196   if (!curl_lib_) {
197     curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW);
198   }
199   if (!curl_lib_) {
200     std::cout << "Could not find libcurl via dlopen";
201     return false;
202   }
203 
204   if (!SetFunctionPointers()) {
205     std::cout << "Could not find function pointers";
206     return false;
207   }
208 
209   curl_ = (*easy_init_)();
210 
211   last_curl_error_ = "No Error";
212 
213   if (!curl_) {
214     dlclose(curl_lib_);
215     std::cout << "Curl initialization failed";
216     return false;
217   }
218 
219   init_ok_ = true;
220   return true;
221 }
222 
223 #define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \
224   var = reinterpret_cast<type>(dlsym(curl_lib_, function_name)); \
225   if (!var) { \
226     std::cout << "Could not find libcurl function " << function_name; \
227     init_ok_ = false; \
228     return false; \
229   }
230 
SetFunctionPointers()231 bool LibcurlWrapper::SetFunctionPointers() {
232 
233   SET_AND_CHECK_FUNCTION_POINTER(easy_init_,
234                                  "curl_easy_init",
235                                  CURL*(*)());
236 
237   SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_,
238                                  "curl_easy_setopt",
239                                  CURLcode(*)(CURL*, CURLoption, ...));
240 
241   SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd",
242       CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...));
243 
244   SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append",
245       curl_slist*(*)(curl_slist*, const char*));
246 
247   SET_AND_CHECK_FUNCTION_POINTER(easy_perform_,
248                                  "curl_easy_perform",
249                                  CURLcode(*)(CURL*));
250 
251   SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_,
252                                  "curl_easy_cleanup",
253                                  void(*)(CURL*));
254 
255   SET_AND_CHECK_FUNCTION_POINTER(easy_getinfo_,
256                                  "curl_easy_getinfo",
257                                  CURLcode(*)(CURL*, CURLINFO info, ...));
258 
259   SET_AND_CHECK_FUNCTION_POINTER(easy_reset_,
260                                  "curl_easy_reset",
261                                  void(*)(CURL*));
262 
263   SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
264                                  "curl_slist_free_all",
265                                  void(*)(curl_slist*));
266 
267   SET_AND_CHECK_FUNCTION_POINTER(formfree_,
268                                  "curl_formfree",
269                                  void(*)(curl_httppost*));
270 
271   SET_AND_CHECK_FUNCTION_POINTER(global_cleanup_,
272                                  "curl_global_cleanup",
273                                  void(*)(void));
274   return true;
275 }
276 
SendRequestInner(const string & url,long * http_status_code,string * http_header_data,string * http_response_data)277 bool LibcurlWrapper::SendRequestInner(const string& url,
278                                       long* http_status_code,
279                                       string* http_header_data,
280                                       string* http_response_data) {
281   string url_copy(url);
282   (*easy_setopt_)(curl_, CURLOPT_URL, url_copy.c_str());
283 
284   // Disable 100-continue header.
285   char buf[] = "Expect:";
286   headerlist_ = (*slist_append_)(headerlist_, buf);
287   (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_);
288 
289   if (http_response_data != nullptr) {
290     http_response_data->clear();
291     (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
292     (*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
293                     reinterpret_cast<void*>(http_response_data));
294   }
295   if (http_header_data != nullptr) {
296     http_header_data->clear();
297     (*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback);
298     (*easy_setopt_)(curl_, CURLOPT_HEADERDATA,
299                     reinterpret_cast<void*>(http_header_data));
300   }
301   CURLcode err_code = CURLE_OK;
302   err_code = (*easy_perform_)(curl_);
303   easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
304       (dlsym(curl_lib_, "curl_easy_strerror"));
305 
306   if (http_status_code != nullptr) {
307     (*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code);
308   }
309 
310   if (err_code != CURLE_OK)
311     fprintf(stderr, "Failed to send http request to %s, error: %s\n",
312             url.c_str(),
313             (*easy_strerror_)(err_code));
314 
315   Reset();
316 
317   return err_code == CURLE_OK;
318 }
319 
Reset()320 void LibcurlWrapper::Reset() {
321   if (headerlist_ != nullptr) {
322     (*slist_free_all_)(headerlist_);
323     headerlist_ = nullptr;
324   }
325 
326   if (formpost_ != nullptr) {
327     (*formfree_)(formpost_);
328     formpost_ = nullptr;
329   }
330 
331   (*easy_reset_)(curl_);
332 }
333 
CheckInit()334 bool LibcurlWrapper::CheckInit() {
335   if (!init_ok_) {
336     std::cout << "LibcurlWrapper: You must call Init(), and have it return "
337                  "'true' before invoking any other methods.\n";
338     return false;
339   }
340 
341   return true;
342 }
343 
344 }  // namespace google_breakpad
345