1 /*- 2 * Copyright (c) 1998-2004 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_DIRECTORY_CREATED 257 /* multiple meanings */ 94 #define FTP_FILE_CREATED 257 /* multiple meanings */ 95 #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ 96 #define FTP_NEED_PASSWORD 331 97 #define FTP_NEED_ACCOUNT 332 98 #define FTP_FILE_OK 350 99 #define FTP_SYNTAX_ERROR 500 100 #define FTP_PROTOCOL_ERROR 999 101 102 static struct url cached_host; 103 static conn_t *cached_connection; 104 105 #define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 106 && isdigit(foo[2]) \ 107 && (foo[3] == ' ' || foo[3] == '\0')) 108 #define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \ 109 && isdigit(foo[2]) && foo[3] == '-') 110 111 /* 112 * Translate IPv4 mapped IPv6 address to IPv4 address 113 */ 114 static void 115 unmappedaddr(struct sockaddr_in6 *sin6) 116 { 117 struct sockaddr_in *sin4; 118 u_int32_t addr; 119 int port; 120 121 if (sin6->sin6_family != AF_INET6 || 122 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 123 return; 124 sin4 = (struct sockaddr_in *)sin6; 125 addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 126 port = sin6->sin6_port; 127 memset(sin4, 0, sizeof(struct sockaddr_in)); 128 sin4->sin_addr.s_addr = addr; 129 sin4->sin_port = port; 130 sin4->sin_family = AF_INET; 131 sin4->sin_len = sizeof(struct sockaddr_in); 132 } 133 134 /* 135 * Get server response 136 */ 137 static int 138 _ftp_chkerr(conn_t *conn) 139 { 140 if (_fetch_getln(conn) == -1) { 141 _fetch_syserr(); 142 return (-1); 143 } 144 if (isftpinfo(conn->buf)) { 145 while (conn->buflen && !isftpreply(conn->buf)) { 146 if (_fetch_getln(conn) == -1) { 147 _fetch_syserr(); 148 return (-1); 149 } 150 } 151 } 152 153 while (conn->buflen && isspace(conn->buf[conn->buflen - 1])) 154 conn->buflen--; 155 conn->buf[conn->buflen] = '\0'; 156 157 if (!isftpreply(conn->buf)) { 158 _ftp_seterr(FTP_PROTOCOL_ERROR); 159 return (-1); 160 } 161 162 conn->err = (conn->buf[0] - '0') * 100 163 + (conn->buf[1] - '0') * 10 164 + (conn->buf[2] - '0'); 165 166 return (conn->err); 167 } 168 169 /* 170 * Send a command and check reply 171 */ 172 static int 173 _ftp_cmd(conn_t *conn, const char *fmt, ...) 174 { 175 va_list ap; 176 size_t len; 177 char *msg; 178 int r; 179 180 va_start(ap, fmt); 181 len = vasprintf(&msg, fmt, ap); 182 va_end(ap); 183 184 if (msg == NULL) { 185 errno = ENOMEM; 186 _fetch_syserr(); 187 return (-1); 188 } 189 190 r = _fetch_putln(conn, msg, len); 191 free(msg); 192 193 if (r == -1) { 194 _fetch_syserr(); 195 return (-1); 196 } 197 198 return (_ftp_chkerr(conn)); 199 } 200 201 /* 202 * Return a pointer to the filename part of a path 203 */ 204 static const char * 205 _ftp_filename(const char *file, int *len, int *type) 206 { 207 const char *s; 208 209 if ((s = strrchr(file, '/')) == NULL) 210 s = file; 211 else 212 s = s + 1; 213 *len = strlen(s); 214 if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { 215 *type = s[*len - 1]; 216 *len -= 7; 217 } else { 218 *type = '\0'; 219 } 220 return (s); 221 } 222 223 /* 224 * Get current working directory from the reply to a CWD, PWD or CDUP 225 * command. 226 */ 227 static int 228 _ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen) 229 { 230 char *src, *dst, *end; 231 int q; 232 233 if (conn->err != FTP_WORKING_DIRECTORY && 234 conn->err != FTP_FILE_ACTION_OK) 235 return (FTP_PROTOCOL_ERROR); 236 end = conn->buf + conn->buflen; 237 src = conn->buf + 4; 238 if (src >= end || *src++ != '"') 239 return (FTP_PROTOCOL_ERROR); 240 for (q = 0, dst = pwd; src < end && pwdlen--; ++src) { 241 if (!q && *src == '"') 242 q = 1; 243 else if (q && *src != '"') 244 break; 245 else if (q) 246 *dst++ = '"', q = 0; 247 else 248 *dst++ = *src; 249 } 250 if (!pwdlen) 251 return (FTP_PROTOCOL_ERROR); 252 *dst = '\0'; 253 #if 0 254 DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd)); 255 #endif 256 return (FTP_OK); 257 } 258 259 /* 260 * Change working directory to the directory that contains the specified 261 * file. 262 */ 263 static int 264 _ftp_cwd(conn_t *conn, const char *file) 265 { 266 const char *beg, *end; 267 char pwd[PATH_MAX]; 268 int e, i, len; 269 270 if ((end = strrchr(file, '/')) == NULL) 271 return (0); 272 if ((e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || 273 (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { 274 _ftp_seterr(e); 275 return (-1); 276 } 277 for (;;) { 278 len = strlen(pwd); 279 /* look for a common prefix */ 280 for (i = 0; i <= len && i <= end - file; ++i) 281 if (pwd[i] != file[i]) 282 break; 283 #if 0 284 DEBUG(fprintf(stderr, "have: [%.*s|%s]\n", i, pwd, pwd + i)); 285 DEBUG(fprintf(stderr, "want: [%.*s|%s]\n", i, file, file + i)); 286 #endif 287 if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/')) 288 break; 289 if ((e = _ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK || 290 (e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || 291 (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { 292 _ftp_seterr(e); 293 return (-1); 294 } 295 } 296 for (beg = file + i; beg < end; beg = file + i + 1) { 297 for (++i; file + i < end && file[i] != '/'; ++i) 298 /* nothing */ ; 299 e = _ftp_cmd(conn, "CWD %.*s", file + i - beg, beg); 300 if (e != FTP_FILE_ACTION_OK) { 301 _ftp_seterr(e); 302 return (-1); 303 } 304 } 305 return (0); 306 } 307 308 /* 309 * Set transfer mode and data type 310 */ 311 static int 312 _ftp_mode_type(conn_t *conn, int mode, int type) 313 { 314 int e; 315 316 switch (mode) { 317 case 0: 318 case 's': 319 mode = 'S'; 320 case 'S': 321 break; 322 default: 323 return (FTP_PROTOCOL_ERROR); 324 } 325 if ((e = _ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) { 326 if (mode == 'S') { 327 /* 328 * Stream mode is supposed to be the default - so 329 * much so that some servers not only do not 330 * support any other mode, but do not support the 331 * MODE command at all. 332 * 333 * If "MODE S" fails, it is unlikely that we 334 * previously succeeded in setting a different 335 * mode. Therefore, we simply hope that the 336 * server is already in the correct mode, and 337 * silently ignore the failure. 338 */ 339 } else { 340 return (e); 341 } 342 } 343 344 switch (type) { 345 case 0: 346 case 'i': 347 type = 'I'; 348 case 'I': 349 break; 350 case 'a': 351 type = 'A'; 352 case 'A': 353 break; 354 case 'd': 355 type = 'D'; 356 case 'D': 357 /* can't handle yet */ 358 default: 359 return (FTP_PROTOCOL_ERROR); 360 } 361 if ((e = _ftp_cmd(conn, "TYPE %c", type)) != FTP_OK) 362 return (e); 363 364 return (FTP_OK); 365 } 366 367 /* 368 * Request and parse file stats 369 */ 370 static int 371 _ftp_stat(conn_t *conn, const char *file, struct url_stat *us) 372 { 373 char *ln; 374 const char *filename; 375 int filenamelen, type; 376 struct tm tm; 377 time_t t; 378 int e; 379 380 us->size = -1; 381 us->atime = us->mtime = 0; 382 383 filename = _ftp_filename(file, &filenamelen, &type); 384 385 if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK) { 386 _ftp_seterr(e); 387 return (-1); 388 } 389 390 e = _ftp_cmd(conn, "SIZE %.*s", filenamelen, filename); 391 if (e != FTP_FILE_STATUS) { 392 _ftp_seterr(e); 393 return (-1); 394 } 395 for (ln = conn->buf + 4; *ln && isspace(*ln); ln++) 396 /* nothing */ ; 397 for (us->size = 0; *ln && isdigit(*ln); ln++) 398 us->size = us->size * 10 + *ln - '0'; 399 if (*ln && !isspace(*ln)) { 400 _ftp_seterr(FTP_PROTOCOL_ERROR); 401 us->size = -1; 402 return (-1); 403 } 404 if (us->size == 0) 405 us->size = -1; 406 DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size)); 407 408 e = _ftp_cmd(conn, "MDTM %.*s", filenamelen, filename); 409 if (e != FTP_FILE_STATUS) { 410 _ftp_seterr(e); 411 return (-1); 412 } 413 for (ln = conn->buf + 4; *ln && isspace(*ln); ln++) 414 /* nothing */ ; 415 switch (strspn(ln, "0123456789")) { 416 case 14: 417 break; 418 case 15: 419 ln++; 420 ln[0] = '2'; 421 ln[1] = '0'; 422 break; 423 default: 424 _ftp_seterr(FTP_PROTOCOL_ERROR); 425 return (-1); 426 } 427 if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 428 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 429 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 430 _ftp_seterr(FTP_PROTOCOL_ERROR); 431 return (-1); 432 } 433 tm.tm_mon--; 434 tm.tm_year -= 1900; 435 tm.tm_isdst = -1; 436 t = timegm(&tm); 437 if (t == (time_t)-1) 438 t = time(NULL); 439 us->mtime = t; 440 us->atime = t; 441 DEBUG(fprintf(stderr, 442 "last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n", 443 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 444 tm.tm_hour, tm.tm_min, tm.tm_sec)); 445 return (0); 446 } 447 448 /* 449 * I/O functions for FTP 450 */ 451 struct ftpio { 452 conn_t *cconn; /* Control connection */ 453 conn_t *dconn; /* Data connection */ 454 int dir; /* Direction */ 455 int eof; /* EOF reached */ 456 int err; /* Error code */ 457 }; 458 459 static int _ftp_readfn(void *, char *, int); 460 static int _ftp_writefn(void *, const char *, int); 461 static fpos_t _ftp_seekfn(void *, fpos_t, int); 462 static int _ftp_closefn(void *); 463 464 static int 465 _ftp_readfn(void *v, char *buf, int len) 466 { 467 struct ftpio *io; 468 int r; 469 470 io = (struct ftpio *)v; 471 if (io == NULL) { 472 errno = EBADF; 473 return (-1); 474 } 475 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { 476 errno = EBADF; 477 return (-1); 478 } 479 if (io->err) { 480 errno = io->err; 481 return (-1); 482 } 483 if (io->eof) 484 return (0); 485 r = _fetch_read(io->dconn, buf, len); 486 if (r > 0) 487 return (r); 488 if (r == 0) { 489 io->eof = 1; 490 return (0); 491 } 492 if (errno != EINTR) 493 io->err = errno; 494 return (-1); 495 } 496 497 static int 498 _ftp_writefn(void *v, const char *buf, int len) 499 { 500 struct ftpio *io; 501 int w; 502 503 io = (struct ftpio *)v; 504 if (io == NULL) { 505 errno = EBADF; 506 return (-1); 507 } 508 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { 509 errno = EBADF; 510 return (-1); 511 } 512 if (io->err) { 513 errno = io->err; 514 return (-1); 515 } 516 w = _fetch_write(io->dconn, buf, len); 517 if (w >= 0) 518 return (w); 519 if (errno != EINTR) 520 io->err = errno; 521 return (-1); 522 } 523 524 static fpos_t 525 _ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused) 526 { 527 struct ftpio *io; 528 529 io = (struct ftpio *)v; 530 if (io == NULL) { 531 errno = EBADF; 532 return (-1); 533 } 534 errno = ESPIPE; 535 return (-1); 536 } 537 538 static int 539 _ftp_closefn(void *v) 540 { 541 struct ftpio *io; 542 int r; 543 544 io = (struct ftpio *)v; 545 if (io == NULL) { 546 errno = EBADF; 547 return (-1); 548 } 549 if (io->dir == -1) 550 return (0); 551 if (io->cconn == NULL || io->dconn == NULL) { 552 errno = EBADF; 553 return (-1); 554 } 555 _fetch_close(io->dconn); 556 io->dir = -1; 557 io->dconn = NULL; 558 DEBUG(fprintf(stderr, "Waiting for final status\n")); 559 r = _ftp_chkerr(io->cconn); 560 if (io->cconn == cached_connection && io->cconn->ref == 1) 561 cached_connection = NULL; 562 _fetch_close(io->cconn); 563 free(io); 564 return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1; 565 } 566 567 static FILE * 568 _ftp_setup(conn_t *cconn, conn_t *dconn, int mode) 569 { 570 struct ftpio *io; 571 FILE *f; 572 573 if (cconn == NULL || dconn == NULL) 574 return (NULL); 575 if ((io = malloc(sizeof(*io))) == NULL) 576 return (NULL); 577 io->cconn = cconn; 578 io->dconn = dconn; 579 io->dir = mode; 580 io->eof = io->err = 0; 581 f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn); 582 if (f == NULL) 583 free(io); 584 return (f); 585 } 586 587 /* 588 * Transfer file 589 */ 590 static FILE * 591 _ftp_transfer(conn_t *conn, const char *oper, const char *file, 592 int mode, off_t offset, const char *flags) 593 { 594 struct sockaddr_storage sa; 595 struct sockaddr_in6 *sin6; 596 struct sockaddr_in *sin4; 597 const char *filename; 598 int filenamelen, type; 599 int low, pasv, verbose; 600 int e, sd = -1; 601 socklen_t l; 602 char *s; 603 FILE *df; 604 605 /* check flags */ 606 low = CHECK_FLAG('l'); 607 pasv = CHECK_FLAG('p'); 608 verbose = CHECK_FLAG('v'); 609 610 /* passive mode */ 611 if (!pasv) 612 pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && 613 strncasecmp(s, "no", 2) != 0); 614 615 /* isolate filename */ 616 filename = _ftp_filename(file, &filenamelen, &type); 617 618 /* set transfer mode and data type */ 619 if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK) 620 goto ouch; 621 622 /* find our own address, bind, and listen */ 623 l = sizeof(sa); 624 if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) 625 goto sysouch; 626 if (sa.ss_family == AF_INET6) 627 unmappedaddr((struct sockaddr_in6 *)&sa); 628 629 /* open data socket */ 630 if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 631 _fetch_syserr(); 632 return (NULL); 633 } 634 635 if (pasv) { 636 u_char addr[64]; 637 char *ln, *p; 638 unsigned int i; 639 int port; 640 641 /* send PASV command */ 642 if (verbose) 643 _fetch_info("setting passive mode"); 644 switch (sa.ss_family) { 645 case AF_INET: 646 if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) 647 goto ouch; 648 break; 649 case AF_INET6: 650 if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { 651 if (e == -1) 652 goto ouch; 653 if ((e = _ftp_cmd(conn, "LPSV")) != 654 FTP_LPASSIVE_MODE) 655 goto ouch; 656 } 657 break; 658 default: 659 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 660 goto ouch; 661 } 662 663 /* 664 * Find address and port number. The reply to the PASV command 665 * is IMHO the one and only weak point in the FTP protocol. 666 */ 667 ln = conn->buf; 668 switch (e) { 669 case FTP_PASSIVE_MODE: 670 case FTP_LPASSIVE_MODE: 671 for (p = ln + 3; *p && !isdigit(*p); p++) 672 /* nothing */ ; 673 if (!*p) { 674 e = FTP_PROTOCOL_ERROR; 675 goto ouch; 676 } 677 l = (e == FTP_PASSIVE_MODE ? 6 : 21); 678 for (i = 0; *p && i < l; i++, p++) 679 addr[i] = strtol(p, &p, 10); 680 if (i < l) { 681 e = FTP_PROTOCOL_ERROR; 682 goto ouch; 683 } 684 break; 685 case FTP_EPASSIVE_MODE: 686 for (p = ln + 3; *p && *p != '('; p++) 687 /* nothing */ ; 688 if (!*p) { 689 e = FTP_PROTOCOL_ERROR; 690 goto ouch; 691 } 692 ++p; 693 if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 694 &port, &addr[3]) != 5 || 695 addr[0] != addr[1] || 696 addr[0] != addr[2] || addr[0] != addr[3]) { 697 e = FTP_PROTOCOL_ERROR; 698 goto ouch; 699 } 700 break; 701 } 702 703 /* seek to required offset */ 704 if (offset) 705 if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK) 706 goto sysouch; 707 708 /* construct sockaddr for data socket */ 709 l = sizeof(sa); 710 if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) 711 goto sysouch; 712 if (sa.ss_family == AF_INET6) 713 unmappedaddr((struct sockaddr_in6 *)&sa); 714 switch (sa.ss_family) { 715 case AF_INET6: 716 sin6 = (struct sockaddr_in6 *)&sa; 717 if (e == FTP_EPASSIVE_MODE) 718 sin6->sin6_port = htons(port); 719 else { 720 bcopy(addr + 2, (char *)&sin6->sin6_addr, 16); 721 bcopy(addr + 19, (char *)&sin6->sin6_port, 2); 722 } 723 break; 724 case AF_INET: 725 sin4 = (struct sockaddr_in *)&sa; 726 if (e == FTP_EPASSIVE_MODE) 727 sin4->sin_port = htons(port); 728 else { 729 bcopy(addr, (char *)&sin4->sin_addr, 4); 730 bcopy(addr + 4, (char *)&sin4->sin_port, 2); 731 } 732 break; 733 default: 734 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 735 break; 736 } 737 738 /* connect to data port */ 739 if (verbose) 740 _fetch_info("opening data connection"); 741 if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) 742 goto sysouch; 743 744 /* make the server initiate the transfer */ 745 if (verbose) 746 _fetch_info("initiating transfer"); 747 e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename); 748 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 749 goto ouch; 750 751 } else { 752 u_int32_t a; 753 u_short p; 754 int arg, d; 755 char *ap; 756 char hname[INET6_ADDRSTRLEN]; 757 758 switch (sa.ss_family) { 759 case AF_INET6: 760 ((struct sockaddr_in6 *)&sa)->sin6_port = 0; 761 #ifdef IPV6_PORTRANGE 762 arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; 763 if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 764 (char *)&arg, sizeof(arg)) == -1) 765 goto sysouch; 766 #endif 767 break; 768 case AF_INET: 769 ((struct sockaddr_in *)&sa)->sin_port = 0; 770 arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; 771 if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 772 (char *)&arg, sizeof(arg)) == -1) 773 goto sysouch; 774 break; 775 } 776 if (verbose) 777 _fetch_info("binding data socket"); 778 if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) 779 goto sysouch; 780 if (listen(sd, 1) == -1) 781 goto sysouch; 782 783 /* find what port we're on and tell the server */ 784 if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) 785 goto sysouch; 786 switch (sa.ss_family) { 787 case AF_INET: 788 sin4 = (struct sockaddr_in *)&sa; 789 a = ntohl(sin4->sin_addr.s_addr); 790 p = ntohs(sin4->sin_port); 791 e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", 792 (a >> 24) & 0xff, (a >> 16) & 0xff, 793 (a >> 8) & 0xff, a & 0xff, 794 (p >> 8) & 0xff, p & 0xff); 795 break; 796 case AF_INET6: 797 #define UC(b) (((int)b)&0xff) 798 e = -1; 799 sin6 = (struct sockaddr_in6 *)&sa; 800 sin6->sin6_scope_id = 0; 801 if (getnameinfo((struct sockaddr *)&sa, sa.ss_len, 802 hname, sizeof(hname), 803 NULL, 0, NI_NUMERICHOST) == 0) { 804 e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, 805 htons(sin6->sin6_port)); 806 if (e == -1) 807 goto ouch; 808 } 809 if (e != FTP_OK) { 810 ap = (char *)&sin6->sin6_addr; 811 e = _ftp_cmd(conn, 812 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 813 6, 16, 814 UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 815 UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 816 UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 817 UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 818 2, 819 (ntohs(sin6->sin6_port) >> 8) & 0xff, 820 ntohs(sin6->sin6_port) & 0xff); 821 } 822 break; 823 default: 824 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 825 goto ouch; 826 } 827 if (e != FTP_OK) 828 goto ouch; 829 830 /* seek to required offset */ 831 if (offset) 832 if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK) 833 goto sysouch; 834 835 /* make the server initiate the transfer */ 836 if (verbose) 837 _fetch_info("initiating transfer"); 838 e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename); 839 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 840 goto ouch; 841 842 /* accept the incoming connection and go to town */ 843 if ((d = accept(sd, NULL, NULL)) == -1) 844 goto sysouch; 845 close(sd); 846 sd = d; 847 } 848 849 if ((df = _ftp_setup(conn, _fetch_reopen(sd), mode)) == NULL) 850 goto sysouch; 851 return (df); 852 853 sysouch: 854 _fetch_syserr(); 855 if (sd >= 0) 856 close(sd); 857 return (NULL); 858 859 ouch: 860 if (e != -1) 861 _ftp_seterr(e); 862 if (sd >= 0) 863 close(sd); 864 return (NULL); 865 } 866 867 /* 868 * Authenticate 869 */ 870 static int 871 _ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) 872 { 873 const char *user, *pwd, *logname; 874 char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 875 int e, len; 876 877 /* XXX FTP_AUTH, and maybe .netrc */ 878 879 /* send user name and password */ 880 if (url->user[0] == '\0') 881 _fetch_netrc_auth(url); 882 user = url->user; 883 if (*user == '\0') 884 user = getenv("FTP_LOGIN"); 885 if (user == NULL || *user == '\0') 886 user = FTP_ANONYMOUS_USER; 887 if (purl && url->port == _fetch_default_port(url->scheme)) 888 e = _ftp_cmd(conn, "USER %s@%s", user, url->host); 889 else if (purl) 890 e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port); 891 else 892 e = _ftp_cmd(conn, "USER %s", user); 893 894 /* did the server request a password? */ 895 if (e == FTP_NEED_PASSWORD) { 896 pwd = url->pwd; 897 if (*pwd == '\0') 898 pwd = getenv("FTP_PASSWORD"); 899 if (pwd == NULL || *pwd == '\0') { 900 if ((logname = getlogin()) == 0) 901 logname = FTP_ANONYMOUS_USER; 902 if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0) 903 len = 0; 904 else if (len > MAXLOGNAME) 905 len = MAXLOGNAME; 906 gethostname(pbuf + len, sizeof(pbuf) - len); 907 pwd = pbuf; 908 } 909 e = _ftp_cmd(conn, "PASS %s", pwd); 910 } 911 912 return (e); 913 } 914 915 /* 916 * Log on to FTP server 917 */ 918 static conn_t * 919 _ftp_connect(struct url *url, struct url *purl, const char *flags) 920 { 921 conn_t *conn; 922 int e, direct, verbose; 923 #ifdef INET6 924 int af = AF_UNSPEC; 925 #else 926 int af = AF_INET; 927 #endif 928 929 direct = CHECK_FLAG('d'); 930 verbose = CHECK_FLAG('v'); 931 if (CHECK_FLAG('4')) 932 af = AF_INET; 933 else if (CHECK_FLAG('6')) 934 af = AF_INET6; 935 936 if (direct) 937 purl = NULL; 938 939 /* check for proxy */ 940 if (purl) { 941 /* XXX proxy authentication! */ 942 conn = _fetch_connect(purl->host, purl->port, af, verbose); 943 } else { 944 /* no proxy, go straight to target */ 945 conn = _fetch_connect(url->host, url->port, af, verbose); 946 purl = NULL; 947 } 948 949 /* check connection */ 950 if (conn == NULL) 951 /* _fetch_connect() has already set an error code */ 952 return (NULL); 953 954 /* expect welcome message */ 955 if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY) 956 goto fouch; 957 958 /* authenticate */ 959 if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) 960 goto fouch; 961 962 /* done */ 963 return (conn); 964 965 fouch: 966 if (e != -1) 967 _ftp_seterr(e); 968 _fetch_close(conn); 969 return (NULL); 970 } 971 972 /* 973 * Disconnect from server 974 */ 975 static void 976 _ftp_disconnect(conn_t *conn) 977 { 978 (void)_ftp_cmd(conn, "QUIT"); 979 if (conn == cached_connection && conn->ref == 1) 980 cached_connection = NULL; 981 _fetch_close(conn); 982 } 983 984 /* 985 * Check if we're already connected 986 */ 987 static int 988 _ftp_isconnected(struct url *url) 989 { 990 return (cached_connection 991 && (strcmp(url->host, cached_host.host) == 0) 992 && (strcmp(url->user, cached_host.user) == 0) 993 && (strcmp(url->pwd, cached_host.pwd) == 0) 994 && (url->port == cached_host.port)); 995 } 996 997 /* 998 * Check the cache, reconnect if no luck 999 */ 1000 static conn_t * 1001 _ftp_cached_connect(struct url *url, struct url *purl, const char *flags) 1002 { 1003 conn_t *conn; 1004 int e; 1005 1006 /* set default port */ 1007 if (!url->port) 1008 url->port = _fetch_default_port(url->scheme); 1009 1010 /* try to use previously cached connection */ 1011 if (_ftp_isconnected(url)) { 1012 e = _ftp_cmd(cached_connection, "NOOP"); 1013 if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 1014 return (_fetch_ref(cached_connection)); 1015 } 1016 1017 /* connect to server */ 1018 if ((conn = _ftp_connect(url, purl, flags)) == NULL) 1019 return (NULL); 1020 if (cached_connection) 1021 _ftp_disconnect(cached_connection); 1022 cached_connection = _fetch_ref(conn); 1023 memcpy(&cached_host, url, sizeof(*url)); 1024 return (conn); 1025 } 1026 1027 /* 1028 * Check the proxy settings 1029 */ 1030 static struct url * 1031 _ftp_get_proxy(const char *flags) 1032 { 1033 struct url *purl; 1034 char *p; 1035 1036 if (flags != NULL && strchr(flags, 'd') != NULL) 1037 return (NULL); 1038 if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) || 1039 (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 1040 *p && (purl = fetchParseURL(p)) != NULL) { 1041 if (!*purl->scheme) { 1042 if (getenv("FTP_PROXY") || getenv("ftp_proxy")) 1043 strcpy(purl->scheme, SCHEME_FTP); 1044 else 1045 strcpy(purl->scheme, SCHEME_HTTP); 1046 } 1047 if (!purl->port) 1048 purl->port = _fetch_default_proxy_port(purl->scheme); 1049 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 1050 strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 1051 return (purl); 1052 fetchFreeURL(purl); 1053 } 1054 return (NULL); 1055 } 1056 1057 /* 1058 * Process an FTP request 1059 */ 1060 FILE * 1061 _ftp_request(struct url *url, const char *op, struct url_stat *us, 1062 struct url *purl, const char *flags) 1063 { 1064 conn_t *conn; 1065 int oflag; 1066 1067 /* check if we should use HTTP instead */ 1068 if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 1069 if (strcmp(op, "STAT") == 0) 1070 return (_http_request(url, "HEAD", us, purl, flags)); 1071 else if (strcmp(op, "RETR") == 0) 1072 return (_http_request(url, "GET", us, purl, flags)); 1073 /* 1074 * Our HTTP code doesn't support PUT requests yet, so try 1075 * a direct connection. 1076 */ 1077 } 1078 1079 /* connect to server */ 1080 conn = _ftp_cached_connect(url, purl, flags); 1081 if (purl) 1082 fetchFreeURL(purl); 1083 if (conn == NULL) 1084 return (NULL); 1085 1086 /* change directory */ 1087 if (_ftp_cwd(conn, url->doc) == -1) 1088 return (NULL); 1089 1090 /* stat file */ 1091 if (us && _ftp_stat(conn, url->doc, us) == -1 1092 && fetchLastErrCode != FETCH_PROTO 1093 && fetchLastErrCode != FETCH_UNAVAIL) 1094 return (NULL); 1095 1096 /* just a stat */ 1097 if (strcmp(op, "STAT") == 0) 1098 return (FILE *)1; /* bogus return value */ 1099 if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) 1100 oflag = O_WRONLY; 1101 else 1102 oflag = O_RDONLY; 1103 1104 /* initiate the transfer */ 1105 return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags)); 1106 } 1107 1108 /* 1109 * Get and stat file 1110 */ 1111 FILE * 1112 fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) 1113 { 1114 return (_ftp_request(url, "RETR", us, _ftp_get_proxy(flags), flags)); 1115 } 1116 1117 /* 1118 * Get file 1119 */ 1120 FILE * 1121 fetchGetFTP(struct url *url, const char *flags) 1122 { 1123 return (fetchXGetFTP(url, NULL, flags)); 1124 } 1125 1126 /* 1127 * Put file 1128 */ 1129 FILE * 1130 fetchPutFTP(struct url *url, const char *flags) 1131 { 1132 1133 return (_ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, 1134 _ftp_get_proxy(flags), flags)); 1135 } 1136 1137 /* 1138 * Get file stats 1139 */ 1140 int 1141 fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) 1142 { 1143 FILE *f; 1144 1145 f = _ftp_request(url, "STAT", us, _ftp_get_proxy(flags), flags); 1146 if (f == NULL) 1147 return (-1); 1148 return (0); 1149 } 1150 1151 /* 1152 * List a directory 1153 */ 1154 struct url_ent * 1155 fetchListFTP(struct url *url __unused, const char *flags __unused) 1156 { 1157 warnx("fetchListFTP(): not implemented"); 1158 return (NULL); 1159 } 1160