ftp.c revision 89589
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: head/lib/libfetch/ftp.c 89589 2002-01-20 19:52:25Z des $"); 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@login.dknet.dk> 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 <stdio.h> 70#include <stdlib.h> 71#include <string.h> 72#include <time.h> 73#include <unistd.h> 74 75#include "fetch.h" 76#include "common.h" 77#include "ftperr.h" 78 79#define FTP_ANONYMOUS_USER "anonymous" 80 81#define FTP_CONNECTION_ALREADY_OPEN 125 82#define FTP_OPEN_DATA_CONNECTION 150 83#define FTP_OK 200 84#define FTP_FILE_STATUS 213 85#define FTP_SERVICE_READY 220 86#define FTP_TRANSFER_COMPLETE 226 87#define FTP_PASSIVE_MODE 227 88#define FTP_LPASSIVE_MODE 228 89#define FTP_EPASSIVE_MODE 229 90#define FTP_LOGGED_IN 230 91#define FTP_FILE_ACTION_OK 250 92#define FTP_NEED_PASSWORD 331 93#define FTP_NEED_ACCOUNT 332 94#define FTP_FILE_OK 350 95#define FTP_SYNTAX_ERROR 500 96#define FTP_PROTOCOL_ERROR 999 97 98static struct url cached_host; 99static int cached_socket; 100 101static char *last_reply; 102static size_t lr_size, lr_length; 103static int last_code; 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/* translate IPv4 mapped IPv6 address to IPv4 address */ 112static void 113unmappedaddr(struct sockaddr_in6 *sin6) 114{ 115 struct sockaddr_in *sin4; 116 u_int32_t addr; 117 int port; 118 119 if (sin6->sin6_family != AF_INET6 || 120 !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 121 return; 122 sin4 = (struct sockaddr_in *)sin6; 123 addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12]; 124 port = sin6->sin6_port; 125 memset(sin4, 0, sizeof(struct sockaddr_in)); 126 sin4->sin_addr.s_addr = addr; 127 sin4->sin_port = port; 128 sin4->sin_family = AF_INET; 129 sin4->sin_len = sizeof(struct sockaddr_in); 130} 131 132/* 133 * Get server response 134 */ 135static int 136_ftp_chkerr(int cd) 137{ 138 if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 139 _fetch_syserr(); 140 return -1; 141 } 142 if (isftpinfo(last_reply)) { 143 while (lr_length && !isftpreply(last_reply)) { 144 if (_fetch_getln(cd, &last_reply, &lr_size, &lr_length) == -1) { 145 _fetch_syserr(); 146 return -1; 147 } 148 } 149 } 150 151 while (lr_length && isspace(last_reply[lr_length-1])) 152 lr_length--; 153 last_reply[lr_length] = 0; 154 155 if (!isftpreply(last_reply)) { 156 _ftp_seterr(FTP_PROTOCOL_ERROR); 157 return -1; 158 } 159 160 last_code = (last_reply[0] - '0') * 100 161 + (last_reply[1] - '0') * 10 162 + (last_reply[2] - '0'); 163 164 return last_code; 165} 166 167/* 168 * Send a command and check reply 169 */ 170static int 171_ftp_cmd(int cd, const char *fmt, ...) 172{ 173 va_list ap; 174 size_t len; 175 char *msg; 176 int r; 177 178 va_start(ap, fmt); 179 len = vasprintf(&msg, fmt, ap); 180 va_end(ap); 181 182 if (msg == NULL) { 183 errno = ENOMEM; 184 _fetch_syserr(); 185 return -1; 186 } 187 188 r = _fetch_putln(cd, msg, len); 189 free(msg); 190 191 if (r == -1) { 192 _fetch_syserr(); 193 return -1; 194 } 195 196 return _ftp_chkerr(cd); 197} 198 199/* 200 * Return a pointer to the filename part of a path 201 */ 202static const char * 203_ftp_filename(const char *file) 204{ 205 char *s; 206 207 if ((s = strrchr(file, '/')) == NULL) 208 return file; 209 else 210 return s + 1; 211} 212 213/* 214 * Change working directory to the directory that contains the 215 * specified file. 216 */ 217static int 218_ftp_cwd(int cd, const char *file) 219{ 220 char *s; 221 int e; 222 223 if ((s = strrchr(file, '/')) == NULL || s == file) { 224 e = _ftp_cmd(cd, "CWD /"); 225 } else { 226 e = _ftp_cmd(cd, "CWD %.*s", s - file, file); 227 } 228 if (e != FTP_FILE_ACTION_OK) { 229 _ftp_seterr(e); 230 return -1; 231 } 232 return 0; 233} 234 235/* 236 * Request and parse file stats 237 */ 238static int 239_ftp_stat(int cd, const char *file, struct url_stat *us) 240{ 241 char *ln; 242 const char *s; 243 struct tm tm; 244 time_t t; 245 int e; 246 247 us->size = -1; 248 us->atime = us->mtime = 0; 249 250 if ((s = strrchr(file, '/')) == NULL) 251 s = file; 252 else 253 ++s; 254 255 if ((e = _ftp_cmd(cd, "SIZE %s", s)) != FTP_FILE_STATUS) { 256 _ftp_seterr(e); 257 return -1; 258 } 259 for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 260 /* nothing */ ; 261 for (us->size = 0; *ln && isdigit(*ln); ln++) 262 us->size = us->size * 10 + *ln - '0'; 263 if (*ln && !isspace(*ln)) { 264 _ftp_seterr(FTP_PROTOCOL_ERROR); 265 us->size = -1; 266 return -1; 267 } 268 if (us->size == 0) 269 us->size = -1; 270 DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size)); 271 272 if ((e = _ftp_cmd(cd, "MDTM %s", s)) != FTP_FILE_STATUS) { 273 _ftp_seterr(e); 274 return -1; 275 } 276 for (ln = last_reply + 4; *ln && isspace(*ln); ln++) 277 /* nothing */ ; 278 switch (strspn(ln, "0123456789")) { 279 case 14: 280 break; 281 case 15: 282 ln++; 283 ln[0] = '2'; 284 ln[1] = '0'; 285 break; 286 default: 287 _ftp_seterr(FTP_PROTOCOL_ERROR); 288 return -1; 289 } 290 if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", 291 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 292 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { 293 _ftp_seterr(FTP_PROTOCOL_ERROR); 294 return -1; 295 } 296 tm.tm_mon--; 297 tm.tm_year -= 1900; 298 tm.tm_isdst = -1; 299 t = timegm(&tm); 300 if (t == (time_t)-1) 301 t = time(NULL); 302 us->mtime = t; 303 us->atime = t; 304 DEBUG(fprintf(stderr, "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 */ 313struct ftpio { 314 int csd; /* Control socket descriptor */ 315 int dsd; /* Data socket descriptor */ 316 int dir; /* Direction */ 317 int eof; /* EOF reached */ 318 int err; /* Error code */ 319}; 320 321static int _ftp_readfn(void *, char *, int); 322static int _ftp_writefn(void *, const char *, int); 323static fpos_t _ftp_seekfn(void *, fpos_t, int); 324static int _ftp_closefn(void *); 325 326static 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->csd == -1 || io->dsd == -1 || 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 = read(io->dsd, 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 359static 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->csd == -1 || io->dsd == -1 || 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 = write(io->dsd, buf, len); 379 if (w >= 0) 380 return w; 381 if (errno != EINTR) 382 io->err = errno; 383 return -1; 384} 385 386static 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 400static 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->csd == -1 || io->dsd == -1) { 414 errno = EBADF; 415 return -1; 416 } 417 close(io->dsd); 418 io->dir = -1; 419 io->dsd = -1; 420 DEBUG(fprintf(stderr, "Waiting for final status\n")); 421 r = _ftp_chkerr(io->csd); 422 close(io->csd); 423 free(io); 424 return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1; 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, const char *oper, const char *file, 450 int mode, off_t offset, const char *flags) 451{ 452 struct sockaddr_storage sa; 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 sa; 473 if (getsockname(cd, (struct sockaddr *)&sa, &l) == -1) 474 goto sysouch; 475 if (sa.ss_family == AF_INET6) 476 unmappedaddr((struct sockaddr_in6 *)&sa); 477 478 /* open data socket */ 479 if ((sd = socket(sa.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 unsigned int i; 488 int port; 489 490 /* send PASV command */ 491 if (verbose) 492 _fetch_info("setting passive mode"); 493 switch (sa.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 sa; 558 if (getpeername(cd, (struct sockaddr *)&sa, &l) == -1) 559 goto sysouch; 560 if (sa.ss_family == AF_INET6) 561 unmappedaddr((struct sockaddr_in6 *)&sa); 562 switch (sa.ss_family) { 563 case AF_INET6: 564 sin6 = (struct sockaddr_in6 *)&sa; 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 *)&sa; 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 *)&sa, sa.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 (sa.ss_family) { 607 case AF_INET6: 608 ((struct sockaddr_in6 *)&sa)->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 *)&sa)->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 *)&sa, sa.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 *)&sa, &l) == -1) 633 goto sysouch; 634 switch (sa.ss_family) { 635 case AF_INET: 636 sin4 = (struct sockaddr_in *)&sa; 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 *)&sa; 648 if (getnameinfo((struct sockaddr *)&sa, sa.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 * Authenticate 716 */ 717static int 718_ftp_authenticate(int cd, struct url *url, struct url *purl) 719{ 720 const char *user, *pwd, *logname; 721 char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 722 int e, len; 723 724 /* XXX FTP_AUTH, and maybe .netrc */ 725 726 /* send user name and password */ 727 user = url->user; 728 if (!user || !*user) 729 user = getenv("FTP_LOGIN"); 730 if (!user || !*user) 731 user = FTP_ANONYMOUS_USER; 732 if (purl && url->port == _fetch_default_port(url->scheme)) 733 e = _ftp_cmd(cd, "USER %s@%s", user, url->host); 734 else if (purl) 735 e = _ftp_cmd(cd, "USER %s@%s@%d", user, url->host, url->port); 736 else 737 e = _ftp_cmd(cd, "USER %s", user); 738 739 /* did the server request a password? */ 740 if (e == FTP_NEED_PASSWORD) { 741 pwd = url->pwd; 742 if (!pwd || !*pwd) 743 pwd = getenv("FTP_PASSWORD"); 744 if (!pwd || !*pwd) { 745 if ((logname = getlogin()) == 0) 746 logname = FTP_ANONYMOUS_USER; 747 if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0) 748 len = 0; 749 else if (len > MAXLOGNAME) 750 len = MAXLOGNAME; 751 gethostname(pbuf + len, sizeof pbuf - len); 752 pwd = pbuf; 753 } 754 e = _ftp_cmd(cd, "PASS %s", pwd); 755 } 756 757 return e; 758} 759 760/* 761 * Log on to FTP server 762 */ 763static int 764_ftp_connect(struct url *url, struct url *purl, const char *flags) 765{ 766 int cd, e, direct, verbose; 767#ifdef INET6 768 int af = AF_UNSPEC; 769#else 770 int af = AF_INET; 771#endif 772 773 direct = CHECK_FLAG('d'); 774 verbose = CHECK_FLAG('v'); 775 if (CHECK_FLAG('4')) 776 af = AF_INET; 777 else if (CHECK_FLAG('6')) 778 af = AF_INET6; 779 780 if (direct) 781 purl = NULL; 782 783 /* check for proxy */ 784 if (purl) { 785 /* XXX proxy authentication! */ 786 cd = _fetch_connect(purl->host, purl->port, af, verbose); 787 } else { 788 /* no proxy, go straight to target */ 789 cd = _fetch_connect(url->host, url->port, af, verbose); 790 purl = NULL; 791 } 792 793 /* check connection */ 794 if (cd == -1) { 795 _fetch_syserr(); 796 return NULL; 797 } 798 799 /* expect welcome message */ 800 if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 801 goto fouch; 802 803 /* authenticate */ 804 if ((e = _ftp_authenticate(cd, url, purl)) != FTP_LOGGED_IN) 805 goto fouch; 806 807 /* might as well select mode and type at once */ 808#ifdef FTP_FORCE_STREAM_MODE 809 if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 810 goto fouch; 811#endif 812 if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 813 goto fouch; 814 815 /* done */ 816 return cd; 817 818fouch: 819 if (e != -1) 820 _ftp_seterr(e); 821 close(cd); 822 return NULL; 823} 824 825/* 826 * Disconnect from server 827 */ 828static void 829_ftp_disconnect(int cd) 830{ 831 (void)_ftp_cmd(cd, "QUIT"); 832 close(cd); 833} 834 835/* 836 * Check if we're already connected 837 */ 838static int 839_ftp_isconnected(struct url *url) 840{ 841 return (cached_socket 842 && (strcmp(url->host, cached_host.host) == 0) 843 && (strcmp(url->user, cached_host.user) == 0) 844 && (strcmp(url->pwd, cached_host.pwd) == 0) 845 && (url->port == cached_host.port)); 846} 847 848/* 849 * Check the cache, reconnect if no luck 850 */ 851static int 852_ftp_cached_connect(struct url *url, struct url *purl, const char *flags) 853{ 854 int e, cd; 855 856 cd = -1; 857 858 /* set default port */ 859 if (!url->port) 860 url->port = _fetch_default_port(url->scheme); 861 862 /* try to use previously cached connection */ 863 if (_ftp_isconnected(url)) { 864 e = _ftp_cmd(cached_socket, "NOOP"); 865 if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 866 return cached_socket; 867 } 868 869 /* connect to server */ 870 if ((cd = _ftp_connect(url, purl, flags)) == -1) 871 return -1; 872 if (cached_socket) 873 _ftp_disconnect(cached_socket); 874 cached_socket = cd; 875 memcpy(&cached_host, url, sizeof *url); 876 return cd; 877} 878 879/* 880 * Check the proxy settings 881 */ 882static struct url * 883_ftp_get_proxy(void) 884{ 885 struct url *purl; 886 char *p; 887 888 if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) || 889 (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && 890 *p && (purl = fetchParseURL(p)) != NULL) { 891 if (!*purl->scheme) { 892 if (getenv("FTP_PROXY") || getenv("ftp_proxy")) 893 strcpy(purl->scheme, SCHEME_FTP); 894 else 895 strcpy(purl->scheme, SCHEME_HTTP); 896 } 897 if (!purl->port) 898 purl->port = _fetch_default_proxy_port(purl->scheme); 899 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 900 strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 901 return purl; 902 fetchFreeURL(purl); 903 } 904 return NULL; 905} 906 907/* 908 * Process an FTP request 909 */ 910FILE * 911_ftp_request(struct url *url, const char *op, struct url_stat *us, 912 struct url *purl, const char *flags) 913{ 914 int cd, oflag; 915 916 /* check if we should use HTTP instead */ 917 if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 918 if (strcmp(op, "STAT") == 0) 919 return _http_request(url, "HEAD", us, purl, flags); 920 else if (strcmp(op, "RETR") == 0) 921 return _http_request(url, "GET", us, purl, flags); 922 /* 923 * Our HTTP code doesn't support PUT requests yet, so try a 924 * direct connection. 925 */ 926 } 927 928 /* connect to server */ 929 cd = _ftp_cached_connect(url, purl, flags); 930 if (purl) 931 fetchFreeURL(purl); 932 if (cd == NULL) 933 return NULL; 934 935 /* change directory */ 936 if (_ftp_cwd(cd, url->doc) == -1) 937 return NULL; 938 939 /* stat file */ 940 if (us && _ftp_stat(cd, url->doc, us) == -1 941 && fetchLastErrCode != FETCH_PROTO 942 && fetchLastErrCode != FETCH_UNAVAIL) 943 return NULL; 944 945 /* just a stat */ 946 if (strcmp(op, "STAT") == 0) 947 return (FILE *)1; /* bogus return value */ 948 if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) 949 oflag = O_WRONLY; 950 else 951 oflag = O_RDONLY; 952 953 /* initiate the transfer */ 954 return _ftp_transfer(cd, op, url->doc, oflag, url->offset, flags); 955} 956 957/* 958 * Get and stat file 959 */ 960FILE * 961fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) 962{ 963 return _ftp_request(url, "RETR", us, _ftp_get_proxy(), flags); 964} 965 966/* 967 * Get file 968 */ 969FILE * 970fetchGetFTP(struct url *url, const char *flags) 971{ 972 return fetchXGetFTP(url, NULL, flags); 973} 974 975/* 976 * Put file 977 */ 978FILE * 979fetchPutFTP(struct url *url, const char *flags) 980{ 981 982 return _ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, 983 _ftp_get_proxy(), flags); 984} 985 986/* 987 * Get file stats 988 */ 989int 990fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) 991{ 992 993 if (_ftp_request(url, "STAT", us, _ftp_get_proxy(), flags) == NULL) 994 return -1; 995 return 0; 996} 997 998/* 999 * List a directory 1000 */ 1001struct url_ent * 1002fetchListFTP(struct url *url __unused, const char *flags __unused) 1003{ 1004 warnx("fetchListFTP(): not implemented"); 1005 return NULL; 1006} 1007