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