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