1 /*- 2 * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 /* 33 * Portions of this code were taken from or based on ftpio.c: 34 * 35 * ---------------------------------------------------------------------------- 36 * "THE BEER-WARE LICENSE" (Revision 42): 37 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 38 * can do whatever you want with this stuff. If we meet some day, and you think 39 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 40 * ---------------------------------------------------------------------------- 41 * 42 * Major Changelog: 43 * 44 * Dag-Erling Co�dan Sm�rgrav 45 * 9 Jun 1998 46 * 47 * Incorporated into libfetch 48 * 49 * Jordan K. Hubbard 50 * 17 Jan 1996 51 * 52 * Turned inside out. Now returns xfers as new file ids, not as a special 53 * `state' of FTP_t 54 * 55 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 56 * 57 */ 58 59 #include <sys/param.h> 60 #include <sys/socket.h> 61 #include <netinet/in.h> 62 63 #include <ctype.h> 64 #include <err.h> 65 #include <errno.h> 66 #include <fcntl.h> 67 #include <netdb.h> 68 #include <stdarg.h> 69 #include <stdint.h> 70 #include <stdio.h> 71 #include <stdlib.h> 72 #include <string.h> 73 #include <time.h> 74 #include <unistd.h> 75 76 #include "fetch.h" 77 #include "common.h" 78 #include "ftperr.h" 79 80 #define FTP_ANONYMOUS_USER "anonymous" 81 82 #define FTP_CONNECTION_ALREADY_OPEN 125 83 #define FTP_OPEN_DATA_CONNECTION 150 84 #define FTP_OK 200 85 #define FTP_FILE_STATUS 213 86 #define FTP_SERVICE_READY 220 87 #define FTP_TRANSFER_COMPLETE 226 88 #define FTP_PASSIVE_MODE 227 89 #define FTP_LPASSIVE_MODE 228 90 #define FTP_EPASSIVE_MODE 229 91 #define FTP_LOGGED_IN 230 92 #define FTP_FILE_ACTION_OK 250 93 #define FTP_NEED_PASSWORD 331 94 #define FTP_NEED_ACCOUNT 332 95 #define FTP_FILE_OK 350 96 #define FTP_SYNTAX_ERROR 500 97 #define FTP_PROTOCOL_ERROR 999 98 99 static struct url cached_host; 100 static conn_t *cached_connection; 101 102 #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 103 && isdigit(foo[2]) \ 104 && (foo[3] == ' ' || foo[3] == '\0')) 105 #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 106 && isdigit(foo[2]) && foo[3] == '-') 107 108 /* 109 * Translate IPv4 mapped IPv6 address to IPv4 address 110 */ 111 static void 112 unmappedaddr(struct sockaddr_in6 *sin6) 113 { 114 struct sockaddr_in *sin4; 115 u_int32_t addr; 116 int port; 117 118 if (sin6->sin6_family != AF_INET6 || 119 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 120 return; 121 sin4 = (struct sockaddr_in *)sin6; 122 addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 123 port = sin6->sin6_port; 124 memset(sin4, 0, sizeof(struct sockaddr_in)); 125 sin4->sin_addr.s_addr = addr; 126 sin4->sin_port = port; 127 sin4->sin_family = AF_INET; 128 sin4->sin_len = sizeof(struct sockaddr_in); 129 } 130 131 /* 132 * Get server response 133 */ 134 static int 135 _ftp_chkerr(conn_t *conn) 136 { 137 if (_fetch_getln(conn) == -1) { 138 _fetch_syserr(); 139 return (-1); 140 } 141 if (isftpinfo(conn->buf)) { 142 while (conn->buflen && !isftpreply(conn->buf)) { 143 if (_fetch_getln(conn) == -1) { 144 _fetch_syserr(); 145 return (-1); 146 } 147 } 148 } 149 150 while (conn->buflen && isspace(conn->buf[conn->buflen - 1])) 151 conn->buflen--; 152 conn->buf[conn->buflen] = '\0'; 153 154 if (!isftpreply(conn->buf)) { 155 _ftp_seterr(FTP_PROTOCOL_ERROR); 156 return (-1); 157 } 158 159 conn->err = (conn->buf[0] - '0') * 100 160 + (conn->buf[1] - '0') * 10 161 + (conn->buf[2] - '0'); 162 163 return (conn->err); 164 } 165 166 /* 167 * Send a command and check reply 168 */ 169 static int 170 _ftp_cmd(conn_t *conn, const char *fmt, ...) 171 { 172 va_list ap; 173 size_t len; 174 char *msg; 175 int r; 176 177 va_start(ap, fmt); 178 len = vasprintf(&msg, fmt, ap); 179 va_end(ap); 180 181 if (msg == NULL) { 182 errno = ENOMEM; 183 _fetch_syserr(); 184 return (-1); 185 } 186 187 r = _fetch_putln(conn, msg, len); 188 free(msg); 189 190 if (r == -1) { 191 _fetch_syserr(); 192 return (-1); 193 } 194 195 return (_ftp_chkerr(conn)); 196 } 197 198 /* 199 * Return a pointer to the filename part of a path 200 */ 201 static const char * 202 _ftp_filename(const char *file) 203 { 204 char *s; 205 206 if ((s = strrchr(file, '/')) == NULL) 207 return (file); 208 else 209 return (s + 1); 210 } 211 212 /* 213 * Change working directory to the directory that contains the specified 214 * file. 215 */ 216 static int 217 _ftp_cwd(conn_t *conn, const char *file) 218 { 219 char *s; 220 int e; 221 222 if ((s = strrchr(file, '/')) == NULL || s == file) { 223 e = _ftp_cmd(conn, "CWD /"); 224 } else { 225 e = _ftp_cmd(conn, "CWD %.*s", s - file, file); 226 } 227 if (e != FTP_FILE_ACTION_OK) { 228 _ftp_seterr(e); 229 return (-1); 230 } 231 return (0); 232 } 233 234 /* 235 * Request and parse file stats 236 */ 237 static int 238 _ftp_stat(conn_t *conn, const char *file, struct url_stat *us) 239 { 240 char *ln; 241 const char *s; 242 struct tm tm; 243 time_t t; 244 int e; 245 246 us->size = -1; 247 us->atime = us->mtime = 0; 248 249 if ((s = strrchr(file, '/')) == NULL) 250 s = file; 251 else 252 ++s; 253 254 if ((e = _ftp_cmd(conn, "SIZE %s", s)) != FTP_FILE_STATUS) { 255 _ftp_seterr(e); 256 return (-1); 257 } 258 for (ln = conn->buf + 4; *ln && isspace(*ln); ln++) 259 /* nothing */ ; 260 for (us->size = 0; *ln && isdigit(*ln); ln++) 261 us->size = us->size * 10 + *ln - '0'; 262 if (*ln && !isspace(*ln)) { 263 _ftp_seterr(FTP_PROTOCOL_ERROR); 264 us->size = -1; 265 return (-1); 266 } 267 if (us->size == 0) 268 us->size = -1; 269 DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size)); 270 271 if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) { 272 _ftp_seterr(e); 273 return (-1); 274 } 275 for (ln = conn->buf + 4; *ln && isspace(*ln); ln++) 276 /* nothing */ ; 277 switch (strspn(ln, "0123456789")) { 278 case 14: 279 break; 280 case 15: 281 ln++; 282 ln[0] = '2'; 283 ln[1] = '0'; 284 break; 285 default: 286 _ftp_seterr(FTP_PROTOCOL_ERROR); 287 return (-1); 288 } 289 if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 290 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 291 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 292 _ftp_seterr(FTP_PROTOCOL_ERROR); 293 return (-1); 294 } 295 tm.tm_mon--; 296 tm.tm_year -= 1900; 297 tm.tm_isdst = -1; 298 t = timegm(&tm); 299 if (t == (time_t)-1) 300 t = time(NULL); 301 us->mtime = t; 302 us->atime = t; 303 DEBUG(fprintf(stderr, 304 "last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n", 305 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 306 tm.tm_hour, tm.tm_min, tm.tm_sec)); 307 return (0); 308 } 309 310 /* 311 * I/O functions for FTP 312 */ 313 struct ftpio { 314 conn_t *cconn; /* Control connection */ 315 conn_t *dconn; /* Data connection */ 316 int dir; /* Direction */ 317 int eof; /* EOF reached */ 318 int err; /* Error code */ 319 }; 320 321 static int _ftp_readfn(void *, char *, int); 322 static int _ftp_writefn(void *, const char *, int); 323 static fpos_t _ftp_seekfn(void *, fpos_t, int); 324 static int _ftp_closefn(void *); 325 326 static int 327 _ftp_readfn(void *v, char *buf, int len) 328 { 329 struct ftpio *io; 330 int r; 331 332 io = (struct ftpio *)v; 333 if (io == NULL) { 334 errno = EBADF; 335 return (-1); 336 } 337 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { 338 errno = EBADF; 339 return (-1); 340 } 341 if (io->err) { 342 errno = io->err; 343 return (-1); 344 } 345 if (io->eof) 346 return (0); 347 r = _fetch_read(io->dconn, buf, len); 348 if (r > 0) 349 return (r); 350 if (r == 0) { 351 io->eof = 1; 352 return (0); 353 } 354 if (errno != EINTR) 355 io->err = errno; 356 return (-1); 357 } 358 359 static int 360 _ftp_writefn(void *v, const char *buf, int len) 361 { 362 struct ftpio *io; 363 int w; 364 365 io = (struct ftpio *)v; 366 if (io == NULL) { 367 errno = EBADF; 368 return (-1); 369 } 370 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { 371 errno = EBADF; 372 return (-1); 373 } 374 if (io->err) { 375 errno = io->err; 376 return (-1); 377 } 378 w = _fetch_write(io->dconn, buf, len); 379 if (w >= 0) 380 return (w); 381 if (errno != EINTR) 382 io->err = errno; 383 return (-1); 384 } 385 386 static fpos_t 387 _ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused) 388 { 389 struct ftpio *io; 390 391 io = (struct ftpio *)v; 392 if (io == NULL) { 393 errno = EBADF; 394 return (-1); 395 } 396 errno = ESPIPE; 397 return (-1); 398 } 399 400 static int 401 _ftp_closefn(void *v) 402 { 403 struct ftpio *io; 404 int r; 405 406 io = (struct ftpio *)v; 407 if (io == NULL) { 408 errno = EBADF; 409 return (-1); 410 } 411 if (io->dir == -1) 412 return (0); 413 if (io->cconn == NULL || io->dconn == NULL) { 414 errno = EBADF; 415 return (-1); 416 } 417 _fetch_close(io->dconn); 418 io->dir = -1; 419 io->dconn = NULL; 420 DEBUG(fprintf(stderr, "Waiting for final status\n")); 421 r = _ftp_chkerr(io->cconn); 422 _fetch_close(io->cconn); 423 free(io); 424 return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1; 425 } 426 427 static FILE * 428 _ftp_setup(conn_t *cconn, conn_t *dconn, int mode) 429 { 430 struct ftpio *io; 431 FILE *f; 432 433 if (cconn == NULL || dconn == NULL) 434 return (NULL); 435 if ((io = malloc(sizeof *io)) == NULL) 436 return (NULL); 437 io->cconn = cconn; 438 io->dconn = dconn; 439 io->dir = mode; 440 io->eof = io->err = 0; 441 f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn); 442 if (f == NULL) 443 free(io); 444 return (f); 445 } 446 447 /* 448 * Transfer file 449 */ 450 static FILE * 451 _ftp_transfer(conn_t *conn, const char *oper, const char *file, 452 int mode, off_t offset, const char *flags) 453 { 454 struct sockaddr_storage sa; 455 struct sockaddr_in6 *sin6; 456 struct sockaddr_in *sin4; 457 int low, pasv, verbose; 458 int e, sd = -1; 459 socklen_t l; 460 char *s; 461 FILE *df; 462 463 /* check flags */ 464 low = CHECK_FLAG('l'); 465 pasv = CHECK_FLAG('p'); 466 verbose = CHECK_FLAG('v'); 467 468 /* passive mode */ 469 if (!pasv) 470 pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && 471 strncasecmp(s, "no", 2) != 0); 472 473 /* find our own address, bind, and listen */ 474 l = sizeof sa; 475 if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) 476 goto sysouch; 477 if (sa.ss_family == AF_INET6) 478 unmappedaddr((struct sockaddr_in6 *)&sa); 479 480 /* open data socket */ 481 if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 482 _fetch_syserr(); 483 return (NULL); 484 } 485 486 if (pasv) { 487 u_char addr[64]; 488 char *ln, *p; 489 unsigned int i; 490 int port; 491 492 /* send PASV command */ 493 if (verbose) 494 _fetch_info("setting passive mode"); 495 switch (sa.ss_family) { 496 case AF_INET: 497 if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) 498 goto ouch; 499 break; 500 case AF_INET6: 501 if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { 502 if (e == -1) 503 goto ouch; 504 if ((e = _ftp_cmd(conn, "LPSV")) != 505 FTP_LPASSIVE_MODE) 506 goto ouch; 507 } 508 break; 509 default: 510 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 511 goto ouch; 512 } 513 514 /* 515 * Find address and port number. The reply to the PASV command 516 * is IMHO the one and only weak point in the FTP protocol. 517 */ 518 ln = conn->buf; 519 switch (e) { 520 case FTP_PASSIVE_MODE: 521 case FTP_LPASSIVE_MODE: 522 for (p = ln + 3; *p && !isdigit(*p); p++) 523 /* nothing */ ; 524 if (!*p) { 525 e = FTP_PROTOCOL_ERROR; 526 goto ouch; 527 } 528 l = (e == FTP_PASSIVE_MODE ? 6 : 21); 529 for (i = 0; *p && i < l; i++, p++) 530 addr[i] = strtol(p, &p, 10); 531 if (i < l) { 532 e = FTP_PROTOCOL_ERROR; 533 goto ouch; 534 } 535 break; 536 case FTP_EPASSIVE_MODE: 537 for (p = ln + 3; *p && *p != '('; p++) 538 /* nothing */ ; 539 if (!*p) { 540 e = FTP_PROTOCOL_ERROR; 541 goto ouch; 542 } 543 ++p; 544 if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 545 &port, &addr[3]) != 5 || 546 addr[0] != addr[1] || 547 addr[0] != addr[2] || addr[0] != addr[3]) { 548 e = FTP_PROTOCOL_ERROR; 549 goto ouch; 550 } 551 break; 552 } 553 554 /* seek to required offset */ 555 if (offset) 556 if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK) 557 goto sysouch; 558 559 /* construct sockaddr for data socket */ 560 l = sizeof sa; 561 if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) 562 goto sysouch; 563 if (sa.ss_family == AF_INET6) 564 unmappedaddr((struct sockaddr_in6 *)&sa); 565 switch (sa.ss_family) { 566 case AF_INET6: 567 sin6 = (struct sockaddr_in6 *)&sa; 568 if (e == FTP_EPASSIVE_MODE) 569 sin6->sin6_port = htons(port); 570 else { 571 bcopy(addr + 2, (char *)&sin6->sin6_addr, 16); 572 bcopy(addr + 19, (char *)&sin6->sin6_port, 2); 573 } 574 break; 575 case AF_INET: 576 sin4 = (struct sockaddr_in *)&sa; 577 if (e == FTP_EPASSIVE_MODE) 578 sin4->sin_port = htons(port); 579 else { 580 bcopy(addr, (char *)&sin4->sin_addr, 4); 581 bcopy(addr + 4, (char *)&sin4->sin_port, 2); 582 } 583 break; 584 default: 585 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 586 break; 587 } 588 589 /* connect to data port */ 590 if (verbose) 591 _fetch_info("opening data connection"); 592 if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) 593 goto sysouch; 594 595 /* make the server initiate the transfer */ 596 if (verbose) 597 _fetch_info("initiating transfer"); 598 e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file)); 599 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 600 goto ouch; 601 602 } else { 603 u_int32_t a; 604 u_short p; 605 int arg, d; 606 char *ap; 607 char hname[INET6_ADDRSTRLEN]; 608 609 switch (sa.ss_family) { 610 case AF_INET6: 611 ((struct sockaddr_in6 *)&sa)->sin6_port = 0; 612 #ifdef IPV6_PORTRANGE 613 arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; 614 if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 615 (char *)&arg, sizeof(arg)) == -1) 616 goto sysouch; 617 #endif 618 break; 619 case AF_INET: 620 ((struct sockaddr_in *)&sa)->sin_port = 0; 621 arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; 622 if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 623 (char *)&arg, sizeof arg) == -1) 624 goto sysouch; 625 break; 626 } 627 if (verbose) 628 _fetch_info("binding data socket"); 629 if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) 630 goto sysouch; 631 if (listen(sd, 1) == -1) 632 goto sysouch; 633 634 /* find what port we're on and tell the server */ 635 if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) 636 goto sysouch; 637 switch (sa.ss_family) { 638 case AF_INET: 639 sin4 = (struct sockaddr_in *)&sa; 640 a = ntohl(sin4->sin_addr.s_addr); 641 p = ntohs(sin4->sin_port); 642 e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", 643 (a >> 24) & 0xff, (a >> 16) & 0xff, 644 (a >> 8) & 0xff, a & 0xff, 645 (p >> 8) & 0xff, p & 0xff); 646 break; 647 case AF_INET6: 648 #define UC(b) (((int)b)&0xff) 649 e = -1; 650 sin6 = (struct sockaddr_in6 *)&sa; 651 sin6->sin6_scope_id = 0; 652 if (getnameinfo((struct sockaddr *)&sa, sa.ss_len, 653 hname, sizeof(hname), 654 NULL, 0, NI_NUMERICHOST) == 0) { 655 e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, 656 htons(sin6->sin6_port)); 657 if (e == -1) 658 goto ouch; 659 } 660 if (e != FTP_OK) { 661 ap = (char *)&sin6->sin6_addr; 662 e = _ftp_cmd(conn, 663 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 664 6, 16, 665 UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 666 UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 667 UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 668 UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 669 2, 670 (ntohs(sin6->sin6_port) >> 8) & 0xff, 671 ntohs(sin6->sin6_port) & 0xff); 672 } 673 break; 674 default: 675 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 676 goto ouch; 677 } 678 if (e != FTP_OK) 679 goto ouch; 680 681 /* seek to required offset */ 682 if (offset) 683 if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK) 684 goto sysouch; 685 686 /* make the server initiate the transfer */ 687 if (verbose) 688 _fetch_info("initiating transfer"); 689 e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file)); 690 if (e != FTP_OPEN_DATA_CONNECTION) 691 goto ouch; 692 693 /* accept the incoming connection and go to town */ 694 if ((d = accept(sd, NULL, NULL)) == -1) 695 goto sysouch; 696 close(sd); 697 sd = d; 698 } 699 700 if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL) 701 goto sysouch; 702 return (df); 703 704 sysouch: 705 _fetch_syserr(); 706 if (sd >= 0) 707 close(sd); 708 return (NULL); 709 710 ouch: 711 if (e != -1) 712 _ftp_seterr(e); 713 if (sd >= 0) 714 close(sd); 715 return (NULL); 716 } 717 718 /* 719 * Authenticate 720 */ 721 static int 722 _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) 723 { 724 const char *user, *pwd, *logname; 725 char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 726 int e, len; 727 728 /* XXX FTP_AUTH, and maybe .netrc */ 729 730 /* send user name and password */ 731 user = url->user; 732 if (!user || !*user) 733 user = getenv("FTP_LOGIN"); 734 if (!user || !*user) 735 user = FTP_ANONYMOUS_USER; 736 if (purl && url->port == _fetch_default_port(url->scheme)) 737 e = _ftp_cmd(conn, "USER %s@%s", user, url->host); 738 else if (purl) 739 e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port); 740 else 741 e = _ftp_cmd(conn, "USER %s", user); 742 743 /* did the server request a password? */ 744 if (e == FTP_NEED_PASSWORD) { 745 pwd = url->pwd; 746 if (!pwd || !*pwd) 747 pwd = getenv("FTP_PASSWORD"); 748 if (!pwd || !*pwd) { 749 if ((logname = getlogin()) == 0) 750 logname = FTP_ANONYMOUS_USER; 751 if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0) 752 len = 0; 753 else if (len > MAXLOGNAME) 754 len = MAXLOGNAME; 755 gethostname(pbuf + len, sizeof pbuf - len); 756 pwd = pbuf; 757 } 758 e = _ftp_cmd(conn, "PASS %s", pwd); 759 } 760 761 return (e); 762 } 763 764 /* 765 * Log on to FTP server 766 */ 767 static conn_t * 768 _ftp_connect(struct url *url, struct url *purl, const char *flags) 769 { 770 conn_t *conn; 771 int e, direct, verbose; 772 #ifdef INET6 773 int af = AF_UNSPEC; 774 #else 775 int af = AF_INET; 776 #endif 777 778 direct = CHECK_FLAG('d'); 779 verbose = CHECK_FLAG('v'); 780 if (CHECK_FLAG('4')) 781 af = AF_INET; 782 else if (CHECK_FLAG('6')) 783 af = AF_INET6; 784 785 if (direct) 786 purl = NULL; 787 788 /* check for proxy */ 789 if (purl) { 790 /* XXX proxy authentication! */ 791 conn = _fetch_connect(purl->host, purl->port, af, verbose); 792 } else { 793 /* no proxy, go straight to target */ 794 conn = _fetch_connect(url->host, url->port, af, verbose); 795 purl = NULL; 796 } 797 798 /* check connection */ 799 if (conn == NULL) 800 /* _fetch_connect() has already set an error code */ 801 return (NULL); 802 803 /* expect welcome message */ 804 if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY) 805 goto fouch; 806 807 /* authenticate */ 808 if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) 809 goto fouch; 810 811 /* might as well select mode and type at once */ 812 #ifdef FTP_FORCE_STREAM_MODE 813 if ((e = _ftp_cmd(conn, "MODE S")) != FTP_OK) /* default is S */ 814 goto fouch; 815 #endif 816 if ((e = _ftp_cmd(conn, "TYPE I")) != FTP_OK) /* default is A */ 817 goto fouch; 818 819 /* done */ 820 return (conn); 821 822 fouch: 823 if (e != -1) 824 _ftp_seterr(e); 825 _fetch_close(conn); 826 return (NULL); 827 } 828 829 /* 830 * Disconnect from server 831 */ 832 static void 833 _ftp_disconnect(conn_t *conn) 834 { 835 (void)_ftp_cmd(conn, "QUIT"); 836 _fetch_close(conn); 837 } 838 839 /* 840 * Check if we're already connected 841 */ 842 static int 843 _ftp_isconnected(struct url *url) 844 { 845 return (cached_connection 846 && (strcmp(url->host, cached_host.host) == 0) 847 && (strcmp(url->user, cached_host.user) == 0) 848 && (strcmp(url->pwd, cached_host.pwd) == 0) 849 && (url->port == cached_host.port)); 850 } 851 852 /* 853 * Check the cache, reconnect if no luck 854 */ 855 static conn_t * 856 _ftp_cached_connect(struct url *url, struct url *purl, const char *flags) 857 { 858 conn_t *conn; 859 int e; 860 861 /* set default port */ 862 if (!url->port) 863 url->port = _fetch_default_port(url->scheme); 864 865 /* try to use previously cached connection */ 866 if (_ftp_isconnected(url)) { 867 e = _ftp_cmd(cached_connection, "NOOP"); 868 if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 869 return (_fetch_ref(cached_connection)); 870 } 871 872 /* connect to server */ 873 if ((conn = _ftp_connect(url, purl, flags)) == NULL) 874 return (NULL); 875 if (cached_connection) 876 _ftp_disconnect(cached_connection); 877 cached_connection = _fetch_ref(conn); 878 memcpy(&cached_host, url, sizeof *url); 879 return (conn); 880 } 881 882 /* 883 * Check the proxy settings 884 */ 885 static struct url * 886 _ftp_get_proxy(void) 887 { 888 struct url *purl; 889 char *p; 890 891 if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) || 892 (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 893 *p && (purl = fetchParseURL(p)) != NULL) { 894 if (!*purl->scheme) { 895 if (getenv("FTP_PROXY") || getenv("ftp_proxy")) 896 strcpy(purl->scheme, SCHEME_FTP); 897 else 898 strcpy(purl->scheme, SCHEME_HTTP); 899 } 900 if (!purl->port) 901 purl->port = _fetch_default_proxy_port(purl->scheme); 902 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 903 strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 904 return (purl); 905 fetchFreeURL(purl); 906 } 907 return (NULL); 908 } 909 910 /* 911 * Process an FTP request 912 */ 913 FILE * 914 _ftp_request(struct url *url, const char *op, struct url_stat *us, 915 struct url *purl, const char *flags) 916 { 917 conn_t *conn; 918 int oflag; 919 920 /* check if we should use HTTP instead */ 921 if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 922 if (strcmp(op, "STAT") == 0) 923 return (_http_request(url, "HEAD", us, purl, flags)); 924 else if (strcmp(op, "RETR") == 0) 925 return (_http_request(url, "GET", us, purl, flags)); 926 /* 927 * Our HTTP code doesn't support PUT requests yet, so try 928 * a direct connection. 929 */ 930 } 931 932 /* connect to server */ 933 conn = _ftp_cached_connect(url, purl, flags); 934 if (purl) 935 fetchFreeURL(purl); 936 if (conn == NULL) 937 return (NULL); 938 939 /* change directory */ 940 if (_ftp_cwd(conn, url->doc) == -1) 941 return (NULL); 942 943 /* stat file */ 944 if (us && _ftp_stat(conn, url->doc, us) == -1 945 && fetchLastErrCode != FETCH_PROTO 946 && fetchLastErrCode != FETCH_UNAVAIL) 947 return (NULL); 948 949 /* just a stat */ 950 if (strcmp(op, "STAT") == 0) 951 return (FILE *)1; /* bogus return value */ 952 if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) 953 oflag = O_WRONLY; 954 else 955 oflag = O_RDONLY; 956 957 /* initiate the transfer */ 958 return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags)); 959 } 960 961 /* 962 * Get and stat file 963 */ 964 FILE * 965 fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) 966 { 967 return (_ftp_request(url, "RETR", us, _ftp_get_proxy(), flags)); 968 } 969 970 /* 971 * Get file 972 */ 973 FILE * 974 fetchGetFTP(struct url *url, const char *flags) 975 { 976 return (fetchXGetFTP(url, NULL, flags)); 977 } 978 979 /* 980 * Put file 981 */ 982 FILE * 983 fetchPutFTP(struct url *url, const char *flags) 984 { 985 986 return _ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, 987 _ftp_get_proxy(), flags); 988 } 989 990 /* 991 * Get file stats 992 */ 993 int 994 fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) 995 { 996 997 if (_ftp_request(url, "STAT", us, _ftp_get_proxy(), flags) == NULL) 998 return (-1); 999 return (0); 1000 } 1001 1002 /* 1003 * List a directory 1004 */ 1005 struct url_ent * 1006 fetchListFTP(struct url *url __unused, const char *flags __unused) 1007 { 1008 warnx("fetchListFTP(): not implemented"); 1009 return (NULL); 1010 } 1011