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