http_transport_libcurl.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. // Copyright 2017 The Crashpad Authors. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "util/net/http_transport.h"
  15. #include <curl/curl.h>
  16. #include <dlfcn.h>
  17. #include <string.h>
  18. #include <sys/utsname.h>
  19. #include <algorithm>
  20. #include <limits>
  21. #include "base/check.h"
  22. #include "base/logging.h"
  23. #include "base/numerics/safe_math.h"
  24. #include "base/scoped_generic.h"
  25. #include "base/strings/string_number_conversions.h"
  26. #include "base/strings/stringprintf.h"
  27. #include "build/build_config.h"
  28. #include "package.h"
  29. #include "util/misc/no_cfi_icall.h"
  30. #include "util/net/http_body.h"
  31. #include "util/numeric/safe_assignment.h"
  32. namespace crashpad {
  33. namespace {
  34. // Crashpad depends on libcurl via dlopen() in order to maximize its tolerance
  35. // of various libcurl flavors and installation configurations. This class serves
  36. // as a linkage table for libcurl procedures.
  37. class Libcurl {
  38. public:
  39. Libcurl(const Libcurl&) = delete;
  40. Libcurl& operator=(const Libcurl&) = delete;
  41. static bool Initialized() {
  42. static bool initialized = Get()->Initialize();
  43. return initialized;
  44. }
  45. static void CurlEasyCleanup(CURL* curl) {
  46. return Get()->curl_easy_cleanup_(curl);
  47. }
  48. static CURL* CurlEasyInit() { return Get()->curl_easy_init_(); }
  49. static CURLcode CurlEasyPerform(CURL* curl) {
  50. return Get()->curl_easy_perform_(curl);
  51. }
  52. static const char* CurlEasyStrError(CURLcode code) {
  53. return Get()->curl_easy_strerror_(code);
  54. }
  55. template <typename Pointer>
  56. static CURLcode CurlEasyGetInfo(CURL* curl, CURLINFO info, Pointer out) {
  57. return Get()->curl_easy_getinfo_(curl, info, out);
  58. }
  59. template <typename Pointer>
  60. static CURLcode CurlEasySetOpt(CURL* curl, CURLoption option, Pointer param) {
  61. return Get()->curl_easy_setopt_(curl, option, param);
  62. }
  63. static CURLcode CurlGlobalInit(long flags) {
  64. return Get()->curl_global_init_(flags);
  65. }
  66. static void CurlSlistFreeAll(struct curl_slist* slist) {
  67. return Get()->curl_slist_free_all_(slist);
  68. }
  69. static struct curl_slist* CurlSlistAppend(struct curl_slist* slist,
  70. const char* data) {
  71. return Get()->curl_slist_append_(slist, data);
  72. }
  73. static char* CurlVersion() { return Get()->curl_version_(); }
  74. private:
  75. Libcurl() = default;
  76. ~Libcurl() = delete;
  77. static Libcurl* Get() {
  78. static Libcurl* instance = new Libcurl();
  79. return instance;
  80. }
  81. bool Initialize() {
  82. void* libcurl = []() {
  83. std::vector<std::string> errors;
  84. for (const auto& lib : {
  85. "libcurl.so",
  86. "libcurl-gnutls.so.4",
  87. "libcurl-nss.so.4",
  88. "libcurl.so.4",
  89. }) {
  90. void* libcurl = dlopen(lib, RTLD_LAZY | RTLD_LOCAL);
  91. if (libcurl) {
  92. return libcurl;
  93. }
  94. errors.emplace_back(dlerror());
  95. }
  96. for (const auto& message : errors) {
  97. LOG(ERROR) << "dlopen:" << message;
  98. }
  99. return static_cast<void*>(nullptr);
  100. }();
  101. if (!libcurl) {
  102. return false;
  103. }
  104. #define LINK_OR_RETURN_FALSE(symbol) \
  105. do { \
  106. symbol##_.SetPointer(dlsym(libcurl, #symbol)); \
  107. if (!symbol##_) { \
  108. LOG(ERROR) << "dlsym:" << dlerror(); \
  109. return false; \
  110. } \
  111. } while (0);
  112. LINK_OR_RETURN_FALSE(curl_easy_cleanup);
  113. LINK_OR_RETURN_FALSE(curl_easy_init);
  114. LINK_OR_RETURN_FALSE(curl_easy_perform);
  115. LINK_OR_RETURN_FALSE(curl_easy_strerror);
  116. LINK_OR_RETURN_FALSE(curl_easy_getinfo);
  117. LINK_OR_RETURN_FALSE(curl_easy_setopt);
  118. LINK_OR_RETURN_FALSE(curl_global_init);
  119. LINK_OR_RETURN_FALSE(curl_slist_free_all);
  120. LINK_OR_RETURN_FALSE(curl_slist_append);
  121. LINK_OR_RETURN_FALSE(curl_version);
  122. #undef LINK_OR_RETURN_FALSE
  123. return true;
  124. }
  125. NoCfiIcall<decltype(curl_easy_cleanup)*> curl_easy_cleanup_;
  126. NoCfiIcall<decltype(curl_easy_init)*> curl_easy_init_;
  127. NoCfiIcall<decltype(curl_easy_perform)*> curl_easy_perform_;
  128. NoCfiIcall<decltype(curl_easy_strerror)*> curl_easy_strerror_;
  129. NoCfiIcall<decltype(curl_easy_getinfo)*> curl_easy_getinfo_;
  130. NoCfiIcall<decltype(curl_easy_setopt)*> curl_easy_setopt_;
  131. NoCfiIcall<decltype(curl_global_init)*> curl_global_init_;
  132. NoCfiIcall<decltype(curl_slist_free_all)*> curl_slist_free_all_;
  133. NoCfiIcall<decltype(curl_slist_append)*> curl_slist_append_;
  134. NoCfiIcall<decltype(curl_version)*> curl_version_;
  135. };
  136. std::string UserAgent() {
  137. std::string user_agent = base::StringPrintf(
  138. "%s/%s %s", PACKAGE_NAME, PACKAGE_VERSION, Libcurl::CurlVersion());
  139. utsname os;
  140. if (uname(&os) != 0) {
  141. PLOG(WARNING) << "uname";
  142. } else {
  143. // Match the architecture name that would be used by the kernel, so that the
  144. // strcmp() below can omit the kernel’s architecture name if it’s the same
  145. // as the user process’ architecture. On Linux, these names are normally
  146. // defined in each architecture’s Makefile as UTS_MACHINE, but can be
  147. // overridden in architecture-specific configuration as COMPAT_UTS_MACHINE.
  148. // See linux-4.9.17/arch/*/Makefile and
  149. // linux-4.9.17/arch/*/include/asm/compat.h. In turn, on some systems, these
  150. // names are further overridden or refined in early kernel startup code by
  151. // modifying the string returned by linux-4.9.17/include/linux/utsname.h
  152. // init_utsname() as noted.
  153. #if defined(ARCH_CPU_X86)
  154. // linux-4.9.17/arch/x86/kernel/cpu/bugs.c check_bugs() sets the first digit
  155. // to 4, 5, or 6, but no higher.
  156. #if defined(__i686__)
  157. static constexpr char arch[] = "i686";
  158. #elif defined(__i586__)
  159. static constexpr char arch[] = "i586";
  160. #elif defined(__i486__)
  161. static constexpr char arch[] = "i486";
  162. #else
  163. static constexpr char arch[] = "i386";
  164. #endif
  165. #elif defined(ARCH_CPU_X86_64)
  166. static constexpr char arch[] = "x86_64";
  167. #elif defined(ARCH_CPU_ARMEL)
  168. // linux-4.9.17/arch/arm/kernel/setup.c setup_processor() bases the string
  169. // on the ARM processor name and a character identifying little- or
  170. // big-endian. The processor name comes from a definition in
  171. // arch/arm/mm/proc-*.S.
  172. #if defined(__ARM_ARCH_4T__)
  173. static constexpr char arch[] =
  174. "armv4t"
  175. #elif defined(__ARM_ARCH_5TEJ__)
  176. static constexpr char arch[] =
  177. "armv5tej"
  178. #elif defined(__ARM_ARCH_5TE__)
  179. static constexpr char arch[] =
  180. "armv5te"
  181. #elif defined(__ARM_ARCH_5T__)
  182. static constexpr char arch[] =
  183. "armv5t"
  184. #elif defined(__ARM_ARCH_7M__)
  185. static constexpr char arch[] =
  186. "armv7m"
  187. #else
  188. // Most ARM architectures fall into here, including all profile variants of
  189. // armv6, armv7, armv8, with one exception, armv7m, handled above.
  190. // xstr(__ARM_ARCH) will be the architecture revision number, such as 6, 7,
  191. // or 8.
  192. #define xstr(s) str(s)
  193. #define str(s) #s
  194. static constexpr char arch[] =
  195. "armv" xstr(__ARM_ARCH)
  196. #undef str
  197. #undef xstr
  198. #endif
  199. #if defined(ARCH_CPU_LITTLE_ENDIAN)
  200. "l";
  201. #elif defined(ARCH_CPU_BIG_ENDIAN)
  202. "b";
  203. #endif
  204. #elif defined(ARCH_CPU_ARM64)
  205. // ARM64 uses aarch64 or aarch64_be as directed by ELF_PLATFORM. See
  206. // linux-4.9.17/arch/arm64/kernel/setup.c setup_arch().
  207. #if defined(ARCH_CPU_LITTLE_ENDIAN)
  208. static constexpr char arch[] = "aarch64";
  209. #elif defined(ARCH_CPU_BIG_ENDIAN)
  210. static constexpr char arch[] = "aarch64_be";
  211. #endif
  212. #elif defined(ARCH_CPU_RISCV64)
  213. static constexpr char arch[] = "riscv64";
  214. #else
  215. #error Port
  216. #endif
  217. user_agent.append(
  218. base::StringPrintf(" %s/%s (%s", os.sysname, os.release, arch));
  219. if (strcmp(arch, os.machine) != 0) {
  220. user_agent.append(base::StringPrintf("; %s", os.machine));
  221. }
  222. user_agent.append(1, ')');
  223. }
  224. return user_agent;
  225. }
  226. std::string CurlErrorMessage(CURLcode curl_err, const std::string& base) {
  227. return base::StringPrintf("%s: %s (%d)",
  228. base.c_str(),
  229. Libcurl::CurlEasyStrError(curl_err),
  230. curl_err);
  231. }
  232. struct ScopedCURLTraits {
  233. static CURL* InvalidValue() { return nullptr; }
  234. static void Free(CURL* curl) {
  235. if (curl) {
  236. Libcurl::CurlEasyCleanup(curl);
  237. }
  238. }
  239. };
  240. using ScopedCURL = base::ScopedGeneric<CURL*, ScopedCURLTraits>;
  241. class CurlSList {
  242. public:
  243. CurlSList() : list_(nullptr) {}
  244. CurlSList(const CurlSList&) = delete;
  245. CurlSList& operator=(const CurlSList&) = delete;
  246. ~CurlSList() {
  247. if (list_) {
  248. Libcurl::CurlSlistFreeAll(list_);
  249. }
  250. }
  251. curl_slist* get() const { return list_; }
  252. bool Append(const char* data) {
  253. curl_slist* list = Libcurl::CurlSlistAppend(list_, data);
  254. if (!list_) {
  255. list_ = list;
  256. }
  257. return list != nullptr;
  258. }
  259. private:
  260. curl_slist* list_;
  261. };
  262. class ScopedClearString {
  263. public:
  264. explicit ScopedClearString(std::string* string) : string_(string) {}
  265. ScopedClearString(const ScopedClearString&) = delete;
  266. ScopedClearString& operator=(const ScopedClearString&) = delete;
  267. ~ScopedClearString() {
  268. if (string_) {
  269. string_->clear();
  270. }
  271. }
  272. void Disarm() { string_ = nullptr; }
  273. private:
  274. std::string* string_;
  275. };
  276. class HTTPTransportLibcurl final : public HTTPTransport {
  277. public:
  278. HTTPTransportLibcurl();
  279. HTTPTransportLibcurl(const HTTPTransportLibcurl&) = delete;
  280. HTTPTransportLibcurl& operator=(const HTTPTransportLibcurl&) = delete;
  281. ~HTTPTransportLibcurl() override;
  282. // HTTPTransport:
  283. bool ExecuteSynchronously(std::string* response_body) override;
  284. private:
  285. static size_t ReadRequestBody(char* buffer,
  286. size_t size,
  287. size_t nitems,
  288. void* userdata);
  289. static size_t WriteResponseBody(char* buffer,
  290. size_t size,
  291. size_t nitems,
  292. void* userdata);
  293. };
  294. HTTPTransportLibcurl::HTTPTransportLibcurl() : HTTPTransport() {}
  295. HTTPTransportLibcurl::~HTTPTransportLibcurl() {}
  296. bool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) {
  297. DCHECK(body_stream());
  298. response_body->clear();
  299. // curl_easy_init() will do this on the first call if it hasn’t been done yet,
  300. // but not in a thread-safe way as is done here.
  301. static CURLcode curl_global_init_err = []() {
  302. return Libcurl::CurlGlobalInit(CURL_GLOBAL_DEFAULT);
  303. }();
  304. if (curl_global_init_err != CURLE_OK) {
  305. LOG(ERROR) << CurlErrorMessage(curl_global_init_err, "curl_global_init");
  306. return false;
  307. }
  308. CurlSList curl_headers;
  309. ScopedCURL curl(Libcurl::CurlEasyInit());
  310. if (!curl.get()) {
  311. LOG(ERROR) << "curl_easy_init";
  312. return false;
  313. }
  314. // These macros wrap the repetitive “try something, log an error and return
  315. // false on failure” pattern. Macros are convenient because the log messages
  316. // will point to the correct line number, which can help pinpoint a problem when
  317. // there are as many calls to these functions as there are here.
  318. #define TRY_CURL_EASY_SETOPT(curl, option, parameter) \
  319. do { \
  320. CURLcode curl_err = \
  321. Libcurl::CurlEasySetOpt((curl), (option), (parameter)); \
  322. if (curl_err != CURLE_OK) { \
  323. LOG(ERROR) << CurlErrorMessage(curl_err, "curl_easy_setopt"); \
  324. return false; \
  325. } \
  326. } while (false)
  327. #define TRY_CURL_SLIST_APPEND(slist, data) \
  328. do { \
  329. if (!(slist).Append(data)) { \
  330. LOG(ERROR) << "curl_slist_append"; \
  331. return false; \
  332. } \
  333. } while (false)
  334. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_USERAGENT, UserAgent().c_str());
  335. // Accept and automatically decode any encoding that libcurl understands.
  336. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_ACCEPT_ENCODING, "");
  337. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_URL, url().c_str());
  338. if (!root_ca_certificate_path().empty()) {
  339. TRY_CURL_EASY_SETOPT(
  340. curl.get(), CURLOPT_CAINFO, root_ca_certificate_path().value().c_str());
  341. }
  342. constexpr int kMillisecondsPerSecond = 1E3;
  343. TRY_CURL_EASY_SETOPT(curl.get(),
  344. CURLOPT_TIMEOUT_MS,
  345. static_cast<long>(timeout() * kMillisecondsPerSecond));
  346. // If the request body size is known ahead of time, a Content-Length header
  347. // field will be present. Store that to use as CURLOPT_POSTFIELDSIZE_LARGE,
  348. // which will both set the Content-Length field in the request header and
  349. // inform libcurl of the request body size. Otherwise, use Transfer-Encoding:
  350. // chunked, which does not require advance knowledge of the request body size.
  351. bool chunked = true;
  352. size_t content_length;
  353. for (const auto& pair : headers()) {
  354. if (pair.first == kContentLength) {
  355. chunked = !base::StringToSizeT(pair.second, &content_length);
  356. DCHECK(!chunked);
  357. } else {
  358. TRY_CURL_SLIST_APPEND(curl_headers,
  359. (pair.first + ": " + pair.second).c_str());
  360. }
  361. }
  362. if (method() == "POST") {
  363. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_POST, 1l);
  364. // By default when sending a POST request, libcurl includes an “Expect:
  365. // 100-continue” header field. Althogh this header is specified in HTTP/1.1
  366. // (RFC 2616 §8.2.3, RFC 7231 §5.1.1), even collection servers that claim to
  367. // speak HTTP/1.1 may not respond to it. When sending this header field,
  368. // libcurl will wait for one second for the server to respond with a “100
  369. // Continue” status before continuing to transmit the request body. This
  370. // delay is avoided by telling libcurl not to send this header field at all.
  371. // The drawback is that certain HTTP error statuses may not be received
  372. // until after substantial amounts of data have been sent to the server.
  373. TRY_CURL_SLIST_APPEND(curl_headers, "Expect:");
  374. if (chunked) {
  375. TRY_CURL_SLIST_APPEND(curl_headers, "Transfer-Encoding: chunked");
  376. } else {
  377. curl_off_t content_length_curl;
  378. if (!AssignIfInRange(&content_length_curl, content_length)) {
  379. LOG(ERROR) << base::StringPrintf("Content-Length %zu too large",
  380. content_length);
  381. return false;
  382. }
  383. TRY_CURL_EASY_SETOPT(
  384. curl.get(), CURLOPT_POSTFIELDSIZE_LARGE, content_length_curl);
  385. }
  386. } else if (method() != "GET") {
  387. // Untested.
  388. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_CUSTOMREQUEST, method().c_str());
  389. }
  390. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_HTTPHEADER, curl_headers.get());
  391. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READFUNCTION, ReadRequestBody);
  392. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READDATA, this);
  393. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, WriteResponseBody);
  394. TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEDATA, response_body);
  395. #undef TRY_CURL_EASY_SETOPT
  396. #undef TRY_CURL_SLIST_APPEND
  397. // If a partial response body is received and then a failure occurs, ensure
  398. // that response_body is cleared.
  399. ScopedClearString clear_response_body(response_body);
  400. // Do it.
  401. CURLcode curl_err = Libcurl::CurlEasyPerform(curl.get());
  402. if (curl_err != CURLE_OK) {
  403. LOG(ERROR) << CurlErrorMessage(curl_err, "curl_easy_perform");
  404. return false;
  405. }
  406. long status;
  407. curl_err =
  408. Libcurl::CurlEasyGetInfo(curl.get(), CURLINFO_RESPONSE_CODE, &status);
  409. if (curl_err != CURLE_OK) {
  410. LOG(ERROR) << CurlErrorMessage(curl_err, "curl_easy_getinfo");
  411. return false;
  412. }
  413. if (status != 200) {
  414. LOG(ERROR) << base::StringPrintf("HTTP status %ld", status);
  415. return false;
  416. }
  417. // The response body is complete. Don’t clear it.
  418. clear_response_body.Disarm();
  419. return true;
  420. }
  421. // static
  422. size_t HTTPTransportLibcurl::ReadRequestBody(char* buffer,
  423. size_t size,
  424. size_t nitems,
  425. void* userdata) {
  426. HTTPTransportLibcurl* self =
  427. reinterpret_cast<HTTPTransportLibcurl*>(userdata);
  428. // This libcurl callback mimics the silly stdio-style fread() interface: size
  429. // and nitems have been separated and must be multiplied.
  430. base::CheckedNumeric<size_t> checked_len = base::CheckMul(size, nitems);
  431. size_t len = checked_len.ValueOrDefault(std::numeric_limits<size_t>::max());
  432. // Limit the read to what can be expressed in a FileOperationResult.
  433. len = std::min(
  434. len,
  435. static_cast<size_t>(std::numeric_limits<FileOperationResult>::max()));
  436. FileOperationResult bytes_read = self->body_stream()->GetBytesBuffer(
  437. reinterpret_cast<uint8_t*>(buffer), len);
  438. if (bytes_read < 0) {
  439. return CURL_READFUNC_ABORT;
  440. }
  441. return bytes_read;
  442. }
  443. // static
  444. size_t HTTPTransportLibcurl::WriteResponseBody(char* buffer,
  445. size_t size,
  446. size_t nitems,
  447. void* userdata) {
  448. std::string* response_body = reinterpret_cast<std::string*>(userdata);
  449. // This libcurl callback mimics the silly stdio-style fread() interface: size
  450. // and nitems have been separated and must be multiplied.
  451. base::CheckedNumeric<size_t> checked_len = base::CheckMul(size, nitems);
  452. size_t len = checked_len.ValueOrDefault(std::numeric_limits<size_t>::max());
  453. response_body->append(buffer, len);
  454. return len;
  455. }
  456. } // namespace
  457. // static
  458. std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
  459. return std::unique_ptr<HTTPTransport>(
  460. Libcurl::Initialized() ? new HTTPTransportLibcurl() : nullptr);
  461. }
  462. } // namespace crashpad