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