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