1 /* $NetBSD: ftp.c,v 1.1.1.11 2010/03/24 20:51:44 joerg Exp $ */ 2 /*- 3 * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav 4 * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $ 31 */ 32 33 /* 34 * Portions of this code were taken from or based on ftpio.c: 35 * 36 * ---------------------------------------------------------------------------- 37 * "THE BEER-WARE LICENSE" (Revision 42): 38 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 39 * can do whatever you want with this stuff. If we meet some day, and you think 40 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 41 * ---------------------------------------------------------------------------- 42 * 43 * Major Changelog: 44 * 45 * Dag-Erling Co�dan Sm�rgrav 46 * 9 Jun 1998 47 * 48 * Incorporated into libfetch 49 * 50 * Jordan K. Hubbard 51 * 17 Jan 1996 52 * 53 * Turned inside out. Now returns xfers as new file ids, not as a special 54 * `state' of FTP_t 55 * 56 * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ 57 * 58 */ 59 60 #ifdef __linux__ 61 /* Keep this down to Linux, it can create surprises else where. */ 62 #define _GNU_SOURCE 63 #endif 64 65 #if HAVE_CONFIG_H 66 #include "config.h" 67 #endif 68 #ifndef NETBSD 69 #include <nbcompat.h> 70 #endif 71 72 #include <sys/types.h> 73 #include <sys/socket.h> 74 75 #include <netinet/in.h> 76 #include <arpa/inet.h> 77 78 #include <ctype.h> 79 #include <errno.h> 80 #include <fcntl.h> 81 #if defined(HAVE_INTTYPES_H) || defined(NETBSD) 82 #include <inttypes.h> 83 #endif 84 #include <stdarg.h> 85 #ifndef NETBSD 86 #include <nbcompat/netdb.h> 87 #include <nbcompat/stdio.h> 88 #else 89 #include <netdb.h> 90 #include <stdio.h> 91 #endif 92 #include <stdlib.h> 93 #include <string.h> 94 #include <time.h> 95 #include <unistd.h> 96 97 #include "fetch.h" 98 #include "common.h" 99 #include "ftperr.h" 100 101 #define FTP_ANONYMOUS_USER "anonymous" 102 103 #define FTP_CONNECTION_ALREADY_OPEN 125 104 #define FTP_OPEN_DATA_CONNECTION 150 105 #define FTP_OK 200 106 #define FTP_FILE_STATUS 213 107 #define FTP_SERVICE_READY 220 108 #define FTP_TRANSFER_COMPLETE 226 109 #define FTP_PASSIVE_MODE 227 110 #define FTP_LPASSIVE_MODE 228 111 #define FTP_EPASSIVE_MODE 229 112 #define FTP_LOGGED_IN 230 113 #define FTP_FILE_ACTION_OK 250 114 #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ 115 #define FTP_FILE_CREATED 257 /* multiple meanings */ 116 #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ 117 #define FTP_NEED_PASSWORD 331 118 #define FTP_NEED_ACCOUNT 332 119 #define FTP_FILE_OK 350 120 #define FTP_SYNTAX_ERROR 500 121 #define FTP_PROTOCOL_ERROR 999 122 123 #define isftpreply(foo) \ 124 (isdigit((unsigned char)foo[0]) && \ 125 isdigit((unsigned char)foo[1]) && \ 126 isdigit((unsigned char)foo[2]) && \ 127 (foo[3] == ' ' || foo[3] == '\0')) 128 #define isftpinfo(foo) \ 129 (isdigit((unsigned char)foo[0]) && \ 130 isdigit((unsigned char)foo[1]) && \ 131 isdigit((unsigned char)foo[2]) && \ 132 foo[3] == '-') 133 134 /* 135 * Translate IPv4 mapped IPv6 address to IPv4 address 136 */ 137 static void 138 unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len) 139 { 140 struct sockaddr_in *sin4; 141 uint32_t addr; 142 int port; 143 144 if (sin6->sin6_family != AF_INET6 || 145 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 146 return; 147 sin4 = (struct sockaddr_in *)sin6; 148 addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12]; 149 port = sin6->sin6_port; 150 memset(sin4, 0, sizeof(struct sockaddr_in)); 151 sin4->sin_addr.s_addr = addr; 152 sin4->sin_port = port; 153 sin4->sin_family = AF_INET; 154 *len = sizeof(struct sockaddr_in); 155 #ifdef HAVE_SA_LEN 156 sin4->sin_len = sizeof(struct sockaddr_in); 157 #endif 158 } 159 160 /* 161 * Get server response 162 */ 163 static int 164 ftp_chkerr(conn_t *conn) 165 { 166 if (fetch_getln(conn) == -1) { 167 fetch_syserr(); 168 return (-1); 169 } 170 if (isftpinfo(conn->buf)) { 171 while (conn->buflen && !isftpreply(conn->buf)) { 172 if (fetch_getln(conn) == -1) { 173 fetch_syserr(); 174 return (-1); 175 } 176 } 177 } 178 179 while (conn->buflen && 180 isspace((unsigned char)conn->buf[conn->buflen - 1])) 181 conn->buflen--; 182 conn->buf[conn->buflen] = '\0'; 183 184 if (!isftpreply(conn->buf)) { 185 ftp_seterr(FTP_PROTOCOL_ERROR); 186 return (-1); 187 } 188 189 conn->err = (conn->buf[0] - '0') * 100 190 + (conn->buf[1] - '0') * 10 191 + (conn->buf[2] - '0'); 192 193 return (conn->err); 194 } 195 196 /* 197 * Send a command and check reply 198 */ 199 static int 200 ftp_cmd(conn_t *conn, const char *fmt, ...) 201 { 202 va_list ap; 203 size_t len; 204 char *msg; 205 int r; 206 207 va_start(ap, fmt); 208 len = vasprintf(&msg, fmt, ap); 209 va_end(ap); 210 211 if (msg == NULL) { 212 errno = ENOMEM; 213 fetch_syserr(); 214 return (-1); 215 } 216 217 r = fetch_write(conn, msg, len); 218 free(msg); 219 220 if (r == -1) { 221 fetch_syserr(); 222 return (-1); 223 } 224 225 return (ftp_chkerr(conn)); 226 } 227 228 /* 229 * Return a pointer to the filename part of a path 230 */ 231 static const char * 232 ftp_filename(const char *file, int *len, int *type, int subdir) 233 { 234 const char *s; 235 236 if ((s = strrchr(file, '/')) == NULL || subdir) 237 s = file; 238 else 239 s = s + 1; 240 *len = strlen(s); 241 if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { 242 *type = s[*len - 1]; 243 *len -= 7; 244 } else { 245 *type = '\0'; 246 } 247 return (s); 248 } 249 250 /* 251 * Get current working directory from the reply to a CWD, PWD or CDUP 252 * command. 253 */ 254 static int 255 ftp_pwd(conn_t *conn, char **pwd) 256 { 257 char *src, *dst, *end; 258 int q; 259 260 if (conn->err != FTP_WORKING_DIRECTORY && 261 conn->err != FTP_FILE_ACTION_OK) 262 return (FTP_PROTOCOL_ERROR); 263 end = conn->buf + conn->buflen; 264 src = conn->buf + 4; 265 if (src >= end || *src++ != '"') 266 return (FTP_PROTOCOL_ERROR); 267 *pwd = malloc(end - src + 1); 268 if (*pwd == NULL) 269 return (FTP_PROTOCOL_ERROR); 270 for (q = 0, dst = *pwd; src < end; ++src) { 271 if (!q && *src == '"') 272 q = 1; 273 else if (q && *src != '"') 274 break; 275 else if (q) 276 *dst++ = '"', q = 0; 277 else 278 *dst++ = *src; 279 } 280 *dst = '\0'; 281 if (**pwd != '/') { 282 free(*pwd); 283 *pwd = NULL; 284 return (FTP_PROTOCOL_ERROR); 285 } 286 return (FTP_OK); 287 } 288 289 /* 290 * Change working directory to the directory that contains the specified 291 * file. 292 */ 293 static int 294 ftp_cwd(conn_t *conn, const char *path, int subdir) 295 { 296 const char *beg, *end; 297 char *pwd, *dst; 298 int e, i, len; 299 300 if (*path != '/') { 301 ftp_seterr(501); 302 return (-1); 303 } 304 ++path; 305 306 /* Simple case: still in the home directory and no directory change. */ 307 if (conn->ftp_home == NULL && strchr(path, '/') == NULL && 308 (!subdir || *path == '\0')) 309 return 0; 310 311 if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || 312 (e = ftp_pwd(conn, &pwd)) != FTP_OK) { 313 ftp_seterr(e); 314 return (-1); 315 } 316 if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) { 317 fetch_syserr(); 318 free(pwd); 319 return (-1); 320 } 321 if (*path == '/') { 322 while (path[1] == '/') 323 ++path; 324 dst = strdup(path); 325 } else if (strcmp(conn->ftp_home, "/") == 0) { 326 dst = strdup(path - 1); 327 } else { 328 asprintf(&dst, "%s/%s", conn->ftp_home, path); 329 } 330 if (dst == NULL) { 331 fetch_syserr(); 332 free(pwd); 333 return (-1); 334 } 335 336 if (subdir) 337 end = dst + strlen(dst); 338 else 339 end = strrchr(dst, '/'); 340 341 for (;;) { 342 len = strlen(pwd); 343 344 /* Look for a common prefix between PWD and dir to fetch. */ 345 for (i = 0; i <= len && i <= end - dst; ++i) 346 if (pwd[i] != dst[i]) 347 break; 348 /* Keep going up a dir until we have a matching prefix. */ 349 if (strcmp(pwd, "/") == 0) 350 break; 351 if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/')) 352 break; 353 free(pwd); 354 if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK || 355 (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || 356 (e = ftp_pwd(conn, &pwd)) != FTP_OK) { 357 ftp_seterr(e); 358 free(dst); 359 return (-1); 360 } 361 } 362 free(pwd); 363 364 #ifdef FTP_COMBINE_CWDS 365 /* Skip leading slashes, even "////". */ 366 for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i) 367 /* nothing */ ; 368 369 /* If there is no trailing dir, we're already there. */ 370 if (beg >= end) { 371 free(dst); 372 return (0); 373 } 374 375 /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ 376 e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg); 377 if (e == FTP_FILE_ACTION_OK) { 378 free(dst); 379 return (0); 380 } 381 #endif /* FTP_COMBINE_CWDS */ 382 383 /* That didn't work so go back to legacy behavior (multiple CWDs). */ 384 for (beg = dst + i; beg < end; beg = dst + i + 1) { 385 while (*beg == '/') 386 ++beg, ++i; 387 for (++i; dst + i < end && dst[i] != '/'; ++i) 388 /* nothing */ ; 389 e = ftp_cmd(conn, "CWD %.*s\r\n", dst + i - beg, beg); 390 if (e != FTP_FILE_ACTION_OK) { 391 free(dst); 392 ftp_seterr(e); 393 return (-1); 394 } 395 } 396 free(dst); 397 return (0); 398 } 399 400 /* 401 * Set transfer mode and data type 402 */ 403 static int 404 ftp_mode_type(conn_t *conn, int mode, int type) 405 { 406 int e; 407 408 switch (mode) { 409 case 0: 410 case 's': 411 mode = 'S'; 412 case 'S': 413 break; 414 default: 415 return (FTP_PROTOCOL_ERROR); 416 } 417 if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) { 418 if (mode == 'S') { 419 /* 420 * Stream mode is supposed to be the default - so 421 * much so that some servers not only do not 422 * support any other mode, but do not support the 423 * MODE command at all. 424 * 425 * If "MODE S" fails, it is unlikely that we 426 * previously succeeded in setting a different 427 * mode. Therefore, we simply hope that the 428 * server is already in the correct mode, and 429 * silently ignore the failure. 430 */ 431 } else { 432 return (e); 433 } 434 } 435 436 switch (type) { 437 case 0: 438 case 'i': 439 type = 'I'; 440 case 'I': 441 break; 442 case 'a': 443 type = 'A'; 444 case 'A': 445 break; 446 case 'd': 447 type = 'D'; 448 case 'D': 449 /* can't handle yet */ 450 default: 451 return (FTP_PROTOCOL_ERROR); 452 } 453 if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK) 454 return (e); 455 456 return (FTP_OK); 457 } 458 459 /* 460 * Request and parse file stats 461 */ 462 static int 463 ftp_stat(conn_t *conn, const char *file, struct url_stat *us) 464 { 465 char *ln; 466 const char *filename; 467 int filenamelen, type; 468 struct tm tm; 469 time_t t; 470 int e; 471 472 us->size = -1; 473 us->atime = us->mtime = 0; 474 475 filename = ftp_filename(file, &filenamelen, &type, 0); 476 477 if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { 478 ftp_seterr(e); 479 return (-1); 480 } 481 482 e = ftp_cmd(conn, "SIZE %.*s\r\n", filenamelen, filename); 483 if (e != FTP_FILE_STATUS) { 484 ftp_seterr(e); 485 return (-1); 486 } 487 for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) 488 /* nothing */ ; 489 for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) 490 us->size = us->size * 10 + *ln - '0'; 491 if (*ln && !isspace((unsigned char)*ln)) { 492 ftp_seterr(FTP_PROTOCOL_ERROR); 493 us->size = -1; 494 return (-1); 495 } 496 if (us->size == 0) 497 us->size = -1; 498 499 e = ftp_cmd(conn, "MDTM %.*s\r\n", filenamelen, filename); 500 if (e != FTP_FILE_STATUS) { 501 ftp_seterr(e); 502 return (-1); 503 } 504 for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) 505 /* nothing */ ; 506 switch (strspn(ln, "0123456789")) { 507 case 14: 508 break; 509 case 15: 510 ln++; 511 ln[0] = '2'; 512 ln[1] = '0'; 513 break; 514 default: 515 ftp_seterr(FTP_PROTOCOL_ERROR); 516 return (-1); 517 } 518 if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 519 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 520 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 521 ftp_seterr(FTP_PROTOCOL_ERROR); 522 return (-1); 523 } 524 tm.tm_mon--; 525 tm.tm_year -= 1900; 526 tm.tm_isdst = -1; 527 t = timegm(&tm); 528 if (t == (time_t)-1) 529 t = time(NULL); 530 us->mtime = t; 531 us->atime = t; 532 533 return (0); 534 } 535 536 /* 537 * I/O functions for FTP 538 */ 539 struct ftpio { 540 conn_t *cconn; /* Control connection */ 541 conn_t *dconn; /* Data connection */ 542 int dir; /* Direction */ 543 int eof; /* EOF reached */ 544 int err; /* Error code */ 545 }; 546 547 static ssize_t ftp_readfn(void *, void *, size_t); 548 static ssize_t ftp_writefn(void *, const void *, size_t); 549 static void ftp_closefn(void *); 550 551 static ssize_t 552 ftp_readfn(void *v, void *buf, size_t len) 553 { 554 struct ftpio *io; 555 int r; 556 557 io = (struct ftpio *)v; 558 if (io == NULL) { 559 errno = EBADF; 560 return (-1); 561 } 562 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { 563 errno = EBADF; 564 return (-1); 565 } 566 if (io->err) { 567 errno = io->err; 568 return (-1); 569 } 570 if (io->eof) 571 return (0); 572 r = fetch_read(io->dconn, buf, len); 573 if (r > 0) 574 return (r); 575 if (r == 0) { 576 io->eof = 1; 577 return (0); 578 } 579 if (errno != EINTR) 580 io->err = errno; 581 return (-1); 582 } 583 584 static ssize_t 585 ftp_writefn(void *v, const void *buf, size_t len) 586 { 587 struct ftpio *io; 588 int w; 589 590 io = (struct ftpio *)v; 591 if (io == NULL) { 592 errno = EBADF; 593 return (-1); 594 } 595 if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { 596 errno = EBADF; 597 return (-1); 598 } 599 if (io->err) { 600 errno = io->err; 601 return (-1); 602 } 603 w = fetch_write(io->dconn, buf, len); 604 if (w >= 0) 605 return (w); 606 if (errno != EINTR) 607 io->err = errno; 608 return (-1); 609 } 610 611 static int 612 ftp_disconnect(conn_t *conn) 613 { 614 ftp_cmd(conn, "QUIT\r\n"); 615 return fetch_close(conn); 616 } 617 618 static void 619 ftp_closefn(void *v) 620 { 621 struct ftpio *io; 622 int r; 623 624 io = (struct ftpio *)v; 625 if (io == NULL) { 626 errno = EBADF; 627 return; 628 } 629 if (io->dir == -1) 630 return; 631 if (io->cconn == NULL || io->dconn == NULL) { 632 errno = EBADF; 633 return; 634 } 635 fetch_close(io->dconn); 636 io->dconn = NULL; 637 io->dir = -1; 638 r = ftp_chkerr(io->cconn); 639 fetch_cache_put(io->cconn, ftp_disconnect); 640 free(io); 641 return; 642 } 643 644 static fetchIO * 645 ftp_setup(conn_t *cconn, conn_t *dconn, int mode) 646 { 647 struct ftpio *io; 648 fetchIO *f; 649 650 if (cconn == NULL || dconn == NULL) 651 return (NULL); 652 if ((io = malloc(sizeof(*io))) == NULL) 653 return (NULL); 654 io->cconn = cconn; 655 io->dconn = dconn; 656 io->dir = mode; 657 io->eof = io->err = 0; 658 f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); 659 if (f == NULL) 660 free(io); 661 return (f); 662 } 663 664 /* 665 * Transfer file 666 */ 667 static fetchIO * 668 ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, 669 int mode, off_t offset, const char *flags) 670 { 671 union anonymous { 672 struct sockaddr_storage ss; 673 struct sockaddr sa; 674 struct sockaddr_in6 sin6; 675 struct sockaddr_in sin4; 676 } u; 677 const char *bindaddr; 678 const char *filename; 679 int filenamelen, type; 680 int low, pasv, verbose; 681 int e, sd = -1; 682 socklen_t l; 683 char *s; 684 fetchIO *df; 685 686 /* check flags */ 687 low = CHECK_FLAG('l'); 688 pasv = !CHECK_FLAG('a'); 689 verbose = CHECK_FLAG('v'); 690 691 /* passive mode */ 692 if (!pasv) 693 pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && 694 strncasecmp(s, "no", 2) != 0); 695 696 /* isolate filename */ 697 filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); 698 699 /* set transfer mode and data type */ 700 if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) 701 goto ouch; 702 703 /* find our own address, bind, and listen */ 704 l = sizeof(u.ss); 705 if (getsockname(conn->sd, &u.sa, &l) == -1) 706 goto sysouch; 707 if (u.ss.ss_family == AF_INET6) 708 unmappedaddr(&u.sin6, &l); 709 710 retry_mode: 711 712 /* open data socket */ 713 if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 714 fetch_syserr(); 715 return (NULL); 716 } 717 718 if (pasv) { 719 unsigned char addr[64]; 720 char *ln, *p; 721 unsigned int i; 722 int port; 723 724 /* send PASV command */ 725 if (verbose) 726 fetch_info("setting passive mode"); 727 switch (u.ss.ss_family) { 728 case AF_INET: 729 if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE) 730 goto ouch; 731 break; 732 case AF_INET6: 733 if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) { 734 if (e == -1) 735 goto ouch; 736 if ((e = ftp_cmd(conn, "LPSV\r\n")) != 737 FTP_LPASSIVE_MODE) 738 goto ouch; 739 } 740 break; 741 default: 742 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 743 goto ouch; 744 } 745 746 /* 747 * Find address and port number. The reply to the PASV command 748 * is IMHO the one and only weak point in the FTP protocol. 749 */ 750 ln = conn->buf; 751 switch (e) { 752 case FTP_PASSIVE_MODE: 753 case FTP_LPASSIVE_MODE: 754 for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) 755 /* nothing */ ; 756 if (!*p) { 757 e = FTP_PROTOCOL_ERROR; 758 goto ouch; 759 } 760 l = (e == FTP_PASSIVE_MODE ? 6 : 21); 761 for (i = 0; *p && i < l; i++, p++) 762 addr[i] = strtol(p, &p, 10); 763 if (i < l) { 764 e = FTP_PROTOCOL_ERROR; 765 goto ouch; 766 } 767 break; 768 case FTP_EPASSIVE_MODE: 769 for (p = ln + 3; *p && *p != '('; p++) 770 /* nothing */ ; 771 if (!*p) { 772 e = FTP_PROTOCOL_ERROR; 773 goto ouch; 774 } 775 ++p; 776 if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 777 &port, &addr[3]) != 5 || 778 addr[0] != addr[1] || 779 addr[0] != addr[2] || addr[0] != addr[3]) { 780 e = FTP_PROTOCOL_ERROR; 781 goto ouch; 782 } 783 break; 784 case FTP_SYNTAX_ERROR: 785 if (verbose) 786 fetch_info("passive mode failed"); 787 /* Close socket and retry with passive mode. */ 788 pasv = 0; 789 close(sd); 790 sd = -1; 791 goto retry_mode; 792 } 793 794 /* seek to required offset */ 795 if (offset) 796 if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK) 797 goto sysouch; 798 799 /* construct sockaddr for data socket */ 800 l = sizeof(u.ss); 801 if (getpeername(conn->sd, &u.sa, &l) == -1) 802 goto sysouch; 803 if (u.ss.ss_family == AF_INET6) 804 unmappedaddr(&u.sin6, &l); 805 switch (u.ss.ss_family) { 806 case AF_INET6: 807 if (e == FTP_EPASSIVE_MODE) 808 u.sin6.sin6_port = htons(port); 809 else { 810 memcpy(&u.sin6.sin6_addr, addr + 2, 16); 811 memcpy(&u.sin6.sin6_port, addr + 19, 2); 812 } 813 break; 814 case AF_INET: 815 if (e == FTP_EPASSIVE_MODE) 816 u.sin4.sin_port = htons(port); 817 else { 818 memcpy(&u.sin4.sin_addr, addr, 4); 819 memcpy(&u.sin4.sin_port, addr + 4, 2); 820 } 821 break; 822 default: 823 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 824 break; 825 } 826 827 /* connect to data port */ 828 if (verbose) 829 fetch_info("opening data connection"); 830 bindaddr = getenv("FETCH_BIND_ADDRESS"); 831 if (bindaddr != NULL && *bindaddr != '\0' && 832 fetch_bind(sd, u.ss.ss_family, bindaddr) != 0) 833 goto sysouch; 834 if (connect(sd, &u.sa, l) == -1) 835 goto sysouch; 836 837 /* make the server initiate the transfer */ 838 if (verbose) 839 fetch_info("initiating transfer"); 840 if (op_arg) 841 e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); 842 else 843 e = ftp_cmd(conn, "%s %.*s\r\n", oper, 844 filenamelen, filename); 845 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 846 goto ouch; 847 848 } else { 849 uint32_t a; 850 uint16_t p; 851 #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) 852 int arg; 853 #endif 854 int d; 855 char *ap; 856 char hname[INET6_ADDRSTRLEN]; 857 858 switch (u.ss.ss_family) { 859 case AF_INET6: 860 u.sin6.sin6_port = 0; 861 #ifdef IPV6_PORTRANGE 862 arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; 863 if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 864 (char *)&arg, sizeof(arg)) == -1) 865 goto sysouch; 866 #endif 867 break; 868 case AF_INET: 869 u.sin4.sin_port = 0; 870 #ifdef IP_PORTRANGE 871 arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; 872 if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 873 (char *)&arg, sizeof(arg)) == -1) 874 goto sysouch; 875 #endif 876 break; 877 } 878 if (verbose) 879 fetch_info("binding data socket"); 880 if (bind(sd, &u.sa, l) == -1) 881 goto sysouch; 882 if (listen(sd, 1) == -1) 883 goto sysouch; 884 885 /* find what port we're on and tell the server */ 886 if (getsockname(sd, &u.sa, &l) == -1) 887 goto sysouch; 888 switch (u.ss.ss_family) { 889 case AF_INET: 890 a = ntohl(u.sin4.sin_addr.s_addr); 891 p = ntohs(u.sin4.sin_port); 892 e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n", 893 (a >> 24) & 0xff, (a >> 16) & 0xff, 894 (a >> 8) & 0xff, a & 0xff, 895 (p >> 8) & 0xff, p & 0xff); 896 break; 897 case AF_INET6: 898 #define UC(b) (((int)b)&0xff) 899 e = -1; 900 u.sin6.sin6_scope_id = 0; 901 if (getnameinfo(&u.sa, l, 902 hname, sizeof(hname), 903 NULL, 0, NI_NUMERICHOST) == 0) { 904 e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname, 905 htons(u.sin6.sin6_port)); 906 if (e == -1) 907 goto ouch; 908 } 909 if (e != FTP_OK) { 910 ap = (char *)&u.sin6.sin6_addr; 911 e = ftp_cmd(conn, 912 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n", 913 6, 16, 914 UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 915 UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 916 UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 917 UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 918 2, 919 (ntohs(u.sin6.sin6_port) >> 8) & 0xff, 920 ntohs(u.sin6.sin6_port) & 0xff); 921 } 922 break; 923 default: 924 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 925 goto ouch; 926 } 927 if (e != FTP_OK) 928 goto ouch; 929 930 /* seek to required offset */ 931 if (offset) 932 if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK) 933 goto sysouch; 934 935 /* make the server initiate the transfer */ 936 if (verbose) 937 fetch_info("initiating transfer"); 938 if (op_arg) 939 e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); 940 else 941 e = ftp_cmd(conn, "%s %.*s\r\n", oper, 942 filenamelen, filename); 943 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 944 goto ouch; 945 946 /* accept the incoming connection and go to town */ 947 if ((d = accept(sd, NULL, NULL)) == -1) 948 goto sysouch; 949 close(sd); 950 sd = d; 951 } 952 953 if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) 954 goto sysouch; 955 return (df); 956 957 sysouch: 958 fetch_syserr(); 959 if (sd >= 0) 960 close(sd); 961 return (NULL); 962 963 ouch: 964 if (e != -1) 965 ftp_seterr(e); 966 if (sd >= 0) 967 close(sd); 968 return (NULL); 969 } 970 971 /* 972 * Authenticate 973 */ 974 static int 975 ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) 976 { 977 const char *user, *pwd, *login_name; 978 char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1]; 979 int e, len; 980 981 /* XXX FTP_AUTH, and maybe .netrc */ 982 983 /* send user name and password */ 984 if (url->user[0] == '\0') 985 fetch_netrc_auth(url); 986 user = url->user; 987 if (*user == '\0') 988 user = getenv("FTP_LOGIN"); 989 if (user == NULL || *user == '\0') 990 user = FTP_ANONYMOUS_USER; 991 if (purl && url->port == fetch_default_port(url->scheme)) 992 e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host); 993 else if (purl) 994 e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port); 995 else 996 e = ftp_cmd(conn, "USER %s\r\n", user); 997 998 /* did the server request a password? */ 999 if (e == FTP_NEED_PASSWORD) { 1000 pwd = url->pwd; 1001 if (*pwd == '\0') 1002 pwd = getenv("FTP_PASSWORD"); 1003 if (pwd == NULL || *pwd == '\0') { 1004 if ((login_name = getlogin()) == 0) 1005 login_name = FTP_ANONYMOUS_USER; 1006 if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0) 1007 len = 0; 1008 else if (len > URL_USERLEN + 1) 1009 len = URL_USERLEN + 1; 1010 gethostname(pbuf + len, sizeof(pbuf) - len); 1011 /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */ 1012 pbuf[sizeof(pbuf) - 1] = '\0'; 1013 pwd = pbuf; 1014 } 1015 e = ftp_cmd(conn, "PASS %s\r\n", pwd); 1016 } 1017 1018 return (e); 1019 } 1020 1021 /* 1022 * Log on to FTP server 1023 */ 1024 static conn_t * 1025 ftp_connect(struct url *url, struct url *purl, const char *flags) 1026 { 1027 conn_t *conn; 1028 int e, direct, verbose; 1029 #ifdef INET6 1030 int af = AF_UNSPEC; 1031 #else 1032 int af = AF_INET; 1033 #endif 1034 1035 direct = CHECK_FLAG('d'); 1036 verbose = CHECK_FLAG('v'); 1037 if (CHECK_FLAG('4')) 1038 af = AF_INET; 1039 else if (CHECK_FLAG('6')) 1040 af = AF_INET6; 1041 1042 if (direct) 1043 purl = NULL; 1044 1045 /* check for proxy */ 1046 if (purl) { 1047 /* XXX proxy authentication! */ 1048 /* XXX connetion caching */ 1049 if (!purl->port) 1050 purl->port = fetch_default_port(purl->scheme); 1051 1052 conn = fetch_connect(purl, af, verbose); 1053 } else { 1054 /* no proxy, go straight to target */ 1055 if (!url->port) 1056 url->port = fetch_default_port(url->scheme); 1057 1058 while ((conn = fetch_cache_get(url, af)) != NULL) { 1059 e = ftp_cmd(conn, "NOOP\r\n"); 1060 if (e == FTP_OK) 1061 return conn; 1062 fetch_close(conn); 1063 } 1064 conn = fetch_connect(url, af, verbose); 1065 purl = NULL; 1066 } 1067 1068 /* check connection */ 1069 if (conn == NULL) 1070 /* fetch_connect() has already set an error code */ 1071 return (NULL); 1072 1073 /* expect welcome message */ 1074 if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) 1075 goto fouch; 1076 1077 /* authenticate */ 1078 if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) 1079 goto fouch; 1080 1081 /* TODO: Request extended features supported, if any (RFC 3659). */ 1082 1083 /* done */ 1084 return (conn); 1085 1086 fouch: 1087 if (e != -1) 1088 ftp_seterr(e); 1089 fetch_close(conn); 1090 return (NULL); 1091 } 1092 1093 /* 1094 * Check the proxy settings 1095 */ 1096 static struct url * 1097 ftp_get_proxy(struct url * url, const char *flags) 1098 { 1099 struct url *purl; 1100 char *p; 1101 1102 if (flags != NULL && strchr(flags, 'd') != NULL) 1103 return (NULL); 1104 if (fetch_no_proxy_match(url->host)) 1105 return (NULL); 1106 if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) || 1107 (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 1108 *p && (purl = fetchParseURL(p)) != NULL) { 1109 if (!*purl->scheme) { 1110 if (getenv("FTP_PROXY") || getenv("ftp_proxy")) 1111 strcpy(purl->scheme, SCHEME_FTP); 1112 else 1113 strcpy(purl->scheme, SCHEME_HTTP); 1114 } 1115 if (!purl->port) 1116 purl->port = fetch_default_proxy_port(purl->scheme); 1117 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 1118 strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 1119 return (purl); 1120 fetchFreeURL(purl); 1121 } 1122 return (NULL); 1123 } 1124 1125 /* 1126 * Process an FTP request 1127 */ 1128 fetchIO * 1129 ftp_request(struct url *url, const char *op, const char *op_arg, 1130 struct url_stat *us, struct url *purl, const char *flags) 1131 { 1132 fetchIO *f; 1133 char *path; 1134 conn_t *conn; 1135 int if_modified_since, oflag; 1136 struct url_stat local_us; 1137 1138 /* check if we should use HTTP instead */ 1139 if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 1140 if (strcmp(op, "STAT") == 0) 1141 return (http_request(url, "HEAD", us, purl, flags)); 1142 else if (strcmp(op, "RETR") == 0) 1143 return (http_request(url, "GET", us, purl, flags)); 1144 /* 1145 * Our HTTP code doesn't support PUT requests yet, so try 1146 * a direct connection. 1147 */ 1148 } 1149 1150 /* connect to server */ 1151 conn = ftp_connect(url, purl, flags); 1152 if (purl) 1153 fetchFreeURL(purl); 1154 if (conn == NULL) 1155 return (NULL); 1156 1157 if ((path = fetchUnquotePath(url)) == NULL) { 1158 fetch_syserr(); 1159 return NULL; 1160 } 1161 1162 /* change directory */ 1163 if (ftp_cwd(conn, path, op_arg != NULL) == -1) { 1164 free(path); 1165 return (NULL); 1166 } 1167 1168 if_modified_since = CHECK_FLAG('i'); 1169 if (if_modified_since && us == NULL) 1170 us = &local_us; 1171 1172 /* stat file */ 1173 if (us && ftp_stat(conn, path, us) == -1 1174 && fetchLastErrCode != FETCH_PROTO 1175 && fetchLastErrCode != FETCH_UNAVAIL) { 1176 free(path); 1177 return (NULL); 1178 } 1179 1180 if (if_modified_since && url->last_modified > 0 && 1181 url->last_modified >= us->mtime) { 1182 free(path); 1183 fetchLastErrCode = FETCH_UNCHANGED; 1184 snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged"); 1185 return NULL; 1186 } 1187 1188 /* just a stat */ 1189 if (strcmp(op, "STAT") == 0) { 1190 free(path); 1191 return fetchIO_unopen(NULL, NULL, NULL, NULL); 1192 } 1193 if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) 1194 oflag = O_WRONLY; 1195 else 1196 oflag = O_RDONLY; 1197 1198 /* initiate the transfer */ 1199 f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags)); 1200 free(path); 1201 return f; 1202 } 1203 1204 /* 1205 * Get and stat file 1206 */ 1207 fetchIO * 1208 fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) 1209 { 1210 return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags)); 1211 } 1212 1213 /* 1214 * Get file 1215 */ 1216 fetchIO * 1217 fetchGetFTP(struct url *url, const char *flags) 1218 { 1219 return (fetchXGetFTP(url, NULL, flags)); 1220 } 1221 1222 /* 1223 * Put file 1224 */ 1225 fetchIO * 1226 fetchPutFTP(struct url *url, const char *flags) 1227 { 1228 return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL, 1229 ftp_get_proxy(url, flags), flags)); 1230 } 1231 1232 /* 1233 * Get file stats 1234 */ 1235 int 1236 fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) 1237 { 1238 fetchIO *f; 1239 1240 f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags); 1241 if (f == NULL) 1242 return (-1); 1243 fetchIO_close(f); 1244 return (0); 1245 } 1246 1247 /* 1248 * List a directory 1249 */ 1250 int 1251 fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags) 1252 { 1253 fetchIO *f; 1254 char buf[2 * PATH_MAX], *eol, *eos; 1255 ssize_t len; 1256 size_t cur_off; 1257 int ret; 1258 1259 /* XXX What about proxies? */ 1260 if (pattern == NULL || strcmp(pattern, "*") == 0) 1261 pattern = ""; 1262 f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags); 1263 if (f == NULL) 1264 return -1; 1265 1266 cur_off = 0; 1267 ret = 0; 1268 1269 while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) { 1270 cur_off += len; 1271 while ((eol = memchr(buf, '\n', cur_off)) != NULL) { 1272 if (len == eol - buf) 1273 break; 1274 if (eol != buf) { 1275 if (eol[-1] == '\r') 1276 eos = eol - 1; 1277 else 1278 eos = eol; 1279 *eos = '\0'; 1280 ret = fetch_add_entry(ue, url, buf, 0); 1281 if (ret) 1282 break; 1283 cur_off -= eol - buf + 1; 1284 memmove(buf, eol + 1, cur_off); 1285 } 1286 } 1287 if (ret) 1288 break; 1289 } 1290 if (cur_off != 0 || len < 0) { 1291 /* Not RFC conform, bail out. */ 1292 fetchIO_close(f); 1293 return -1; 1294 } 1295 fetchIO_close(f); 1296 return ret; 1297 } 1298