uclient-fetch.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. /*
  2. * uclient - ustream based protocol client library
  3. *
  4. * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
  5. *
  6. * Permission to use, copy, modify, and/or distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #define _GNU_SOURCE
  19. #include <sys/stat.h>
  20. #include <sys/socket.h>
  21. #include <unistd.h>
  22. #include <stdio.h>
  23. #include <getopt.h>
  24. #include <fcntl.h>
  25. #include <glob.h>
  26. #include <stdint.h>
  27. #include <inttypes.h>
  28. #include <signal.h>
  29. #include <libubox/blobmsg.h>
  30. #include "progress.h"
  31. #include "uclient.h"
  32. #include "uclient-utils.h"
  33. #ifndef strdupa
  34. #define strdupa(x) strcpy(alloca(strlen(x)+1),x)
  35. #endif
  36. static const char *user_agent = "uclient-fetch";
  37. static const char *post_data;
  38. static const char *post_file;
  39. static struct ustream_ssl_ctx *ssl_ctx;
  40. static const struct ustream_ssl_ops *ssl_ops;
  41. static int quiet = false;
  42. static bool verify = true;
  43. static bool proxy = true;
  44. static bool default_certs = false;
  45. static bool no_output;
  46. static const char *opt_output_file;
  47. static int output_fd = -1;
  48. static int error_ret;
  49. static off_t out_offset;
  50. static off_t out_bytes;
  51. static off_t out_len;
  52. static char *auth_str;
  53. static char **urls;
  54. static int n_urls;
  55. static int timeout;
  56. static bool resume, cur_resume;
  57. static struct progress pmt;
  58. static struct uloop_timeout pmt_timer;
  59. static int init_request(struct uclient *cl);
  60. static void request_done(struct uclient *cl);
  61. static void pmt_update(struct uloop_timeout *t)
  62. {
  63. progress_update(&pmt, out_offset, out_bytes, out_len);
  64. uloop_timeout_set(t, 1000);
  65. }
  66. static const char *
  67. get_proxy_url(char *url)
  68. {
  69. char prefix[16];
  70. char *sep;
  71. if (!proxy)
  72. return NULL;
  73. sep = strchr(url, ':');
  74. if (!sep)
  75. return NULL;
  76. if (sep - url > 5)
  77. return NULL;
  78. memcpy(prefix, url, sep - url);
  79. strcpy(prefix + (sep - url), "_proxy");
  80. return getenv(prefix);
  81. }
  82. static int open_output_file(const char *path, uint64_t resume_offset)
  83. {
  84. const char *output_file = opt_output_file;
  85. char *filename = NULL;
  86. int flags;
  87. int ret;
  88. if (cur_resume)
  89. flags = O_RDWR;
  90. else
  91. flags = O_WRONLY | O_TRUNC;
  92. if (!cur_resume && !output_file)
  93. flags |= O_EXCL;
  94. flags |= O_CREAT;
  95. if (output_file) {
  96. if (!strcmp(output_file, "-")) {
  97. if (!quiet)
  98. fprintf(stderr, "Writing to stdout\n");
  99. ret = STDOUT_FILENO;
  100. goto done;
  101. }
  102. } else {
  103. filename = uclient_get_url_filename(path, "index.html");
  104. if (!filename) {
  105. ret = -ENOMEM;
  106. goto out;
  107. }
  108. output_file = filename;
  109. }
  110. if (!quiet)
  111. fprintf(stderr, "Writing to '%s'\n", output_file);
  112. ret = open(output_file, flags, 0644);
  113. if (ret < 0)
  114. goto free;
  115. if (resume_offset &&
  116. lseek(ret, resume_offset, SEEK_SET) < 0) {
  117. if (!quiet)
  118. fprintf(stderr, "Failed to seek %"PRIu64" bytes in output file\n", resume_offset);
  119. close(ret);
  120. ret = -1;
  121. goto free;
  122. }
  123. out_offset = resume_offset;
  124. out_bytes += resume_offset;
  125. done:
  126. if (!quiet) {
  127. progress_init(&pmt, output_file);
  128. pmt_timer.cb = pmt_update;
  129. pmt_timer.cb(&pmt_timer);
  130. }
  131. free:
  132. free(filename);
  133. out:
  134. return ret;
  135. }
  136. static void header_done_cb(struct uclient *cl)
  137. {
  138. enum {
  139. H_RANGE,
  140. H_LEN,
  141. __H_MAX
  142. };
  143. static const struct blobmsg_policy policy[__H_MAX] = {
  144. [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING },
  145. [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING },
  146. };
  147. struct blob_attr *tb[__H_MAX];
  148. uint64_t resume_offset = 0, resume_end, resume_size;
  149. static int retries;
  150. if (retries < 10) {
  151. int ret = uclient_http_redirect(cl);
  152. if (ret < 0) {
  153. if (!quiet)
  154. fprintf(stderr, "Failed to redirect to %s on %s\n", cl->url->location, cl->url->host);
  155. error_ret = 8;
  156. request_done(cl);
  157. return;
  158. }
  159. if (ret > 0) {
  160. if (!quiet)
  161. fprintf(stderr, "Redirected to %s on %s\n", cl->url->location, cl->url->host);
  162. retries++;
  163. return;
  164. }
  165. }
  166. if (cl->status_code == 204 && cur_resume) {
  167. /* Resume attempt failed, try normal download */
  168. cur_resume = false;
  169. init_request(cl);
  170. return;
  171. }
  172. blobmsg_parse(policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta));
  173. switch (cl->status_code) {
  174. case 416:
  175. if (!quiet)
  176. fprintf(stderr, "File download already fully retrieved; nothing to do.\n");
  177. request_done(cl);
  178. break;
  179. case 206:
  180. if (!cur_resume) {
  181. if (!quiet)
  182. fprintf(stderr, "Error: Partial content received, full content requested\n");
  183. error_ret = 8;
  184. request_done(cl);
  185. break;
  186. }
  187. if (!tb[H_RANGE]) {
  188. if (!quiet)
  189. fprintf(stderr, "Content-Range header is missing\n");
  190. error_ret = 8;
  191. break;
  192. }
  193. if (sscanf(blobmsg_get_string(tb[H_RANGE]),
  194. "bytes %"PRIu64"-%"PRIu64"/%"PRIu64,
  195. &resume_offset, &resume_end, &resume_size) != 3) {
  196. if (!quiet)
  197. fprintf(stderr, "Content-Range header is invalid\n");
  198. error_ret = 8;
  199. break;
  200. }
  201. /* fall through */
  202. case 204:
  203. case 200:
  204. if (no_output)
  205. break;
  206. if (tb[H_LEN])
  207. out_len = strtoul(blobmsg_get_string(tb[H_LEN]), NULL, 10);
  208. output_fd = open_output_file(cl->url->location, resume_offset);
  209. if (output_fd < 0) {
  210. if (!quiet)
  211. perror("Cannot open output file");
  212. error_ret = 3;
  213. request_done(cl);
  214. }
  215. break;
  216. default:
  217. if (!quiet)
  218. fprintf(stderr, "HTTP error %d\n", cl->status_code);
  219. request_done(cl);
  220. error_ret = 8;
  221. break;
  222. }
  223. }
  224. static void read_data_cb(struct uclient *cl)
  225. {
  226. char buf[256];
  227. ssize_t n;
  228. int len;
  229. if (!no_output && output_fd < 0)
  230. return;
  231. while (1) {
  232. len = uclient_read(cl, buf, sizeof(buf));
  233. if (len <= 0)
  234. return;
  235. out_bytes += len;
  236. if (!no_output) {
  237. n = write(output_fd, buf, len);
  238. if (n < 0)
  239. return;
  240. }
  241. }
  242. }
  243. static void msg_connecting(struct uclient *cl)
  244. {
  245. char addr[INET6_ADDRSTRLEN];
  246. int port;
  247. if (quiet)
  248. return;
  249. uclient_get_addr(addr, &port, &cl->remote_addr);
  250. fprintf(stderr, "Connecting to %s:%d\n", addr, port);
  251. }
  252. static void check_resume_offset(struct uclient *cl)
  253. {
  254. char range_str[64];
  255. struct stat st;
  256. char *file;
  257. int ret;
  258. file = uclient_get_url_filename(cl->url->location, "index.html");
  259. if (!file)
  260. return;
  261. ret = stat(file, &st);
  262. free(file);
  263. if (ret)
  264. return;
  265. if (!st.st_size)
  266. return;
  267. snprintf(range_str, sizeof(range_str), "bytes=%"PRIu64"-", (uint64_t) st.st_size);
  268. uclient_http_set_header(cl, "Range", range_str);
  269. }
  270. static int init_request(struct uclient *cl)
  271. {
  272. int rc;
  273. out_offset = 0;
  274. out_bytes = 0;
  275. out_len = 0;
  276. uclient_http_set_ssl_ctx(cl, ssl_ops, ssl_ctx, verify);
  277. if (timeout)
  278. cl->timeout_msecs = timeout * 1000;
  279. rc = uclient_connect(cl);
  280. if (rc)
  281. return rc;
  282. msg_connecting(cl);
  283. rc = uclient_http_set_request_type(cl, post_data || post_file ? "POST" : "GET");
  284. if (rc)
  285. return rc;
  286. uclient_http_reset_headers(cl);
  287. uclient_http_set_header(cl, "User-Agent", user_agent);
  288. if (cur_resume)
  289. check_resume_offset(cl);
  290. if (post_data) {
  291. uclient_http_set_header(cl, "Content-Type", "application/x-www-form-urlencoded");
  292. uclient_write(cl, post_data, strlen(post_data));
  293. }
  294. else if(post_file)
  295. {
  296. FILE *input_file;
  297. uclient_http_set_header(cl, "Content-Type", "application/x-www-form-urlencoded");
  298. input_file = fopen(post_file, "r");
  299. if (!input_file)
  300. return errno;
  301. char tbuf[1024];
  302. size_t rlen = 0;
  303. do
  304. {
  305. rlen = fread(tbuf, 1, sizeof(tbuf), input_file);
  306. uclient_write(cl, tbuf, rlen);
  307. }
  308. while(rlen);
  309. fclose(input_file);
  310. }
  311. rc = uclient_request(cl);
  312. if (rc)
  313. return rc;
  314. return 0;
  315. }
  316. static void request_done(struct uclient *cl)
  317. {
  318. const char *proxy_url;
  319. if (n_urls) {
  320. proxy_url = get_proxy_url(*urls);
  321. if (proxy_url) {
  322. uclient_set_url(cl, proxy_url, NULL);
  323. uclient_set_proxy_url(cl, *urls, auth_str);
  324. } else {
  325. uclient_set_url(cl, *urls, auth_str);
  326. }
  327. n_urls--;
  328. cur_resume = resume;
  329. error_ret = init_request(cl);
  330. if (error_ret == 0)
  331. return;
  332. }
  333. if (output_fd >= 0 && !opt_output_file) {
  334. close(output_fd);
  335. output_fd = -1;
  336. }
  337. uclient_disconnect(cl);
  338. uloop_end();
  339. }
  340. static void eof_cb(struct uclient *cl)
  341. {
  342. if (!quiet) {
  343. pmt_update(&pmt_timer);
  344. uloop_timeout_cancel(&pmt_timer);
  345. fprintf(stderr, "\n");
  346. }
  347. if (!cl->data_eof) {
  348. if (!quiet)
  349. fprintf(stderr, "Connection reset prematurely\n");
  350. error_ret = 4;
  351. } else if (!quiet) {
  352. fprintf(stderr, "Download completed (%"PRIu64" bytes)\n", (uint64_t) out_bytes);
  353. }
  354. request_done(cl);
  355. }
  356. static void handle_uclient_error(struct uclient *cl, int code)
  357. {
  358. const char *type = "Unknown error";
  359. bool ignore = false;
  360. switch(code) {
  361. case UCLIENT_ERROR_CONNECT:
  362. type = "Connection failed";
  363. error_ret = 4;
  364. break;
  365. case UCLIENT_ERROR_TIMEDOUT:
  366. type = "Connection timed out";
  367. error_ret = 4;
  368. break;
  369. case UCLIENT_ERROR_SSL_INVALID_CERT:
  370. type = "Invalid SSL certificate";
  371. ignore = !verify;
  372. error_ret = 5;
  373. break;
  374. case UCLIENT_ERROR_SSL_CN_MISMATCH:
  375. type = "Server hostname does not match SSL certificate";
  376. ignore = !verify;
  377. error_ret = 5;
  378. break;
  379. default:
  380. error_ret = 1;
  381. break;
  382. }
  383. if (!quiet)
  384. fprintf(stderr, "Connection error: %s%s\n", type, ignore ? " (ignored)" : "");
  385. if (ignore)
  386. error_ret = 0;
  387. else
  388. request_done(cl);
  389. }
  390. static const struct uclient_cb cb = {
  391. .header_done = header_done_cb,
  392. .data_read = read_data_cb,
  393. .data_eof = eof_cb,
  394. .error = handle_uclient_error,
  395. };
  396. static int usage(const char *progname)
  397. {
  398. fprintf(stderr,
  399. "Usage: %s [options] <URL>\n"
  400. "Options:\n"
  401. " -4 Use IPv4 only\n"
  402. " -6 Use IPv6 only\n"
  403. " -O <file> Redirect output to file (use \"-\" for stdout)\n"
  404. " -P <dir> Set directory for output files\n"
  405. " --quiet | -q Turn off status messages\n"
  406. " --continue | -c Continue a partially-downloaded file\n"
  407. " --user=<user> HTTP authentication username\n"
  408. " --password=<password> HTTP authentication password\n"
  409. " --user-agent | -U <str> Set HTTP user agent\n"
  410. " --post-data=STRING use the POST method; send STRING as the data\n"
  411. " --post-file=FILE use the POST method; send FILE as the data\n"
  412. " --spider | -s Spider mode - only check file existence\n"
  413. " --timeout=N | -T N Set connect/request timeout to N seconds\n"
  414. " --proxy=on | -Y on Enable interpretation of proxy env vars (default)\n"
  415. " --proxy=off | -Y off |\n"
  416. " --no-proxy Disable interpretation of proxy env vars\n"
  417. "\n"
  418. "HTTPS options:\n"
  419. " --ca-certificate=<cert> Load CA certificates from file <cert>\n"
  420. " --no-check-certificate don't validate the server's certificate\n"
  421. " --ciphers=<cipherlist> Set the cipher list string\n"
  422. "\n", progname);
  423. return 1;
  424. }
  425. static void init_ca_cert(void)
  426. {
  427. glob_t gl;
  428. unsigned int i;
  429. glob("/etc/ssl/certs/*.crt", 0, NULL, &gl);
  430. for (i = 0; i < gl.gl_pathc; i++)
  431. ssl_ops->context_add_ca_crt_file(ssl_ctx, gl.gl_pathv[i]);
  432. globfree(&gl);
  433. }
  434. static int no_ssl(const char *progname)
  435. {
  436. fprintf(stderr,
  437. "%s: SSL support not available, please install one of the "
  438. "libustream-.*[ssl|tls] packages as well as the ca-bundle and "
  439. "ca-certificates packages.\n",
  440. progname);
  441. return 1;
  442. }
  443. enum {
  444. L_NO_CHECK_CERTIFICATE,
  445. L_CA_CERTIFICATE,
  446. L_CIPHERS,
  447. L_USER,
  448. L_PASSWORD,
  449. L_USER_AGENT,
  450. L_POST_DATA,
  451. L_POST_FILE,
  452. L_SPIDER,
  453. L_TIMEOUT,
  454. L_CONTINUE,
  455. L_PROXY,
  456. L_NO_PROXY,
  457. L_QUIET,
  458. };
  459. static const struct option longopts[] = {
  460. [L_NO_CHECK_CERTIFICATE] = { "no-check-certificate", no_argument, NULL, 0 },
  461. [L_CA_CERTIFICATE] = { "ca-certificate", required_argument, NULL, 0 },
  462. [L_CIPHERS] = { "ciphers", required_argument, NULL, 0 },
  463. [L_USER] = { "user", required_argument, NULL, 0 },
  464. [L_PASSWORD] = { "password", required_argument, NULL, 0 },
  465. [L_USER_AGENT] = { "user-agent", required_argument, NULL, 0 },
  466. [L_POST_DATA] = { "post-data", required_argument, NULL, 0 },
  467. [L_POST_FILE] = { "post-file", required_argument, NULL, 0 },
  468. [L_SPIDER] = { "spider", no_argument, NULL, 0 },
  469. [L_TIMEOUT] = { "timeout", required_argument, NULL, 0 },
  470. [L_CONTINUE] = { "continue", no_argument, NULL, 0 },
  471. [L_PROXY] = { "proxy", required_argument, NULL, 0 },
  472. [L_NO_PROXY] = { "no-proxy", no_argument, NULL, 0 },
  473. [L_QUIET] = { "quiet", no_argument, NULL, 0 },
  474. {}
  475. };
  476. int main(int argc, char **argv)
  477. {
  478. const char *progname = argv[0];
  479. const char *proxy_url;
  480. char *username = NULL;
  481. char *password = NULL;
  482. struct uclient *cl;
  483. int longopt_idx = 0;
  484. bool has_cert = false;
  485. int i, ch;
  486. int rc;
  487. int af = -1;
  488. signal(SIGPIPE, SIG_IGN);
  489. ssl_ctx = uclient_new_ssl_context(&ssl_ops);
  490. while ((ch = getopt_long(argc, argv, "46cO:P:qsT:U:Y:", longopts, &longopt_idx)) != -1) {
  491. switch(ch) {
  492. case 0:
  493. switch (longopt_idx) {
  494. case L_NO_CHECK_CERTIFICATE:
  495. verify = false;
  496. if (ssl_ctx)
  497. ssl_ops->context_set_require_validation(ssl_ctx, verify);
  498. break;
  499. case L_CA_CERTIFICATE:
  500. has_cert = true;
  501. if (ssl_ctx)
  502. ssl_ops->context_add_ca_crt_file(ssl_ctx, optarg);
  503. break;
  504. case L_CIPHERS:
  505. if (ssl_ctx) {
  506. if (ssl_ops->context_set_ciphers(ssl_ctx, optarg)) {
  507. if (!quiet)
  508. fprintf(stderr, "No recognized ciphers in cipher list\n");
  509. exit(1);
  510. }
  511. }
  512. break;
  513. case L_USER:
  514. if (!strlen(optarg))
  515. break;
  516. username = strdupa(optarg);
  517. memset(optarg, '*', strlen(optarg));
  518. break;
  519. case L_PASSWORD:
  520. if (!strlen(optarg))
  521. break;
  522. password = strdupa(optarg);
  523. memset(optarg, '*', strlen(optarg));
  524. break;
  525. case L_USER_AGENT:
  526. user_agent = optarg;
  527. break;
  528. case L_POST_DATA:
  529. post_data = optarg;
  530. break;
  531. case L_POST_FILE:
  532. post_file = optarg;
  533. break;
  534. case L_SPIDER:
  535. no_output = true;
  536. break;
  537. case L_TIMEOUT:
  538. timeout = atoi(optarg);
  539. break;
  540. case L_CONTINUE:
  541. resume = true;
  542. break;
  543. case L_PROXY:
  544. if (strcmp(optarg, "on") != 0)
  545. proxy = false;
  546. break;
  547. case L_NO_PROXY:
  548. proxy = false;
  549. break;
  550. case L_QUIET:
  551. quiet = true;
  552. break;
  553. default:
  554. return usage(progname);
  555. }
  556. break;
  557. case '4':
  558. af = AF_INET;
  559. break;
  560. case '6':
  561. af = AF_INET6;
  562. break;
  563. case 'c':
  564. resume = true;
  565. break;
  566. case 'U':
  567. user_agent = optarg;
  568. break;
  569. case 'O':
  570. opt_output_file = optarg;
  571. break;
  572. case 'P':
  573. if (chdir(optarg)) {
  574. if (!quiet)
  575. perror("Change output directory");
  576. exit(1);
  577. }
  578. break;
  579. case 'q':
  580. quiet = true;
  581. break;
  582. case 's':
  583. no_output = true;
  584. break;
  585. case 'T':
  586. timeout = atoi(optarg);
  587. break;
  588. case 'Y':
  589. if (strcmp(optarg, "on") != 0)
  590. proxy = false;
  591. break;
  592. default:
  593. return usage(progname);
  594. }
  595. }
  596. argv += optind;
  597. argc -= optind;
  598. if (verify && !has_cert)
  599. default_certs = true;
  600. if (argc < 1)
  601. return usage(progname);
  602. if (!ssl_ctx) {
  603. for (i = 0; i < argc; i++) {
  604. if (!strncmp(argv[i], "https", 5))
  605. return no_ssl(progname);
  606. }
  607. }
  608. urls = argv + 1;
  609. n_urls = argc - 1;
  610. uloop_init();
  611. if (username) {
  612. if (password) {
  613. rc = asprintf(&auth_str, "%s:%s", username, password);
  614. if (rc < 0)
  615. return rc;
  616. } else
  617. auth_str = username;
  618. }
  619. if (!quiet)
  620. fprintf(stderr, "Downloading '%s'\n", argv[0]);
  621. proxy_url = get_proxy_url(argv[0]);
  622. if (proxy_url) {
  623. cl = uclient_new(proxy_url, auth_str, &cb);
  624. if (cl)
  625. uclient_set_proxy_url(cl, argv[0], NULL);
  626. } else {
  627. cl = uclient_new(argv[0], auth_str, &cb);
  628. }
  629. if (!cl) {
  630. fprintf(stderr, "Failed to allocate uclient context\n");
  631. return 1;
  632. }
  633. if (af >= 0)
  634. uclient_http_set_address_family(cl, af);
  635. if (ssl_ctx && default_certs)
  636. init_ca_cert();
  637. cur_resume = resume;
  638. rc = init_request(cl);
  639. if (!rc) {
  640. /* no error received, we can enter main loop */
  641. uloop_run();
  642. } else {
  643. fprintf(stderr, "Failed to send request: %s\n", strerror(rc));
  644. error_ret = 4;
  645. }
  646. uloop_done();
  647. uclient_free(cl);
  648. if (output_fd >= 0 && output_fd != STDOUT_FILENO)
  649. close(output_fd);
  650. if (ssl_ctx)
  651. ssl_ops->context_free(ssl_ctx);
  652. return error_ret;
  653. }