1 /* $OpenBSD: fetch.c,v 1.107 2012/08/18 06:46:46 haesbaert 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 } 881 882 /* Open the output file. */ 883 if (!pipeout) { 884 #ifndef SMALL 885 if (resume) 886 out = open(savefile, O_CREAT | O_WRONLY | O_APPEND, 887 0666); 888 else 889 #endif /* !SMALL */ 890 out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 891 0666); 892 if (out < 0) { 893 warn("Can't open %s", savefile); 894 goto cleanup_url_get; 895 } 896 } else 897 out = fileno(stdout); 898 899 /* Trap signals */ 900 oldintr = NULL; 901 oldinti = NULL; 902 if (setjmp(httpabort)) { 903 if (oldintr) 904 (void)signal(SIGINT, oldintr); 905 if (oldinti) 906 (void)signal(SIGINFO, oldinti); 907 goto cleanup_url_get; 908 } 909 oldintr = signal(SIGINT, aborthttp); 910 911 bytes = 0; 912 hashbytes = mark; 913 progressmeter(-1, path); 914 915 free(buf); 916 917 /* Finally, suck down the file. */ 918 if ((buf = malloc(4096)) == NULL) 919 errx(1, "Can't allocate memory for transfer buffer"); 920 i = 0; 921 len = 1; 922 oldinti = signal(SIGINFO, psummary); 923 while (len > 0) { 924 len = ftp_read(fin, ssl, buf, 4096); 925 bytes += len; 926 for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { 927 if ((i = write(out, cp, wlen)) == -1) { 928 warn("Writing %s", savefile); 929 signal(SIGINFO, oldinti); 930 goto cleanup_url_get; 931 } 932 else if (i == 0) 933 break; 934 } 935 if (hash && !progress) { 936 while (bytes >= hashbytes) { 937 (void)putc('#', ttyout); 938 hashbytes += mark; 939 } 940 (void)fflush(ttyout); 941 } 942 } 943 signal(SIGINFO, oldinti); 944 if (hash && !progress && bytes > 0) { 945 if (bytes < mark) 946 (void)putc('#', ttyout); 947 (void)putc('\n', ttyout); 948 (void)fflush(ttyout); 949 } 950 if (len != 0) { 951 warn("Reading from socket"); 952 goto cleanup_url_get; 953 } 954 progressmeter(1, NULL); 955 if ( 956 #ifndef SMALL 957 !resume && 958 #endif /* !SMALL */ 959 filesize != -1 && len == 0 && bytes != filesize) { 960 if (verbose) 961 fputs("Read short file.\n", ttyout); 962 goto cleanup_url_get; 963 } 964 965 if (verbose) 966 ptransfer(0); 967 (void)signal(SIGINT, oldintr); 968 969 rval = 0; 970 goto cleanup_url_get; 971 972 noftpautologin: 973 warnx( 974 "Auto-login using ftp URLs isn't supported when using $ftp_proxy"); 975 goto cleanup_url_get; 976 977 improper: 978 warnx("Improper response from %s", host); 979 980 cleanup_url_get: 981 #ifndef SMALL 982 if (ssl) { 983 SSL_shutdown(ssl); 984 SSL_free(ssl); 985 } 986 free(full_host); 987 free(auth); 988 #endif /* !SMALL */ 989 if (fin != NULL) 990 fclose(fin); 991 else if (s != -1) 992 close(s); 993 free(buf); 994 free(proxyurl); 995 free(newline); 996 free(cookie); 997 return (rval); 998 } 999 1000 /* 1001 * Abort a http retrieval 1002 */ 1003 /* ARGSUSED */ 1004 void 1005 aborthttp(int signo) 1006 { 1007 1008 alarmtimer(0); 1009 fputs("\nhttp fetch aborted.\n", ttyout); 1010 (void)fflush(ttyout); 1011 longjmp(httpabort, 1); 1012 } 1013 1014 /* 1015 * Abort a http retrieval 1016 */ 1017 /* ARGSUSED */ 1018 void 1019 abortfile(int signo) 1020 { 1021 1022 alarmtimer(0); 1023 fputs("\nfile fetch aborted.\n", ttyout); 1024 (void)fflush(ttyout); 1025 longjmp(httpabort, 1); 1026 } 1027 1028 /* 1029 * Retrieve multiple files from the command line, transferring 1030 * files of the form "host:path", "ftp://host/path" using the 1031 * ftp protocol, and files of the form "http://host/path" using 1032 * the http protocol. 1033 * If path has a trailing "/", then return (-1); 1034 * the path will be cd-ed into and the connection remains open, 1035 * and the function will return -1 (to indicate the connection 1036 * is alive). 1037 * If an error occurs the return value will be the offset+1 in 1038 * argv[] of the file that caused a problem (i.e, argv[x] 1039 * returns x+1) 1040 * Otherwise, 0 is returned if all files retrieved successfully. 1041 */ 1042 int 1043 auto_fetch(int argc, char *argv[], char *outfile) 1044 { 1045 char *xargv[5]; 1046 char *cp, *url, *host, *dir, *file, *portnum; 1047 char *username, *pass, *pathstart; 1048 char *ftpproxy, *httpproxy; 1049 int rval, xargc; 1050 volatile int argpos; 1051 int dirhasglob, filehasglob, oautologin; 1052 char rempath[MAXPATHLEN]; 1053 1054 argpos = 0; 1055 1056 if (setjmp(toplevel)) { 1057 if (connected) 1058 disconnect(0, NULL); 1059 return (argpos + 1); 1060 } 1061 (void)signal(SIGINT, (sig_t)intr); 1062 (void)signal(SIGPIPE, (sig_t)lostpeer); 1063 1064 if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0') 1065 ftpproxy = NULL; 1066 if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0') 1067 httpproxy = NULL; 1068 1069 /* 1070 * Loop through as long as there's files to fetch. 1071 */ 1072 for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) { 1073 if (strchr(argv[argpos], ':') == NULL) 1074 break; 1075 host = dir = file = portnum = username = pass = NULL; 1076 1077 /* 1078 * We muck with the string, so we make a copy. 1079 */ 1080 url = strdup(argv[argpos]); 1081 if (url == NULL) 1082 errx(1, "Can't allocate memory for auto-fetch."); 1083 1084 /* 1085 * Try HTTP URL-style arguments first. 1086 */ 1087 if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || 1088 #ifndef SMALL 1089 /* even if we compiled without SSL, url_get will check */ 1090 strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 || 1091 #endif /* !SMALL */ 1092 strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) { 1093 redirect_loop = 0; 1094 if (url_get(url, httpproxy, outfile) == -1) 1095 rval = argpos + 1; 1096 continue; 1097 } 1098 1099 /* 1100 * Try FTP URL-style arguments next. If ftpproxy is 1101 * set, use url_get() instead of standard ftp. 1102 * Finally, try host:file. 1103 */ 1104 host = url; 1105 if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) { 1106 char *passend, *passagain, *userend; 1107 1108 if (ftpproxy) { 1109 if (url_get(url, ftpproxy, outfile) == -1) 1110 rval = argpos + 1; 1111 continue; 1112 } 1113 host += sizeof(FTP_URL) - 1; 1114 dir = strchr(host, '/'); 1115 1116 /* Look for [user:pass@]host[:port] */ 1117 1118 /* check if we have "user:pass@" */ 1119 userend = strchr(host, ':'); 1120 passend = strchr(host, '@'); 1121 if (passend && userend && userend < passend && 1122 (!dir || passend < dir)) { 1123 username = host; 1124 pass = userend + 1; 1125 host = passend + 1; 1126 *userend = *passend = '\0'; 1127 passagain = strchr(host, '@'); 1128 if (strchr(pass, '@') != NULL || 1129 (passagain != NULL && passagain < dir)) { 1130 warnx(at_encoding_warning); 1131 goto bad_ftp_url; 1132 } 1133 1134 if (EMPTYSTRING(username)) { 1135 bad_ftp_url: 1136 warnx("Invalid URL: %s", argv[argpos]); 1137 rval = argpos + 1; 1138 continue; 1139 } 1140 username = urldecode(username); 1141 pass = urldecode(pass); 1142 } 1143 1144 #ifdef INET6 1145 /* check [host]:port, or [host] */ 1146 if (host[0] == '[') { 1147 cp = strchr(host, ']'); 1148 if (cp && (!dir || cp < dir)) { 1149 if (cp + 1 == dir || cp[1] == ':') { 1150 host++; 1151 *cp++ = '\0'; 1152 } else 1153 cp = NULL; 1154 } else 1155 cp = host; 1156 } else 1157 cp = host; 1158 #else 1159 cp = host; 1160 #endif 1161 1162 /* split off host[:port] if there is */ 1163 if (cp) { 1164 portnum = strchr(cp, ':'); 1165 pathstart = strchr(cp, '/'); 1166 /* : in path is not a port # indicator */ 1167 if (portnum && pathstart && 1168 pathstart < portnum) 1169 portnum = NULL; 1170 1171 if (!portnum) 1172 ; 1173 else { 1174 if (!dir) 1175 ; 1176 else if (portnum + 1 < dir) { 1177 *portnum++ = '\0'; 1178 /* 1179 * XXX should check if portnum 1180 * is decimal number 1181 */ 1182 } else { 1183 /* empty portnum */ 1184 goto bad_ftp_url; 1185 } 1186 } 1187 } else 1188 portnum = NULL; 1189 } else { /* classic style `host:file' */ 1190 dir = strchr(host, ':'); 1191 } 1192 if (EMPTYSTRING(host)) { 1193 rval = argpos + 1; 1194 continue; 1195 } 1196 1197 /* 1198 * If dir is NULL, the file wasn't specified 1199 * (URL looked something like ftp://host) 1200 */ 1201 if (dir != NULL) 1202 *dir++ = '\0'; 1203 1204 /* 1205 * Extract the file and (if present) directory name. 1206 */ 1207 if (!EMPTYSTRING(dir)) { 1208 cp = strrchr(dir, '/'); 1209 if (cp != NULL) { 1210 *cp++ = '\0'; 1211 file = cp; 1212 } else { 1213 file = dir; 1214 dir = NULL; 1215 } 1216 } 1217 #ifndef SMALL 1218 if (debug) 1219 fprintf(ttyout, 1220 "user %s:%s host %s port %s dir %s file %s\n", 1221 username, pass ? "XXXX" : NULL, host, portnum, 1222 dir, file); 1223 #endif /* !SMALL */ 1224 1225 /* 1226 * Set up the connection. 1227 */ 1228 if (connected) 1229 disconnect(0, NULL); 1230 xargv[0] = __progname; 1231 xargv[1] = host; 1232 xargv[2] = NULL; 1233 xargc = 2; 1234 if (!EMPTYSTRING(portnum)) { 1235 xargv[2] = portnum; 1236 xargv[3] = NULL; 1237 xargc = 3; 1238 } 1239 oautologin = autologin; 1240 if (username == NULL) 1241 anonftp = 1; 1242 else { 1243 anonftp = 0; 1244 autologin = 0; 1245 } 1246 setpeer(xargc, xargv); 1247 autologin = oautologin; 1248 if (connected == 0 || 1249 (connected == 1 && autologin && (username == NULL || 1250 !ftp_login(host, username, pass)))) { 1251 warnx("Can't connect or login to host `%s'", host); 1252 rval = argpos + 1; 1253 continue; 1254 } 1255 1256 /* Always use binary transfers. */ 1257 setbinary(0, NULL); 1258 1259 dirhasglob = filehasglob = 0; 1260 if (doglob) { 1261 if (!EMPTYSTRING(dir) && 1262 strpbrk(dir, "*?[]{}") != NULL) 1263 dirhasglob = 1; 1264 if (!EMPTYSTRING(file) && 1265 strpbrk(file, "*?[]{}") != NULL) 1266 filehasglob = 1; 1267 } 1268 1269 /* Change directories, if necessary. */ 1270 if (!EMPTYSTRING(dir) && !dirhasglob) { 1271 xargv[0] = "cd"; 1272 xargv[1] = dir; 1273 xargv[2] = NULL; 1274 cd(2, xargv); 1275 if (!dirchange) { 1276 rval = argpos + 1; 1277 continue; 1278 } 1279 } 1280 1281 if (EMPTYSTRING(file)) { 1282 #ifndef SMALL 1283 rval = -1; 1284 #else /* !SMALL */ 1285 recvrequest("NLST", "-", NULL, "w", 0, 0); 1286 rval = 0; 1287 #endif /* !SMALL */ 1288 continue; 1289 } 1290 1291 if (verbose) 1292 fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file); 1293 1294 if (dirhasglob) { 1295 snprintf(rempath, sizeof(rempath), "%s/%s", dir, file); 1296 file = rempath; 1297 } 1298 1299 /* Fetch the file(s). */ 1300 xargc = 2; 1301 xargv[0] = "get"; 1302 xargv[1] = file; 1303 xargv[2] = NULL; 1304 if (dirhasglob || filehasglob) { 1305 int ointeractive; 1306 1307 ointeractive = interactive; 1308 interactive = 0; 1309 xargv[0] = "mget"; 1310 #ifndef SMALL 1311 if (resume) { 1312 xargc = 3; 1313 xargv[1] = "-c"; 1314 xargv[2] = file; 1315 xargv[3] = NULL; 1316 } 1317 #endif /* !SMALL */ 1318 mget(xargc, xargv); 1319 interactive = ointeractive; 1320 } else { 1321 if (outfile != NULL) { 1322 xargv[2] = outfile; 1323 xargv[3] = NULL; 1324 xargc++; 1325 } 1326 #ifndef SMALL 1327 if (resume) 1328 reget(xargc, xargv); 1329 else 1330 #endif /* !SMALL */ 1331 get(xargc, xargv); 1332 } 1333 1334 if ((code / 100) != COMPLETE) 1335 rval = argpos + 1; 1336 } 1337 if (connected && rval != -1) 1338 disconnect(0, NULL); 1339 return (rval); 1340 } 1341 1342 char * 1343 urldecode(const char *str) 1344 { 1345 char *ret, c; 1346 int i, reallen; 1347 1348 if (str == NULL) 1349 return NULL; 1350 if ((ret = malloc(strlen(str)+1)) == NULL) 1351 err(1, "Can't allocate memory for URL decoding"); 1352 for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) { 1353 c = str[i]; 1354 if (c == '+') { 1355 *ret = ' '; 1356 continue; 1357 } 1358 1359 /* Cannot use strtol here because next char 1360 * after %xx may be a digit. 1361 */ 1362 if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) { 1363 *ret = hextochar(&str[i+1]); 1364 i+=2; 1365 continue; 1366 } 1367 *ret = c; 1368 } 1369 *ret = '\0'; 1370 1371 return ret-reallen; 1372 } 1373 1374 char 1375 hextochar(const char *str) 1376 { 1377 char c, ret; 1378 1379 c = str[0]; 1380 ret = c; 1381 if (isalpha(c)) 1382 ret -= isupper(c) ? 'A' - 10 : 'a' - 10; 1383 else 1384 ret -= '0'; 1385 ret *= 16; 1386 1387 c = str[1]; 1388 ret += c; 1389 if (isalpha(c)) 1390 ret -= isupper(c) ? 'A' - 10 : 'a' - 10; 1391 else 1392 ret -= '0'; 1393 return ret; 1394 } 1395 1396 int 1397 isurl(const char *p) 1398 { 1399 1400 if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || 1401 strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || 1402 #ifndef SMALL 1403 strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 || 1404 #endif /* !SMALL */ 1405 strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || 1406 strstr(p, ":/")) 1407 return (1); 1408 return (0); 1409 } 1410 1411 char * 1412 ftp_readline(FILE *fp, SSL *ssl, size_t *lenp) 1413 { 1414 if (fp != NULL) 1415 return fparseln(fp, lenp, NULL, "\0\0\0", 0); 1416 #ifndef SMALL 1417 else if (ssl != NULL) 1418 return SSL_readline(ssl, lenp); 1419 #endif /* !SMALL */ 1420 else 1421 return NULL; 1422 } 1423 1424 size_t 1425 ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len) 1426 { 1427 size_t ret; 1428 if (fp != NULL) 1429 ret = fread(buf, sizeof(char), len, fp); 1430 #ifndef SMALL 1431 else if (ssl != NULL) { 1432 int nr; 1433 1434 if (len > INT_MAX) 1435 len = INT_MAX; 1436 if ((nr = SSL_read(ssl, buf, (int)len)) <= 0) 1437 ret = 0; 1438 else 1439 ret = nr; 1440 } 1441 #endif /* !SMALL */ 1442 else 1443 ret = 0; 1444 return (ret); 1445 } 1446 1447 int 1448 ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...) 1449 { 1450 int ret; 1451 va_list ap; 1452 1453 va_start(ap, fmt); 1454 1455 if (fp != NULL) 1456 ret = vfprintf(fp, fmt, ap); 1457 #ifndef SMALL 1458 else if (ssl != NULL) 1459 ret = SSL_vprintf((SSL*)ssl, fmt, ap); 1460 #endif /* !SMALL */ 1461 else 1462 ret = 0; 1463 1464 va_end(ap); 1465 return (ret); 1466 } 1467 1468 #ifndef SMALL 1469 int 1470 SSL_vprintf(SSL *ssl, const char *fmt, va_list ap) 1471 { 1472 int ret; 1473 char *string; 1474 1475 if ((ret = vasprintf(&string, fmt, ap)) == -1) 1476 return ret; 1477 ret = SSL_write(ssl, string, ret); 1478 free(string); 1479 return ret; 1480 } 1481 1482 char * 1483 SSL_readline(SSL *ssl, size_t *lenp) 1484 { 1485 size_t i, len; 1486 char *buf, *q, c; 1487 1488 len = 128; 1489 if ((buf = malloc(len)) == NULL) 1490 errx(1, "Can't allocate memory for transfer buffer"); 1491 for (i = 0; ; i++) { 1492 if (i >= len - 1) { 1493 if ((q = realloc(buf, 2 * len)) == NULL) 1494 errx(1, "Can't expand transfer buffer"); 1495 buf = q; 1496 len *= 2; 1497 } 1498 if (SSL_read(ssl, &c, 1) <= 0) 1499 break; 1500 buf[i] = c; 1501 if (c == '\n') 1502 break; 1503 } 1504 *lenp = i; 1505 return (buf); 1506 } 1507 1508 int 1509 proxy_connect(int socket, char *host, char *cookie) 1510 { 1511 int l; 1512 char buf[1024]; 1513 char *connstr, *hosttail, *port; 1514 1515 if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && 1516 (hosttail[1] == '\0' || hosttail[1] == ':')) { 1517 host++; 1518 *hosttail++ = '\0'; 1519 } else 1520 hosttail = host; 1521 1522 port = strrchr(hosttail, ':'); /* find portnum */ 1523 if (port != NULL) 1524 *port++ = '\0'; 1525 if (!port) 1526 port = "443"; 1527 1528 if (cookie) { 1529 l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n" 1530 "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n", 1531 host, port, cookie, HTTP_USER_AGENT); 1532 } else { 1533 l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n", 1534 host, port, HTTP_USER_AGENT); 1535 } 1536 1537 if (l == -1) 1538 errx(1, "Could not allocate memory to assemble connect string!"); 1539 #ifndef SMALL 1540 if (debug) 1541 printf("%s", connstr); 1542 #endif /* !SMALL */ 1543 if (write(socket, connstr, l) != l) 1544 err(1, "Could not send connect string"); 1545 read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */ 1546 free(connstr); 1547 return(200); 1548 } 1549 #endif /* !SMALL */ 1550