libcurl_wrapper.cc
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 { 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 51 LibcurlWrapper::~LibcurlWrapper() { 52 if (init_ok_) { 53 (*easy_cleanup_)(curl_); 54 (*global_cleanup_)(); 55 dlclose(curl_lib_); 56 } 57 } 58 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 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. 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 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 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 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 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 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 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 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 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 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