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