1 /* $OpenBSD: fetch.c,v 1.109 2013/04/09 08:58:46 sthen Exp $ */ 2 /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */ 3 4 /*- 5 * Copyright (c) 1997 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason Thorpe and Luke Mewburn. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * FTP User Program -- Command line file retrieval 35 */ 36 37 #include <sys/types.h> 38 #include <sys/param.h> 39 #include <sys/socket.h> 40 #include <sys/stat.h> 41 42 #include <netinet/in.h> 43 44 #include <arpa/ftp.h> 45 #include <arpa/inet.h> 46 47 #include <ctype.h> 48 #include <err.h> 49 #include <libgen.h> 50 #include <limits.h> 51 #include <netdb.h> 52 #include <fcntl.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdarg.h> 56 #include <errno.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <util.h> 61 #include <resolv.h> 62 63 #ifndef SMALL 64 #include <openssl/ssl.h> 65 #include <openssl/err.h> 66 #else /* !SMALL */ 67 #define SSL void 68 #endif /* !SMALL */ 69 70 #include "ftp_var.h" 71 #include "cmds.h" 72 73 static int url_get(const char *, const char *, const char *); 74 void aborthttp(int); 75 void abortfile(int); 76 char hextochar(const char *); 77 char *urldecode(const char *); 78 int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4))); 79 char *ftp_readline(FILE *, SSL *, size_t *); 80 size_t ftp_read(FILE *, SSL *, char *, size_t); 81 #ifndef SMALL 82 int proxy_connect(int, char *, char *); 83 int SSL_vprintf(SSL *, const char *, va_list); 84 char *SSL_readline(SSL *, size_t *); 85 #endif /* !SMALL */ 86 87 #define FTP_URL "ftp://" /* ftp URL prefix */ 88 #define HTTP_URL "http://" /* http URL prefix */ 89 #define HTTPS_URL "https://" /* https URL prefix */ 90 #define FILE_URL "file:" /* file URL prefix */ 91 #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ 92 #define HTTP_PROXY "http_proxy" /* env var with http proxy location */ 93 94 #define COOKIE_MAX_LEN 42 95 96 #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) 97 98 static const char *at_encoding_warning = 99 "Extra `@' characters in usernames and passwords should be encoded as %%40"; 100 101 jmp_buf httpabort; 102 103 static int redirect_loop; 104 105 /* 106 * Determine whether the character needs encoding, per RFC1738: 107 * - No corresponding graphic US-ASCII. 108 * - Unsafe characters. 109 */ 110 static int 111 unsafe_char(const char *c) 112 { 113 const char *unsafe_chars = " <>\"#{}|\\^~[]`"; 114 115 /* 116 * No corresponding graphic US-ASCII. 117 * Control characters and octets not used in US-ASCII. 118 */ 119 return (iscntrl(*c) || !isascii(*c) || 120 121 /* 122 * Unsafe characters. 123 * '%' is also unsafe, if is not followed by two 124 * hexadecimal digits. 125 */ 126 strchr(unsafe_chars, *c) != NULL || 127 (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c)))); 128 } 129 130 /* 131 * Encode given URL, per RFC1738. 132 * Allocate and return string to the caller. 133 */ 134 static char * 135 url_encode(const char *path) 136 { 137 size_t i, length, new_length; 138 char *epath, *epathp; 139 140 length = new_length = strlen(path); 141 142 /* 143 * First pass: 144 * Count unsafe characters, and determine length of the 145 * final URL. 146 */ 147 for (i = 0; i < length; i++) 148 if (unsafe_char(path + i)) 149 new_length += 2; 150 151 epath = epathp = malloc(new_length + 1); /* One more for '\0'. */ 152 if (epath == NULL) 153 err(1, "Can't allocate memory for URL encoding"); 154 155 /* 156 * Second pass: 157 * Encode, and copy final URL. 158 */ 159 for (i = 0; i < length; i++) 160 if (unsafe_char(path + i)) { 161 snprintf(epathp, 4, "%%" "%02x", path[i]); 162 epathp += 3; 163 } else 164 *(epathp++) = path[i]; 165 166 *epathp = '\0'; 167 return (epath); 168 } 169 170 /* 171 * Retrieve URL, via the proxy in $proxyvar if necessary. 172 * Modifies the string argument given. 173 * Returns -1 on failure, 0 on success 174 */ 175 static int 176 url_get(const char *origline, const char *proxyenv, const char *outfile) 177 { 178 char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4]; 179 char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL; 180 char *epath, *redirurl, *loctail, *h, *p; 181 int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1; 182 struct addrinfo hints, *res0, *res, *ares = NULL; 183 const char * volatile savefile; 184 char * volatile proxyurl = NULL; 185 char *cookie = NULL; 186 volatile int s = -1, out; 187 volatile sig_t oldintr, oldinti; 188 FILE *fin = NULL; 189 off_t hashbytes; 190 const char *errstr; 191 size_t len, wlen; 192 #ifndef SMALL 193 char *sslpath = NULL, *sslhost = NULL; 194 char *locbase, *full_host = NULL, *auth = NULL; 195 const char *scheme; 196 int ishttpsurl = 0; 197 SSL_CTX *ssl_ctx = NULL; 198 #endif /* !SMALL */ 199 SSL *ssl = NULL; 200 int status; 201 int save_errno; 202 203 direction = "received"; 204 205 newline = strdup(origline); 206 if (newline == NULL) 207 errx(1, "Can't allocate memory to parse URL"); 208 if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { 209 host = newline + sizeof(HTTP_URL) - 1; 210 #ifndef SMALL 211 scheme = HTTP_URL; 212 #endif /* !SMALL */ 213 } else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) { 214 host = newline + sizeof(FTP_URL) - 1; 215 isftpurl = 1; 216 #ifndef SMALL 217 scheme = FTP_URL; 218 #endif /* !SMALL */ 219 } else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) { 220 host = newline + sizeof(FILE_URL) - 1; 221 isfileurl = 1; 222 #ifndef SMALL 223 scheme = FILE_URL; 224 } else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) { 225 host = newline + sizeof(HTTPS_URL) - 1; 226 ishttpsurl = 1; 227 scheme = HTTPS_URL; 228 #endif /* !SMALL */ 229 } else 230 errx(1, "url_get: Invalid URL '%s'", newline); 231 232 if (isfileurl) { 233 path = host; 234 } else { 235 path = strchr(host, '/'); /* Find path */ 236 if (EMPTYSTRING(path)) { 237 if (outfile) { /* No slash, but */ 238 path=strchr(host,'\0'); /* we have outfile. */ 239 goto noslash; 240 } 241 if (isftpurl) 242 goto noftpautologin; 243 warnx("No `/' after host (use -o): %s", origline); 244 goto cleanup_url_get; 245 } 246 *path++ = '\0'; 247 if (EMPTYSTRING(path) && !outfile) { 248 if (isftpurl) 249 goto noftpautologin; 250 warnx("No filename after host (use -o): %s", origline); 251 goto cleanup_url_get; 252 } 253 } 254 255 noslash: 256 257 #ifndef SMALL 258 /* 259 * Look for auth header in host, since now host does not 260 * contain the path. Basic auth from RFC 2617, valid 261 * characters for path are in RFC 3986 section 3.3. 262 */ 263 if (proxyenv == NULL && 264 (!strcmp(scheme, HTTP_URL) || !strcmp(scheme, HTTPS_URL))) { 265 if ((p = strchr(host, '@')) != NULL) { 266 size_t authlen = (strlen(host) + 5) * 4 / 3; 267 *p = 0; /* Kill @ */ 268 if ((auth = malloc(authlen)) == NULL) 269 err(1, "Can't allocate memory for " 270 "authorization"); 271 if (b64_ntop(host, strlen(host), 272 auth, authlen) == -1) 273 errx(1, "error in base64 encoding"); 274 host = p + 1; 275 } 276 } 277 #endif /* SMALL */ 278 279 if (outfile) 280 savefile = outfile; 281 else { 282 if (path[strlen(path) - 1] == '/') /* Consider no file */ 283 savefile = NULL; /* after dir invalid. */ 284 else 285 savefile = basename(path); 286 } 287 288 if (EMPTYSTRING(savefile)) { 289 if (isftpurl) 290 goto noftpautologin; 291 warnx("No filename after directory (use -o): %s", origline); 292 goto cleanup_url_get; 293 } 294 295 #ifndef SMALL 296 if (resume && pipeout) { 297 warnx("can't append to stdout"); 298 goto cleanup_url_get; 299 } 300 #endif /* !SMALL */ 301 302 if (!isfileurl && proxyenv != NULL) { /* use proxy */ 303 #ifndef SMALL 304 if (ishttpsurl) { 305 sslpath = strdup(path); 306 sslhost = strdup(host); 307 if (! sslpath || ! sslhost) 308 errx(1, "Can't allocate memory for https path/host."); 309 } 310 #endif /* !SMALL */ 311 proxyurl = strdup(proxyenv); 312 if (proxyurl == NULL) 313 errx(1, "Can't allocate memory for proxy URL."); 314 if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) 315 host = proxyurl + sizeof(HTTP_URL) - 1; 316 else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0) 317 host = proxyurl + sizeof(FTP_URL) - 1; 318 else { 319 warnx("Malformed proxy URL: %s", proxyenv); 320 goto cleanup_url_get; 321 } 322 if (EMPTYSTRING(host)) { 323 warnx("Malformed proxy URL: %s", proxyenv); 324 goto cleanup_url_get; 325 } 326 if (*--path == '\0') 327 *path = '/'; /* add / back to real path */ 328 path = strchr(host, '/'); /* remove trailing / on host */ 329 if (!EMPTYSTRING(path)) 330 *path++ = '\0'; /* i guess this ++ is useless */ 331 332 path = strchr(host, '@'); /* look for credentials in proxy */ 333 if (!EMPTYSTRING(path)) { 334 *path = '\0'; 335 cookie = strchr(host, ':'); 336 if (EMPTYSTRING(cookie)) { 337 warnx("Malformed proxy URL: %s", proxyenv); 338 goto cleanup_url_get; 339 } 340 cookie = malloc(COOKIE_MAX_LEN); 341 if (cookie == NULL) 342 errx(1, "out of memory"); 343 if (b64_ntop(host, strlen(host), cookie, COOKIE_MAX_LEN) == -1) 344 errx(1, "error in base64 encoding"); 345 *path = '@'; /* restore @ in proxyurl */ 346 /* 347 * This removes the password from proxyurl, 348 * filling with stars 349 */ 350 for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@'; 351 host++) 352 *host = '*'; 353 354 host = path + 1; 355 } 356 path = newline; 357 } 358 359 if (isfileurl) { 360 struct stat st; 361 362 s = open(path, O_RDONLY); 363 if (s == -1) { 364 warn("Can't open file %s", path); 365 goto cleanup_url_get; 366 } 367 368 if (fstat(s, &st) == -1) 369 filesize = -1; 370 else 371 filesize = st.st_size; 372 373 /* Open the output file. */ 374 if (!pipeout) { 375 #ifndef SMALL 376 if (resume) 377 out = open(savefile, O_CREAT | O_WRONLY | 378 O_APPEND, 0666); 379 380 else 381 #endif /* !SMALL */ 382 out = open(savefile, O_CREAT | O_WRONLY | 383 O_TRUNC, 0666); 384 if (out < 0) { 385 warn("Can't open %s", savefile); 386 goto cleanup_url_get; 387 } 388 } else 389 out = fileno(stdout); 390 391 #ifndef SMALL 392 if (resume) { 393 if (fstat(out, &st) == -1) { 394 warn("Can't fstat %s", savefile); 395 goto cleanup_url_get; 396 } 397 if (lseek(s, st.st_size, SEEK_SET) == -1) { 398 warn("Can't lseek %s", path); 399 goto cleanup_url_get; 400 } 401 restart_point = st.st_size; 402 } 403 #endif /* !SMALL */ 404 405 /* Trap signals */ 406 oldintr = NULL; 407 oldinti = NULL; 408 if (setjmp(httpabort)) { 409 if (oldintr) 410 (void)signal(SIGINT, oldintr); 411 if (oldinti) 412 (void)signal(SIGINFO, oldinti); 413 goto cleanup_url_get; 414 } 415 oldintr = signal(SIGINT, abortfile); 416 417 bytes = 0; 418 hashbytes = mark; 419 progressmeter(-1, path); 420 421 if ((buf = malloc(4096)) == NULL) 422 errx(1, "Can't allocate memory for transfer buffer"); 423 424 /* Finally, suck down the file. */ 425 i = 0; 426 oldinti = signal(SIGINFO, psummary); 427 while ((len = read(s, buf, 4096)) > 0) { 428 bytes += len; 429 for (cp = buf; len > 0; len -= i, cp += i) { 430 if ((i = write(out, cp, len)) == -1) { 431 warn("Writing %s", savefile); 432 signal(SIGINFO, oldinti); 433 goto cleanup_url_get; 434 } 435 else if (i == 0) 436 break; 437 } 438 if (hash && !progress) { 439 while (bytes >= hashbytes) { 440 (void)putc('#', ttyout); 441 hashbytes += mark; 442 } 443 (void)fflush(ttyout); 444 } 445 } 446 signal(SIGINFO, oldinti); 447 if (hash && !progress && bytes > 0) { 448 if (bytes < mark) 449 (void)putc('#', ttyout); 450 (void)putc('\n', ttyout); 451 (void)fflush(ttyout); 452 } 453 if (len != 0) { 454 warn("Reading from file"); 455 goto cleanup_url_get; 456 } 457 progressmeter(1, NULL); 458 if (verbose) 459 ptransfer(0); 460 (void)signal(SIGINT, oldintr); 461 462 rval = 0; 463 goto cleanup_url_get; 464 } 465 466 if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && 467 (hosttail[1] == '\0' || hosttail[1] == ':')) { 468 host++; 469 *hosttail++ = '\0'; 470 #ifndef SMALL 471 if (asprintf(&full_host, "[%s]", host) == -1) 472 errx(1, "Cannot allocate memory for hostname"); 473 #endif /* !SMALL */ 474 } else 475 hosttail = host; 476 477 portnum = strrchr(hosttail, ':'); /* find portnum */ 478 if (portnum != NULL) 479 *portnum++ = '\0'; 480 481 #ifndef SMALL 482 if (full_host == NULL) 483 if ((full_host = strdup(host)) == NULL) 484 errx(1, "Cannot allocate memory for hostname"); 485 if (debug) 486 fprintf(ttyout, "host %s, port %s, path %s, " 487 "save as %s, auth %s.\n", 488 host, portnum, path, savefile, auth); 489 #endif /* !SMALL */ 490 491 memset(&hints, 0, sizeof(hints)); 492 hints.ai_family = family; 493 hints.ai_socktype = SOCK_STREAM; 494 #ifndef SMALL 495 port = portnum ? portnum : (ishttpsurl ? httpsport : httpport); 496 #else /* !SMALL */ 497 port = portnum ? portnum : httpport; 498 #endif /* !SMALL */ 499 error = getaddrinfo(host, port, &hints, &res0); 500 /* 501 * If the services file is corrupt/missing, fall back 502 * on our hard-coded defines. 503 */ 504 if (error == EAI_SERVICE && port == httpport) { 505 snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); 506 error = getaddrinfo(host, pbuf, &hints, &res0); 507 #ifndef SMALL 508 } else if (error == EAI_SERVICE && port == httpsport) { 509 snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); 510 error = getaddrinfo(host, pbuf, &hints, &res0); 511 #endif /* !SMALL */ 512 } 513 if (error) { 514 warnx("%s: %s", gai_strerror(error), host); 515 goto cleanup_url_get; 516 } 517 518 #ifndef SMALL 519 if (srcaddr) { 520 hints.ai_flags |= AI_NUMERICHOST; 521 error = getaddrinfo(srcaddr, NULL, &hints, &ares); 522 if (error) { 523 warnx("%s: %s", gai_strerror(error), srcaddr); 524 goto cleanup_url_get; 525 } 526 } 527 #endif /* !SMALL */ 528 529 s = -1; 530 for (res = res0; res; res = res->ai_next) { 531 if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, 532 sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) 533 strlcpy(hbuf, "(unknown)", sizeof(hbuf)); 534 if (verbose) 535 fprintf(ttyout, "Trying %s...\n", hbuf); 536 537 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 538 if (s == -1) { 539 cause = "socket"; 540 continue; 541 } 542 543 #ifndef SMALL 544 if (srcaddr) { 545 if (ares->ai_family != res->ai_family) { 546 close(s); 547 s = -1; 548 errno = EINVAL; 549 cause = "bind"; 550 continue; 551 } 552 if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) { 553 save_errno = errno; 554 close(s); 555 errno = save_errno; 556 s = -1; 557 cause = "bind"; 558 continue; 559 } 560 } 561 #endif /* !SMALL */ 562 563 again: 564 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 565 if (errno == EINTR) 566 goto again; 567 save_errno = errno; 568 close(s); 569 errno = save_errno; 570 s = -1; 571 cause = "connect"; 572 continue; 573 } 574 575 /* get port in numeric */ 576 if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0, 577 pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0) 578 port = pbuf; 579 else 580 port = NULL; 581 582 #ifndef SMALL 583 if (proxyenv && sslhost) 584 proxy_connect(s, sslhost, cookie); 585 #endif /* !SMALL */ 586 break; 587 } 588 freeaddrinfo(res0); 589 #ifndef SMALL 590 if (srcaddr) 591 freeaddrinfo(ares); 592 #endif /* !SMALL */ 593 if (s < 0) { 594 warn("%s", cause); 595 goto cleanup_url_get; 596 } 597 598 #ifndef SMALL 599 if (ishttpsurl) { 600 if (proxyenv && sslpath) { 601 ishttpsurl = 0; 602 proxyurl = NULL; 603 path = sslpath; 604 } 605 SSL_library_init(); 606 SSL_load_error_strings(); 607 SSLeay_add_ssl_algorithms(); 608 ssl_ctx = SSL_CTX_new(SSLv23_client_method()); 609 ssl = SSL_new(ssl_ctx); 610 if (ssl == NULL || ssl_ctx == NULL) { 611 ERR_print_errors_fp(ttyout); 612 goto cleanup_url_get; 613 } 614 if (SSL_set_fd(ssl, s) == 0) { 615 ERR_print_errors_fp(ttyout); 616 goto cleanup_url_get; 617 } 618 if (SSL_connect(ssl) <= 0) { 619 ERR_print_errors_fp(ttyout); 620 goto cleanup_url_get; 621 } 622 } else { 623 fin = fdopen(s, "r+"); 624 } 625 #else /* !SMALL */ 626 fin = fdopen(s, "r+"); 627 #endif /* !SMALL */ 628 629 if (verbose) 630 fprintf(ttyout, "Requesting %s", origline); 631 632 /* 633 * Construct and send the request. Proxy requests don't want leading /. 634 */ 635 #ifndef SMALL 636 cookie_get(host, path, ishttpsurl, &buf); 637 #endif /* !SMALL */ 638 639 epath = url_encode(path); 640 if (proxyurl) { 641 if (verbose) 642 fprintf(ttyout, " (via %s)\n", proxyurl); 643 /* 644 * Host: directive must use the destination host address for 645 * the original URI (path). We do not attach it at this moment. 646 */ 647 if (cookie) 648 ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n" 649 "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n", 650 epath, cookie, buf ? buf : "", HTTP_USER_AGENT); 651 else 652 ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n", 653 epath, buf ? buf : "", HTTP_USER_AGENT); 654 655 } else { 656 #ifndef SMALL 657 if (resume) { 658 struct stat stbuf; 659 660 if (stat(savefile, &stbuf) == 0) 661 restart_point = stbuf.st_size; 662 else 663 restart_point = 0; 664 } 665 if (auth) { 666 ftp_printf(fin, ssl, 667 "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ", 668 epath, restart_point ? 669 "HTTP/1.1\r\nConnection: close" : "HTTP/1.0", 670 auth); 671 free(auth); 672 auth = NULL; 673 } else 674 #endif /* SMALL */ 675 ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath, 676 #ifndef SMALL 677 restart_point ? "HTTP/1.1\r\nConnection: close" : 678 #endif /* !SMALL */ 679 "HTTP/1.0"); 680 if (strchr(host, ':')) { 681 /* 682 * strip off scoped address portion, since it's 683 * local to node 684 */ 685 h = strdup(host); 686 if (h == NULL) 687 errx(1, "Can't allocate memory."); 688 if ((p = strchr(h, '%')) != NULL) 689 *p = '\0'; 690 ftp_printf(fin, ssl, "[%s]", h); 691 free(h); 692 } else 693 ftp_printf(fin, ssl, "%s", host); 694 695 /* 696 * Send port number only if it's specified and does not equal 697 * 80. Some broken HTTP servers get confused if you explicitly 698 * send them the port number. 699 */ 700 #ifndef SMALL 701 if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0) 702 ftp_printf(fin, ssl, ":%s", port); 703 if (restart_point) 704 ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-", 705 (long long)restart_point); 706 #else /* !SMALL */ 707 if (port && strcmp(port, "80") != 0) 708 ftp_printf(fin, ssl, ":%s", port); 709 #endif /* !SMALL */ 710 ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n", 711 buf ? buf : "", HTTP_USER_AGENT); 712 if (verbose) 713 fprintf(ttyout, "\n"); 714 } 715 free(epath); 716 717 #ifndef SMALL 718 free(buf); 719 #endif /* !SMALL */ 720 buf = NULL; 721 722 if (fin != NULL && fflush(fin) == EOF) { 723 warn("Writing HTTP request"); 724 goto cleanup_url_get; 725 } 726 if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { 727 warn("Receiving HTTP reply"); 728 goto cleanup_url_get; 729 } 730 731 while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) 732 buf[--len] = '\0'; 733 #ifndef SMALL 734 if (debug) 735 fprintf(ttyout, "received '%s'\n", buf); 736 #endif /* !SMALL */ 737 738 cp = strchr(buf, ' '); 739 if (cp == NULL) 740 goto improper; 741 else 742 cp++; 743 744 strlcpy(ststr, cp, sizeof(ststr)); 745 status = strtonum(ststr, 200, 416, &errstr); 746 if (errstr) { 747 warnx("Error retrieving file: %s", cp); 748 goto cleanup_url_get; 749 } 750 751 switch (status) { 752 case 200: /* OK */ 753 #ifndef SMALL 754 /* 755 * When we request a partial file, and we receive an HTTP 200 756 * it is a good indication that the server doesn't support 757 * range requests, and is about to send us the entire file. 758 * If the restart_point == 0, then we are not actually 759 * requesting a partial file, and an HTTP 200 is appropriate. 760 */ 761 if (resume && restart_point != 0) { 762 warnx("Server does not support resume."); 763 restart_point = resume = 0; 764 } 765 /* FALLTHROUGH */ 766 case 206: /* Partial Content */ 767 #endif /* !SMALL */ 768 break; 769 case 301: /* Moved Permanently */ 770 case 302: /* Found */ 771 case 303: /* See Other */ 772 case 307: /* Temporary Redirect */ 773 isredirect++; 774 if (redirect_loop++ > 10) { 775 warnx("Too many redirections requested"); 776 goto cleanup_url_get; 777 } 778 break; 779 #ifndef SMALL 780 case 416: /* Requested Range Not Satisfiable */ 781 warnx("File is already fully retrieved."); 782 goto cleanup_url_get; 783 #endif /* !SMALL */ 784 default: 785 warnx("Error retrieving file: %s", cp); 786 goto cleanup_url_get; 787 } 788 789 /* 790 * Read the rest of the header. 791 */ 792 free(buf); 793 filesize = -1; 794 795 for (;;) { 796 if ((buf = ftp_readline(fin, ssl, &len)) == NULL) { 797 warn("Receiving HTTP reply"); 798 goto cleanup_url_get; 799 } 800 801 while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) 802 buf[--len] = '\0'; 803 if (len == 0) 804 break; 805 #ifndef SMALL 806 if (debug) 807 fprintf(ttyout, "received '%s'\n", buf); 808 #endif /* !SMALL */ 809 810 /* Look for some headers */ 811 cp = buf; 812 #define CONTENTLEN "Content-Length: " 813 if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) { 814 size_t s; 815 cp += sizeof(CONTENTLEN) - 1; 816 if (s=strcspn(cp, " \t")) 817 *(cp+s) = 0; 818 filesize = strtonum(cp, 0, LLONG_MAX, &errstr); 819 if (errstr != NULL) 820 goto improper; 821 #ifndef SMALL 822 if (restart_point) 823 filesize += restart_point; 824 #endif /* !SMALL */ 825 #define LOCATION "Location: " 826 } else if (isredirect && 827 strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) { 828 cp += sizeof(LOCATION) - 1; 829 if (strstr(cp, "://") == NULL) { 830 #ifdef SMALL 831 errx(1, "Relative redirect not supported"); 832 #else /* SMALL */ 833 if (*cp == '/') { 834 locbase = NULL; 835 cp++; 836 } else { 837 locbase = strdup(path); 838 if (locbase == NULL) 839 errx(1, "Can't allocate memory" 840 " for location base"); 841 loctail = strchr(locbase, '#'); 842 if (loctail != NULL) 843 *loctail = '\0'; 844 loctail = strchr(locbase, '?'); 845 if (loctail != NULL) 846 *loctail = '\0'; 847 loctail = strrchr(locbase, '/'); 848 if (loctail == NULL) { 849 free(locbase); 850 locbase = NULL; 851 } else 852 loctail[1] = '\0'; 853 } 854 /* Contruct URL from relative redirect */ 855 if (asprintf(&redirurl, "%s%s%s%s/%s%s", 856 scheme, full_host, 857 portnum ? ":" : "", 858 portnum ? portnum : "", 859 locbase ? locbase : "", 860 cp) == -1) 861 errx(1, "Cannot build " 862 "redirect URL"); 863 free(locbase); 864 #endif /* SMALL */ 865 } else if ((redirurl = strdup(cp)) == NULL) 866 errx(1, "Cannot allocate memory for URL"); 867 loctail = strchr(redirurl, '#'); 868 if (loctail != NULL) 869 *loctail = '\0'; 870 if (verbose) 871 fprintf(ttyout, "Redirected to %s\n", redirurl); 872 if (fin != NULL) 873 fclose(fin); 874 else if (s != -1) 875 close(s); 876 rval = url_get(redirurl, proxyenv, savefile); 877 free(redirurl); 878 goto cleanup_url_get; 879 } 880 free(buf); 881 } 882 883 /* Open the output file. */ 884 if (!pipeout) { 885 #ifndef SMALL 886 if (resume) 887 out = open(savefile, O_CREAT | O_WRONLY | O_APPEND, 888 0666); 889 else 890 #endif /* !SMALL */ 891 out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 892 0666); 893 if (out < 0) { 894 warn("Can't open %s", savefile); 895 goto cleanup_url_get; 896 } 897 } else 898 out = fileno(stdout); 899 900 /* Trap signals */ 901 oldintr = NULL; 902 oldinti = NULL; 903 if (setjmp(httpabort)) { 904 if (oldintr) 905 (void)signal(SIGINT, oldintr); 906 if (oldinti) 907 (void)signal(SIGINFO, oldinti); 908 goto cleanup_url_get; 909 } 910 oldintr = signal(SIGINT, aborthttp); 911 912 bytes = 0; 913 hashbytes = mark; 914 progressmeter(-1, path); 915 916 free(buf); 917 918 /* Finally, suck down the file. */ 919 if ((buf = malloc(4096)) == NULL) 920 errx(1, "Can't allocate memory for transfer buffer"); 921 i = 0; 922 len = 1; 923 oldinti = signal(SIGINFO, psummary); 924 while (len > 0) { 925 len = ftp_read(fin, ssl, buf, 4096); 926 bytes += len; 927 for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { 928 if ((i = write(out, cp, wlen)) == -1) { 929 warn("Writing %s", savefile); 930 signal(SIGINFO, oldinti); 931 goto cleanup_url_get; 932 } 933 else if (i == 0) 934 break; 935 } 936 if (hash && !progress) { 937 while (bytes >= hashbytes) { 938 (void)putc('#', ttyout); 939 hashbytes += mark; 940 } 941 (void)fflush(ttyout); 942 } 943 } 944 signal(SIGINFO, oldinti); 945 if (hash && !progress && bytes > 0) { 946 if (bytes < mark) 947 (void)putc('#', ttyout); 948 (void)putc('\n', ttyout); 949 (void)fflush(ttyout); 950 } 951 if (len != 0) { 952 warn("Reading from socket"); 953 goto cleanup_url_get; 954 } 955 progressmeter(1, NULL); 956 if ( 957 #ifndef SMALL 958 !resume && 959 #endif /* !SMALL */ 960 filesize != -1 && len == 0 && bytes != filesize) { 961 if (verbose) 962 fputs("Read short file.\n", ttyout); 963 goto cleanup_url_get; 964 } 965 966 if (verbose) 967 ptransfer(0); 968 (void)signal(SIGINT, oldintr); 969 970 rval = 0; 971 goto cleanup_url_get; 972 973 noftpautologin: 974 warnx( 975 "Auto-login using ftp URLs isn't supported when using $ftp_proxy"); 976 goto cleanup_url_get; 977 978 improper: 979 warnx("Improper response from %s", host); 980 981 cleanup_url_get: 982 #ifndef SMALL 983 if (ssl) { 984 SSL_shutdown(ssl); 985 SSL_free(ssl); 986 } 987 free(full_host); 988 free(auth); 989 #endif /* !SMALL */ 990 if (fin != NULL) 991 fclose(fin); 992 else if (s != -1) 993 close(s); 994 free(buf); 995 free(proxyurl); 996 free(newline); 997 free(cookie); 998 return (rval); 999 } 1000 1001 /* 1002 * Abort a http retrieval 1003 */ 1004 /* ARGSUSED */ 1005 void 1006 aborthttp(int signo) 1007 { 1008 1009 alarmtimer(0); 1010 fputs("\nhttp fetch aborted.\n", ttyout); 1011 (void)fflush(ttyout); 1012 longjmp(httpabort, 1); 1013 } 1014 1015 /* 1016 * Abort a http retrieval 1017 */ 1018 /* ARGSUSED */ 1019 void 1020 abortfile(int signo) 1021 { 1022 1023 alarmtimer(0); 1024 fputs("\nfile fetch aborted.\n", ttyout); 1025 (void)fflush(ttyout); 1026 longjmp(httpabort, 1); 1027 } 1028 1029 /* 1030 * Retrieve multiple files from the command line, transferring 1031 * files of the form "host:path", "ftp://host/path" using the 1032 * ftp protocol, and files of the form "http://host/path" using 1033 * the http protocol. 1034 * If path has a trailing "/", then return (-1); 1035 * the path will be cd-ed into and the connection remains open, 1036 * and the function will return -1 (to indicate the connection 1037 * is alive). 1038 * If an error occurs the return value will be the offset+1 in 1039 * argv[] of the file that caused a problem (i.e, argv[x] 1040 * returns x+1) 1041 * Otherwise, 0 is returned if all files retrieved successfully. 1042 */ 1043 int 1044 auto_fetch(int argc, char *argv[], char *outfile) 1045 { 1046 char *xargv[5]; 1047 char *cp, *url, *host, *dir, *file, *portnum; 1048 char *username, *pass, *pathstart; 1049 char *ftpproxy, *httpproxy; 1050 int rval, xargc; 1051 volatile int argpos; 1052 int dirhasglob, filehasglob, oautologin; 1053 char rempath[MAXPATHLEN]; 1054 1055 argpos = 0; 1056 1057 if (setjmp(toplevel)) { 1058 if (connected) 1059 disconnect(0, NULL); 1060 return (argpos + 1); 1061 } 1062 (void)signal(SIGINT, (sig_t)intr); 1063 (void)signal(SIGPIPE, (sig_t)lostpeer); 1064 1065 if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0') 1066 ftpproxy = NULL; 1067 if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0') 1068 httpproxy = NULL; 1069 1070 /* 1071 * Loop through as long as there's files to fetch. 1072 */ 1073 for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) { 1074 if (strchr(argv[argpos], ':') == NULL) 1075 break; 1076 host = dir = file = portnum = username = pass = NULL; 1077 1078 /* 1079 * We muck with the string, so we make a copy. 1080 */ 1081 url = strdup(argv[argpos]); 1082 if (url == NULL) 1083 errx(1, "Can't allocate memory for auto-fetch."); 1084 1085 /* 1086 * Try HTTP URL-style arguments first. 1087 */ 1088 if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || 1089 #ifndef SMALL 1090 /* even if we compiled without SSL, url_get will check */ 1091 strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 || 1092 #endif /* !SMALL */ 1093 strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) { 1094 redirect_loop = 0; 1095 if (url_get(url, httpproxy, outfile) == -1) 1096 rval = argpos + 1; 1097 continue; 1098 } 1099 1100 /* 1101 * Try FTP URL-style arguments next. If ftpproxy is 1102 * set, use url_get() instead of standard ftp. 1103 * Finally, try host:file. 1104 */ 1105 host = url; 1106 if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) { 1107 char *passend, *passagain, *userend; 1108 1109 if (ftpproxy) { 1110 if (url_get(url, ftpproxy, outfile) == -1) 1111 rval = argpos + 1; 1112 continue; 1113 } 1114 host += sizeof(FTP_URL) - 1; 1115 dir = strchr(host, '/'); 1116 1117 /* Look for [user:pass@]host[:port] */ 1118 1119 /* check if we have "user:pass@" */ 1120 userend = strchr(host, ':'); 1121 passend = strchr(host, '@'); 1122 if (passend && userend && userend < passend && 1123 (!dir || passend < dir)) { 1124 username = host; 1125 pass = userend + 1; 1126 host = passend + 1; 1127 *userend = *passend = '\0'; 1128 passagain = strchr(host, '@'); 1129 if (strchr(pass, '@') != NULL || 1130 (passagain != NULL && passagain < dir)) { 1131 warnx(at_encoding_warning); 1132 goto bad_ftp_url; 1133 } 1134 1135 if (EMPTYSTRING(username)) { 1136 bad_ftp_url: 1137 warnx("Invalid URL: %s", argv[argpos]); 1138 rval = argpos + 1; 1139 continue; 1140 } 1141 username = urldecode(username); 1142 pass = urldecode(pass); 1143 } 1144 1145 #ifdef INET6 1146 /* check [host]:port, or [host] */ 1147 if (host[0] == '[') { 1148 cp = strchr(host, ']'); 1149 if (cp && (!dir || cp < dir)) { 1150 if (cp + 1 == dir || cp[1] == ':') { 1151 host++; 1152 *cp++ = '\0'; 1153 } else 1154 cp = NULL; 1155 } else 1156 cp = host; 1157 } else 1158 cp = host; 1159 #else 1160 cp = host; 1161 #endif 1162 1163 /* split off host[:port] if there is */ 1164 if (cp) { 1165 portnum = strchr(cp, ':'); 1166 pathstart = strchr(cp, '/'); 1167 /* : in path is not a port # indicator */ 1168 if (portnum && pathstart && 1169 pathstart < portnum) 1170 portnum = NULL; 1171 1172 if (!portnum) 1173 ; 1174 else { 1175 if (!dir) 1176 ; 1177 else if (portnum + 1 < dir) { 1178 *portnum++ = '\0'; 1179 /* 1180 * XXX should check if portnum 1181 * is decimal number 1182 */ 1183 } else { 1184 /* empty portnum */ 1185 goto bad_ftp_url; 1186 } 1187 } 1188 } else 1189 portnum = NULL; 1190 } else { /* classic style `host:file' */ 1191 dir = strchr(host, ':'); 1192 } 1193 if (EMPTYSTRING(host)) { 1194 rval = argpos + 1; 1195 continue; 1196 } 1197 1198 /* 1199 * If dir is NULL, the file wasn't specified 1200 * (URL looked something like ftp://host) 1201 */ 1202 if (dir != NULL) 1203 *dir++ = '\0'; 1204 1205 /* 1206 * Extract the file and (if present) directory name. 1207 */ 1208 if (!EMPTYSTRING(dir)) { 1209 cp = strrchr(dir, '/'); 1210 if (cp != NULL) { 1211 *cp++ = '\0'; 1212 file = cp; 1213 } else { 1214 file = dir; 1215 dir = NULL; 1216 } 1217 } 1218 #ifndef SMALL 1219 if (debug) 1220 fprintf(ttyout, 1221 "user %s:%s host %s port %s dir %s file %s\n", 1222 username, pass ? "XXXX" : NULL, host, portnum, 1223 dir, file); 1224 #endif /* !SMALL */ 1225 1226 /* 1227 * Set up the connection. 1228 */ 1229 if (connected) 1230 disconnect(0, NULL); 1231 xargv[0] = __progname; 1232 xargv[1] = host; 1233 xargv[2] = NULL; 1234 xargc = 2; 1235 if (!EMPTYSTRING(portnum)) { 1236 xargv[2] = portnum; 1237 xargv[3] = NULL; 1238 xargc = 3; 1239 } 1240 oautologin = autologin; 1241 if (username == NULL) 1242 anonftp = 1; 1243 else { 1244 anonftp = 0; 1245 autologin = 0; 1246 } 1247 setpeer(xargc, xargv); 1248 autologin = oautologin; 1249 if (connected == 0 || 1250 (connected == 1 && autologin && (username == NULL || 1251 !ftp_login(host, username, pass)))) { 1252 warnx("Can't connect or login to host `%s'", host); 1253 rval = argpos + 1; 1254 continue; 1255 } 1256 1257 /* Always use binary transfers. */ 1258 setbinary(0, NULL); 1259 1260 dirhasglob = filehasglob = 0; 1261 if (doglob) { 1262 if (!EMPTYSTRING(dir) && 1263 strpbrk(dir, "*?[]{}") != NULL) 1264 dirhasglob = 1; 1265 if (!EMPTYSTRING(file) && 1266 strpbrk(file, "*?[]{}") != NULL) 1267 filehasglob = 1; 1268 } 1269 1270 /* Change directories, if necessary. */ 1271 if (!EMPTYSTRING(dir) && !dirhasglob) { 1272 xargv[0] = "cd"; 1273 xargv[1] = dir; 1274 xargv[2] = NULL; 1275 cd(2, xargv); 1276 if (!dirchange) { 1277 rval = argpos + 1; 1278 continue; 1279 } 1280 } 1281 1282 if (EMPTYSTRING(file)) { 1283 #ifndef SMALL 1284 rval = -1; 1285 #else /* !SMALL */ 1286 recvrequest("NLST", "-", NULL, "w", 0, 0); 1287 rval = 0; 1288 #endif /* !SMALL */ 1289 continue; 1290 } 1291 1292 if (verbose) 1293 fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file); 1294 1295 if (dirhasglob) { 1296 snprintf(rempath, sizeof(rempath), "%s/%s", dir, file); 1297 file = rempath; 1298 } 1299 1300 /* Fetch the file(s). */ 1301 xargc = 2; 1302 xargv[0] = "get"; 1303 xargv[1] = file; 1304 xargv[2] = NULL; 1305 if (dirhasglob || filehasglob) { 1306 int ointeractive; 1307 1308 ointeractive = interactive; 1309 interactive = 0; 1310 xargv[0] = "mget"; 1311 #ifndef SMALL 1312 if (resume) { 1313 xargc = 3; 1314 xargv[1] = "-c"; 1315 xargv[2] = file; 1316 xargv[3] = NULL; 1317 } 1318 #endif /* !SMALL */ 1319 mget(xargc, xargv); 1320 interactive = ointeractive; 1321 } else { 1322 if (outfile != NULL) { 1323 xargv[2] = outfile; 1324 xargv[3] = NULL; 1325 xargc++; 1326 } 1327 #ifndef SMALL 1328 if (resume) 1329 reget(xargc, xargv); 1330 else 1331 #endif /* !SMALL */ 1332 get(xargc, xargv); 1333 } 1334 1335 if ((code / 100) != COMPLETE) 1336 rval = argpos + 1; 1337 } 1338 if (connected && rval != -1) 1339 disconnect(0, NULL); 1340 return (rval); 1341 } 1342 1343 char * 1344 urldecode(const char *str) 1345 { 1346 char *ret, c; 1347 int i, reallen; 1348 1349 if (str == NULL) 1350 return NULL; 1351 if ((ret = malloc(strlen(str)+1)) == NULL) 1352 err(1, "Can't allocate memory for URL decoding"); 1353 for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) { 1354 c = str[i]; 1355 if (c == '+') { 1356 *ret = ' '; 1357 continue; 1358 } 1359 1360 /* Cannot use strtol here because next char 1361 * after %xx may be a digit. 1362 */ 1363 if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { 1364 *ret = hextochar(&str[i+1]); 1365 i+=2; 1366 continue; 1367 } 1368 *ret = c; 1369 } 1370 *ret = '\0'; 1371 1372 return ret-reallen; 1373 } 1374 1375 char 1376 hextochar(const char *str) 1377 { 1378 char c, ret; 1379 1380 c = str[0]; 1381 ret = c; 1382 if (isalpha(c)) 1383 ret -= isupper(c) ? 'A' - 10 : 'a' - 10; 1384 else 1385 ret -= '0'; 1386 ret *= 16; 1387 1388 c = str[1]; 1389 ret += c; 1390 if (isalpha(c)) 1391 ret -= isupper(c) ? 'A' - 10 : 'a' - 10; 1392 else 1393 ret -= '0'; 1394 return ret; 1395 } 1396 1397 int 1398 isurl(const char *p) 1399 { 1400 1401 if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || 1402 strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || 1403 #ifndef SMALL 1404 strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 || 1405 #endif /* !SMALL */ 1406 strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || 1407 strstr(p, ":/")) 1408 return (1); 1409 return (0); 1410 } 1411 1412 char * 1413 ftp_readline(FILE *fp, SSL *ssl, size_t *lenp) 1414 { 1415 if (fp != NULL) 1416 return fparseln(fp, lenp, NULL, "\0\0\0", 0); 1417 #ifndef SMALL 1418 else if (ssl != NULL) 1419 return SSL_readline(ssl, lenp); 1420 #endif /* !SMALL */ 1421 else 1422 return NULL; 1423 } 1424 1425 size_t 1426 ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len) 1427 { 1428 size_t ret; 1429 if (fp != NULL) 1430 ret = fread(buf, sizeof(char), len, fp); 1431 #ifndef SMALL 1432 else if (ssl != NULL) { 1433 int nr; 1434 1435 if (len > INT_MAX) 1436 len = INT_MAX; 1437 if ((nr = SSL_read(ssl, buf, (int)len)) <= 0) 1438 ret = 0; 1439 else 1440 ret = nr; 1441 } 1442 #endif /* !SMALL */ 1443 else 1444 ret = 0; 1445 return (ret); 1446 } 1447 1448 int 1449 ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...) 1450 { 1451 int ret; 1452 va_list ap; 1453 1454 va_start(ap, fmt); 1455 1456 if (fp != NULL) 1457 ret = vfprintf(fp, fmt, ap); 1458 #ifndef SMALL 1459 else if (ssl != NULL) 1460 ret = SSL_vprintf((SSL*)ssl, fmt, ap); 1461 #endif /* !SMALL */ 1462 else 1463 ret = 0; 1464 1465 va_end(ap); 1466 return (ret); 1467 } 1468 1469 #ifndef SMALL 1470 int 1471 SSL_vprintf(SSL *ssl, const char *fmt, va_list ap) 1472 { 1473 int ret; 1474 char *string; 1475 1476 if ((ret = vasprintf(&string, fmt, ap)) == -1) 1477 return ret; 1478 ret = SSL_write(ssl, string, ret); 1479 free(string); 1480 return ret; 1481 } 1482 1483 char * 1484 SSL_readline(SSL *ssl, size_t *lenp) 1485 { 1486 size_t i, len; 1487 char *buf, *q, c; 1488 int ret; 1489 1490 len = 128; 1491 if ((buf = malloc(len)) == NULL) 1492 errx(1, "Can't allocate memory for transfer buffer"); 1493 for (i = 0; ; i++) { 1494 if (i >= len - 1) { 1495 if ((q = realloc(buf, 2 * len)) == NULL) 1496 errx(1, "Can't expand transfer buffer"); 1497 buf = q; 1498 len *= 2; 1499 } 1500 again: 1501 ret = SSL_read(ssl, &c, 1); 1502 if (ret <= 0) { 1503 if (SSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ) 1504 goto again; 1505 else 1506 errx(1, "SSL_read error: %u", 1507 SSL_get_error(ssl, ret)); 1508 } 1509 buf[i] = c; 1510 if (c == '\n') 1511 break; 1512 } 1513 *lenp = i; 1514 return (buf); 1515 } 1516 1517 int 1518 proxy_connect(int socket, char *host, char *cookie) 1519 { 1520 int l; 1521 char buf[1024]; 1522 char *connstr, *hosttail, *port; 1523 1524 if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && 1525 (hosttail[1] == '\0' || hosttail[1] == ':')) { 1526 host++; 1527 *hosttail++ = '\0'; 1528 } else 1529 hosttail = host; 1530 1531 port = strrchr(hosttail, ':'); /* find portnum */ 1532 if (port != NULL) 1533 *port++ = '\0'; 1534 if (!port) 1535 port = "443"; 1536 1537 if (cookie) { 1538 l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n" 1539 "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n", 1540 host, port, cookie, HTTP_USER_AGENT); 1541 } else { 1542 l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n", 1543 host, port, HTTP_USER_AGENT); 1544 } 1545 1546 if (l == -1) 1547 errx(1, "Could not allocate memory to assemble connect string!"); 1548 #ifndef SMALL 1549 if (debug) 1550 printf("%s", connstr); 1551 #endif /* !SMALL */ 1552 if (write(socket, connstr, l) != l) 1553 err(1, "Could not send connect string"); 1554 read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */ 1555 free(connstr); 1556 return(200); 1557 } 1558 #endif /* !SMALL */ 1559