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 19 #define _GNU_SOURCE 20 #include <sys/stat.h> 21 #include <sys/socket.h> 22 #include <unistd.h> 23 #include <stdio.h> 24 #include <ctype.h> 25 #include <getopt.h> 26 #include <fcntl.h> 27 #include <glob.h> 28 #include <stdint.h> 29 #include <inttypes.h> 30 #include <signal.h> 31 32 #include <libubox/blobmsg.h> 33 #include <libubox/list.h> 34 35 #include "progress.h" 36 #include "uclient.h" 37 #include "uclient-utils.h" 38 39 #ifndef strdupa 40 #define strdupa(x) strcpy(alloca(strlen(x)+1),x) 41 #endif 42 43 struct header { 44 struct list_head list; 45 char *name; 46 char *value; 47 }; 48 49 static const char *user_agent = "uclient-fetch"; 50 static const char *post_data; 51 static const char *post_file; 52 static struct ustream_ssl_ctx *ssl_ctx; 53 static const struct ustream_ssl_ops *ssl_ops; 54 static int quiet = false; 55 static bool verify = true; 56 static bool proxy = true; 57 static bool default_certs = false; 58 static bool no_output; 59 static const char *opt_output_file; 60 static int output_fd = -1; 61 static int error_ret; 62 static off_t out_offset; 63 static off_t out_bytes; 64 static off_t out_len; 65 static char *auth_str; 66 static char **urls; 67 static int n_urls; 68 static int timeout; 69 static bool resume, cur_resume; 70 static LIST_HEAD(headers); 71 72 static struct progress pmt; 73 static struct uloop_timeout pmt_timer; 74 75 static int init_request(struct uclient *cl); 76 static void request_done(struct uclient *cl); 77 78 static void pmt_update(struct uloop_timeout *t) 79 { 80 progress_update(&pmt, out_offset, out_bytes, out_len); 81 uloop_timeout_set(t, 1000); 82 } 83 84 static const char * 85 get_proxy_url(char *url) 86 { 87 char prefix[16]; 88 char *sep; 89 90 if (!proxy) 91 return NULL; 92 93 sep = strchr(url, ':'); 94 if (!sep) 95 return NULL; 96 97 if (sep - url > 5) 98 return NULL; 99 100 memcpy(prefix, url, sep - url); 101 strcpy(prefix + (sep - url), "_proxy"); 102 return getenv(prefix); 103 } 104 105 static int open_output_file(const char *path, uint64_t resume_offset) 106 { 107 const char *output_file = opt_output_file; 108 char *filename = NULL; 109 int flags; 110 int ret; 111 112 if (cur_resume) 113 flags = O_RDWR; 114 else 115 flags = O_WRONLY | O_TRUNC; 116 117 if (!cur_resume && !output_file) 118 flags |= O_EXCL; 119 120 flags |= O_CREAT; 121 122 if (output_file) { 123 if (!strcmp(output_file, "-")) { 124 if (!quiet) 125 fprintf(stderr, "Writing to stdout\n"); 126 127 ret = STDOUT_FILENO; 128 goto done; 129 } 130 } else { 131 filename = uclient_get_url_filename(path, "index.html"); 132 if (!filename) { 133 ret = -ENOMEM; 134 goto out; 135 } 136 137 output_file = filename; 138 } 139 140 if (!quiet) 141 fprintf(stderr, "Writing to '%s'\n", output_file); 142 ret = open(output_file, flags, 0644); 143 if (ret < 0) 144 goto free; 145 146 if (resume_offset && 147 lseek(ret, resume_offset, SEEK_SET) < 0) { 148 if (!quiet) 149 fprintf(stderr, "Failed to seek %"PRIu64" bytes in output file\n", resume_offset); 150 close(ret); 151 ret = -1; 152 goto free; 153 } 154 155 out_offset = resume_offset; 156 out_bytes += resume_offset; 157 done: 158 if (!quiet) { 159 progress_init(&pmt, output_file); 160 pmt_timer.cb = pmt_update; 161 pmt_timer.cb(&pmt_timer); 162 } 163 164 free: 165 free(filename); 166 out: 167 return ret; 168 } 169 170 static void header_done_cb(struct uclient *cl) 171 { 172 enum { 173 H_RANGE, 174 H_LEN, 175 __H_MAX 176 }; 177 static const struct blobmsg_policy policy[__H_MAX] = { 178 [H_RANGE] = { .name = "content-range", .type = BLOBMSG_TYPE_STRING }, 179 [H_LEN] = { .name = "content-length", .type = BLOBMSG_TYPE_STRING }, 180 }; 181 struct blob_attr *tb[__H_MAX]; 182 uint64_t resume_offset = 0, resume_end, resume_size; 183 static int retries; 184 185 if (retries < 10) { 186 int ret = uclient_http_redirect(cl); 187 if (ret < 0) { 188 if (!quiet) 189 fprintf(stderr, "Failed to redirect to %s on %s\n", cl->url->location, cl->url->host); 190 error_ret = 8; 191 request_done(cl); 192 return; 193 } 194 if (ret > 0) { 195 if (!quiet) 196 fprintf(stderr, "Redirected to %s on %s\n", cl->url->location, cl->url->host); 197 198 retries++; 199 return; 200 } 201 } 202 203 if (cl->status_code == 204 && cur_resume) { 204 /* Resume attempt failed, try normal download */ 205 cur_resume = false; 206 init_request(cl); 207 return; 208 } 209 210 blobmsg_parse(policy, __H_MAX, tb, blob_data(cl->meta), blob_len(cl->meta)); 211 212 switch (cl->status_code) { 213 case 416: 214 if (!quiet) 215 fprintf(stderr, "File download already fully retrieved; nothing to do.\n"); 216 request_done(cl); 217 break; 218 case 206: 219 if (!cur_resume) { 220 if (!quiet) 221 fprintf(stderr, "Error: Partial content received, full content requested\n"); 222 error_ret = 8; 223 request_done(cl); 224 break; 225 } 226 227 if (!tb[H_RANGE]) { 228 if (!quiet) 229 fprintf(stderr, "Content-Range header is missing\n"); 230 error_ret = 8; 231 break; 232 } 233 234 if (sscanf(blobmsg_get_string(tb[H_RANGE]), 235 "bytes %"PRIu64"-%"PRIu64"/%"PRIu64, 236 &resume_offset, &resume_end, &resume_size) != 3) { 237 if (!quiet) 238 fprintf(stderr, "Content-Range header is invalid\n"); 239 error_ret = 8; 240 break; 241 } 242 /* fall through */ 243 case 204: 244 case 200: 245 if (no_output) 246 break; 247 248 if (tb[H_LEN]) 249 out_len = strtoul(blobmsg_get_string(tb[H_LEN]), NULL, 10); 250 251 output_fd = open_output_file(cl->url->location, resume_offset); 252 if (output_fd < 0) { 253 if (!quiet) 254 perror("Cannot open output file"); 255 error_ret = 3; 256 request_done(cl); 257 } 258 break; 259 260 default: 261 if (!quiet) 262 fprintf(stderr, "HTTP error %d\n", cl->status_code); 263 request_done(cl); 264 error_ret = 8; 265 break; 266 } 267 } 268 269 static void read_data_cb(struct uclient *cl) 270 { 271 char buf[256]; 272 ssize_t n; 273 int len; 274 275 if (!no_output && output_fd < 0) 276 return; 277 278 while (1) { 279 len = uclient_read(cl, buf, sizeof(buf)); 280 if (len <= 0) 281 return; 282 283 out_bytes += len; 284 if (!no_output) { 285 n = write(output_fd, buf, len); 286 if (n < 0) 287 return; 288 } 289 } 290 } 291 292 static void msg_connecting(struct uclient *cl) 293 { 294 char addr[INET6_ADDRSTRLEN]; 295 int port; 296 297 if (quiet) 298 return; 299 300 uclient_get_addr(addr, &port, &cl->remote_addr); 301 fprintf(stderr, "Connecting to %s:%d\n", addr, port); 302 } 303 304 static void check_resume_offset(struct uclient *cl) 305 { 306 char range_str[64]; 307 struct stat st; 308 char *file; 309 int ret; 310 311 file = uclient_get_url_filename(cl->url->location, "index.html"); 312 if (!file) 313 return; 314 315 ret = stat(file, &st); 316 free(file); 317 if (ret) 318 return; 319 320 if (!st.st_size) 321 return; 322 323 snprintf(range_str, sizeof(range_str), "bytes=%"PRIu64"-", (uint64_t) st.st_size); 324 uclient_http_set_header(cl, "Range", range_str); 325 } 326 327 static int init_request(struct uclient *cl) 328 { 329 struct header *h; 330 char *content_type = "application/x-www-form-urlencoded"; 331 int rc; 332 333 out_offset = 0; 334 out_bytes = 0; 335 out_len = 0; 336 uclient_http_set_ssl_ctx(cl, ssl_ops, ssl_ctx, verify); 337 338 if (timeout) 339 cl->timeout_msecs = timeout * 1000; 340 341 rc = uclient_connect(cl); 342 if (rc) 343 return rc; 344 345 msg_connecting(cl); 346 347 rc = uclient_http_set_request_type(cl, post_data || post_file ? "POST" : "GET"); 348 if (rc) 349 return rc; 350 351 uclient_http_reset_headers(cl); 352 353 list_for_each_entry(h, &headers, list) { 354 if (!strcasecmp(h->name, "Content-Type")) { 355 if (!post_data && !post_file) 356 return -EINVAL; 357 358 content_type = h->value; 359 } else if (!strcasecmp(h->name, "User-Agent")) { 360 user_agent = h->value; 361 } else { 362 uclient_http_set_header(cl, h->name, h->value); 363 } 364 } 365 366 uclient_http_set_header(cl, "User-Agent", user_agent); 367 368 if (cur_resume) 369 check_resume_offset(cl); 370 371 if (post_data) { 372 uclient_http_set_header(cl, "Content-Type", content_type); 373 uclient_write(cl, post_data, strlen(post_data)); 374 } 375 else if(post_file) 376 { 377 FILE *input_file; 378 uclient_http_set_header(cl, "Content-Type", content_type); 379 380 input_file = fopen(post_file, "r"); 381 if (!input_file) 382 return errno; 383 384 char tbuf[1024]; 385 size_t rlen = 0; 386 do 387 { 388 rlen = fread(tbuf, 1, sizeof(tbuf), input_file); 389 uclient_write(cl, tbuf, rlen); 390 } 391 while(rlen); 392 393 fclose(input_file); 394 } 395 396 rc = uclient_request(cl); 397 if (rc) 398 return rc; 399 400 return 0; 401 } 402 403 static void request_done(struct uclient *cl) 404 { 405 const char *proxy_url; 406 407 if (n_urls) { 408 proxy_url = get_proxy_url(*urls); 409 if (proxy_url) { 410 uclient_set_url(cl, proxy_url, NULL); 411 uclient_set_proxy_url(cl, *urls, auth_str); 412 } else { 413 uclient_set_url(cl, *urls, auth_str); 414 } 415 n_urls--; 416 cur_resume = resume; 417 error_ret = init_request(cl); 418 if (error_ret == 0) 419 return; 420 } 421 422 if (output_fd >= 0 && !opt_output_file) { 423 close(output_fd); 424 output_fd = -1; 425 } 426 uclient_disconnect(cl); 427 uloop_end(); 428 } 429 430 431 static void eof_cb(struct uclient *cl) 432 { 433 if (!quiet) { 434 pmt_update(&pmt_timer); 435 uloop_timeout_cancel(&pmt_timer); 436 fprintf(stderr, "\n"); 437 } 438 439 if (!cl->data_eof) { 440 if (!quiet) 441 fprintf(stderr, "Connection reset prematurely\n"); 442 error_ret = 4; 443 } else if (!quiet) { 444 fprintf(stderr, "Download completed (%"PRIu64" bytes)\n", (uint64_t) out_bytes); 445 } 446 request_done(cl); 447 } 448 449 static void 450 handle_uclient_log_msg(struct uclient *cl, enum uclient_log_type type, const char *msg) 451 { 452 static const char * const type_str_list[] = { 453 [UCLIENT_LOG_SSL_ERROR] = "SSL error", 454 [UCLIENT_LOG_SSL_VERIFY_ERROR] = "SSL verify error", 455 }; 456 const char *type_str = NULL; 457 458 if (type < ARRAY_SIZE(type_str_list)) 459 type_str = type_str_list[type]; 460 if (!type_str) 461 type_str = "Unknown"; 462 463 fprintf(stderr, "%s: %s\n", type_str, msg); 464 } 465 466 static void handle_uclient_error(struct uclient *cl, int code) 467 { 468 const char *type = "Unknown error"; 469 bool ignore = false; 470 471 switch(code) { 472 case UCLIENT_ERROR_CONNECT: 473 type = "Connection failed"; 474 error_ret = 4; 475 break; 476 case UCLIENT_ERROR_TIMEDOUT: 477 type = "Connection timed out"; 478 error_ret = 4; 479 break; 480 case UCLIENT_ERROR_SSL_INVALID_CERT: 481 type = "Invalid SSL certificate"; 482 ignore = !verify; 483 error_ret = 5; 484 break; 485 case UCLIENT_ERROR_SSL_CN_MISMATCH: 486 type = "Server hostname does not match SSL certificate"; 487 ignore = !verify; 488 error_ret = 5; 489 break; 490 default: 491 error_ret = 1; 492 break; 493 } 494 495 if (!quiet) 496 fprintf(stderr, "Connection error: %s%s\n", type, ignore ? " (ignored)" : ""); 497 498 if (ignore) 499 error_ret = 0; 500 else 501 request_done(cl); 502 } 503 504 static const struct uclient_cb cb = { 505 .header_done = header_done_cb, 506 .data_read = read_data_cb, 507 .data_eof = eof_cb, 508 .error = handle_uclient_error, 509 .log_msg = handle_uclient_log_msg, 510 }; 511 512 static void usage(const char *progname) 513 { 514 fprintf(stderr, 515 "Usage: %s [options] <URL>\n" 516 "Options:\n" 517 " -4 Use IPv4 only\n" 518 " -6 Use IPv6 only\n" 519 " -O <file> Redirect output to file (use \"-\" for stdout)\n" 520 " -P <dir> Set directory for output files\n" 521 " --quiet | -q Turn off status messages\n" 522 " --continue | -c Continue a partially-downloaded file\n" 523 " --header='Header: value' Add HTTP header. Multiple allowed\n" 524 " --user=<user> HTTP authentication username\n" 525 " --password=<password> HTTP authentication password\n" 526 " --user-agent | -U <str> Set HTTP user agent\n" 527 " --post-data=STRING use the POST method; send STRING as the data\n" 528 " --post-file=FILE use the POST method; send FILE as the data\n" 529 " --spider | -s Spider mode - only check file existence\n" 530 " --timeout=N | -T N Set connect/request timeout to N seconds\n" 531 " --proxy=on | -Y on Enable interpretation of proxy env vars (default)\n" 532 " --proxy=off | -Y off |\n" 533 " --no-proxy Disable interpretation of proxy env vars\n" 534 "\n" 535 "HTTPS options:\n" 536 " --ca-certificate=<cert> Load CA certificates from file <cert>\n" 537 " --no-check-certificate don't validate the server's certificate\n" 538 " --ciphers=<cipherlist> Set the cipher list string\n" 539 "\n", progname); 540 error_ret = 1; 541 } 542 543 static void init_ca_cert(void) 544 { 545 glob_t gl; 546 unsigned int i; 547 548 glob("/etc/ssl/certs/*.crt", 0, NULL, &gl); 549 for (i = 0; i < gl.gl_pathc; i++) 550 ssl_ops->context_add_ca_crt_file(ssl_ctx, gl.gl_pathv[i]); 551 globfree(&gl); 552 } 553 554 static void no_ssl(const char *progname) 555 { 556 fprintf(stderr, 557 "%s: SSL support not available, please install one of the " 558 "libustream-.*[ssl|tls] packages as well as the ca-bundle and " 559 "ca-certificates packages.\n", 560 progname); 561 error_ret = 1; 562 } 563 564 static void debug_cb(void *priv, int level, const char *msg) 565 { 566 fprintf(stderr, "%s\n", msg); 567 } 568 569 static bool is_valid_header(char *str) 570 { 571 char *tmp = str; 572 573 /* First character must be a letter */ 574 if (!isalpha(*tmp)) 575 return false; 576 577 /* Subsequent characters must be letters, numbers or dashes */ 578 while (*(++tmp) != '\0') { 579 if (!isalnum(*tmp) && *tmp != '-') 580 return false; 581 } 582 583 return true; 584 }; 585 586 enum { 587 L_NO_CHECK_CERTIFICATE, 588 L_CA_CERTIFICATE, 589 L_CIPHERS, 590 L_USER, 591 L_PASSWORD, 592 L_USER_AGENT, 593 L_POST_DATA, 594 L_POST_FILE, 595 L_SPIDER, 596 L_TIMEOUT, 597 L_CONTINUE, 598 L_PROXY, 599 L_NO_PROXY, 600 L_QUIET, 601 L_VERBOSE, 602 L_HEADER, 603 }; 604 605 static const struct option longopts[] = { 606 [L_NO_CHECK_CERTIFICATE] = { "no-check-certificate", no_argument, NULL, 0 }, 607 [L_CA_CERTIFICATE] = { "ca-certificate", required_argument, NULL, 0 }, 608 [L_CIPHERS] = { "ciphers", required_argument, NULL, 0 }, 609 [L_USER] = { "user", required_argument, NULL, 0 }, 610 [L_PASSWORD] = { "password", required_argument, NULL, 0 }, 611 [L_USER_AGENT] = { "user-agent", required_argument, NULL, 0 }, 612 [L_POST_DATA] = { "post-data", required_argument, NULL, 0 }, 613 [L_POST_FILE] = { "post-file", required_argument, NULL, 0 }, 614 [L_SPIDER] = { "spider", no_argument, NULL, 0 }, 615 [L_TIMEOUT] = { "timeout", required_argument, NULL, 0 }, 616 [L_CONTINUE] = { "continue", no_argument, NULL, 0 }, 617 [L_PROXY] = { "proxy", required_argument, NULL, 0 }, 618 [L_NO_PROXY] = { "no-proxy", no_argument, NULL, 0 }, 619 [L_QUIET] = { "quiet", no_argument, NULL, 0 }, 620 [L_VERBOSE] = { "verbose", no_argument, NULL, 0 }, 621 [L_HEADER] = { "header", required_argument, NULL, 0 }, 622 {} 623 }; 624 625 626 627 int main(int argc, char **argv) 628 { 629 const char *progname = argv[0]; 630 const char *proxy_url; 631 char *username = NULL; 632 char *password = NULL; 633 struct uclient *cl = NULL; 634 int longopt_idx = 0; 635 bool has_cert = false; 636 struct header *h, *th; 637 char *tmp; 638 int i, ch; 639 int rc; 640 int af = -1; 641 int debug_level = 0; 642 643 signal(SIGPIPE, SIG_IGN); 644 ssl_ctx = uclient_new_ssl_context(&ssl_ops); 645 646 while ((ch = getopt_long(argc, argv, "46cO:P:qsT:U:vY:", longopts, &longopt_idx)) != -1) { 647 switch(ch) { 648 case 0: 649 switch (longopt_idx) { 650 case L_NO_CHECK_CERTIFICATE: 651 verify = false; 652 if (ssl_ctx) 653 ssl_ops->context_set_require_validation(ssl_ctx, verify); 654 break; 655 case L_CA_CERTIFICATE: 656 has_cert = true; 657 if (ssl_ctx) 658 ssl_ops->context_add_ca_crt_file(ssl_ctx, optarg); 659 break; 660 case L_CIPHERS: 661 if (ssl_ctx) { 662 if (ssl_ops->context_set_ciphers(ssl_ctx, optarg)) { 663 if (!quiet) 664 fprintf(stderr, "No recognized ciphers in cipher list\n"); 665 exit(1); 666 } 667 } 668 break; 669 case L_USER: 670 if (!strlen(optarg)) 671 break; 672 username = strdupa(optarg); 673 memset(optarg, '*', strlen(optarg)); 674 break; 675 case L_PASSWORD: 676 if (!strlen(optarg)) 677 break; 678 password = strdupa(optarg); 679 memset(optarg, '*', strlen(optarg)); 680 break; 681 case L_USER_AGENT: 682 user_agent = optarg; 683 break; 684 case L_POST_DATA: 685 post_data = optarg; 686 break; 687 case L_POST_FILE: 688 post_file = optarg; 689 break; 690 case L_SPIDER: 691 no_output = true; 692 break; 693 case L_TIMEOUT: 694 timeout = atoi(optarg); 695 break; 696 case L_CONTINUE: 697 resume = true; 698 break; 699 case L_PROXY: 700 if (strcmp(optarg, "on") != 0) 701 proxy = false; 702 break; 703 case L_NO_PROXY: 704 proxy = false; 705 break; 706 case L_QUIET: 707 quiet = true; 708 break; 709 case L_VERBOSE: 710 debug_level++; 711 break; 712 case L_HEADER: 713 tmp = strchr(optarg, ':'); 714 if (!tmp) { 715 usage(progname); 716 goto out; 717 } 718 *(tmp++) = '\0'; 719 while (isspace(*tmp)) 720 ++tmp; 721 722 if (*tmp == '\0' || !is_valid_header(optarg) || strchr(tmp, '\n')) { 723 usage(progname); 724 goto out; 725 } 726 h = malloc(sizeof(*h)); 727 if (!h) { 728 perror("Set HTTP header"); 729 error_ret = 1; 730 goto out; 731 } 732 h->name = optarg; 733 h->value = tmp; 734 list_add_tail(&h->list, &headers); 735 break; 736 default: 737 usage(progname); 738 goto out; 739 } 740 break; 741 case '4': 742 af = AF_INET; 743 break; 744 case '6': 745 af = AF_INET6; 746 break; 747 case 'c': 748 resume = true; 749 break; 750 case 'U': 751 user_agent = optarg; 752 break; 753 case 'O': 754 opt_output_file = optarg; 755 break; 756 case 'P': 757 if (chdir(optarg)) { 758 if (!quiet) 759 perror("Change output directory"); 760 error_ret = 1; 761 goto out; 762 } 763 break; 764 case 'q': 765 quiet = true; 766 break; 767 case 's': 768 no_output = true; 769 break; 770 case 'T': 771 timeout = atoi(optarg); 772 break; 773 case 'v': 774 debug_level++; 775 break; 776 case 'Y': 777 if (strcmp(optarg, "on") != 0) 778 proxy = false; 779 break; 780 default: 781 usage(progname); 782 goto out; 783 } 784 } 785 786 argv += optind; 787 argc -= optind; 788 789 if (debug_level) 790 ssl_ops->context_set_debug(ssl_ctx, debug_level, debug_cb, NULL); 791 792 if (verify && !has_cert) 793 default_certs = true; 794 795 if (argc < 1) { 796 usage(progname); 797 goto out; 798 } 799 800 if (!ssl_ctx) { 801 for (i = 0; i < argc; i++) { 802 if (!strncmp(argv[i], "https", 5)) { 803 no_ssl(progname); 804 goto out; 805 } 806 } 807 } 808 809 urls = argv + 1; 810 n_urls = argc - 1; 811 812 uloop_init(); 813 814 if (username) { 815 if (password) { 816 rc = asprintf(&auth_str, "%s:%s", username, password); 817 if (rc < 0) { 818 error_ret = 1; 819 goto out; 820 } 821 } else 822 auth_str = username; 823 } 824 825 if (!quiet) 826 fprintf(stderr, "Downloading '%s'\n", argv[0]); 827 828 proxy_url = get_proxy_url(argv[0]); 829 if (proxy_url) { 830 cl = uclient_new(proxy_url, auth_str, &cb); 831 if (cl) 832 uclient_set_proxy_url(cl, argv[0], NULL); 833 } else { 834 cl = uclient_new(argv[0], auth_str, &cb); 835 } 836 if (!cl) { 837 fprintf(stderr, "Failed to allocate uclient context\n"); 838 error_ret = 1; 839 goto out; 840 } 841 if (af >= 0) 842 uclient_http_set_address_family(cl, af); 843 844 if (ssl_ctx && default_certs) 845 init_ca_cert(); 846 847 cur_resume = resume; 848 rc = init_request(cl); 849 if (!rc) { 850 /* no error received, we can enter main loop */ 851 uloop_run(); 852 } else { 853 fprintf(stderr, "Failed to send request: %s\n", strerror(rc)); 854 error_ret = 4; 855 goto out; 856 } 857 858 uloop_done(); 859 860 out: 861 if (cl) 862 uclient_free(cl); 863 864 if (output_fd >= 0 && output_fd != STDOUT_FILENO) 865 close(output_fd); 866 867 if (ssl_ctx) 868 ssl_ops->context_free(ssl_ctx); 869 870 list_for_each_entry_safe(h, th, &headers, list) { 871 list_del(&h->list); 872 free(h); 873 } 874 875 return error_ret; 876 } 877
This page was automatically generated by LXR 0.3.1. • OpenWrt