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$ 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 "ftp" 78 #define FTP_ANONYMOUS_PASSWORD "ftp" 79 80 #define FTP_CONNECTION_ALREADY_OPEN 125 81 #define FTP_OPEN_DATA_CONNECTION 150 82 #define FTP_OK 200 83 #define FTP_FILE_STATUS 213 84 #define FTP_SERVICE_READY 220 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 96 static struct url cached_host; 97 static int cached_socket; 98 99 static char *last_reply; 100 static size_t lr_size, lr_length; 101 static 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 */ 110 static void 111 unmappedaddr(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 */ 133 static 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 (!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 */ 168 static 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 */ 200 static 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 */ 215 static 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 */ 236 static 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 */ 310 struct 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 318 static int _ftp_readfn(void *, char *, int); 319 static int _ftp_writefn(void *, const char *, int); 320 static fpos_t _ftp_seekfn(void *, fpos_t, int); 321 static int _ftp_closefn(void *); 322 323 static 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->csd == -1 || io->dsd == -1 || io->dir == O_WRONLY) { 331 errno = EBADF; 332 return -1; 333 } 334 if (io->err) { 335 errno = io->err; 336 return -1; 337 } 338 if (io->eof) 339 return 0; 340 r = read(io->dsd, buf, len); 341 if (r > 0) 342 return r; 343 if (r == 0) { 344 io->eof = 1; 345 return _ftp_closefn(v); 346 } 347 io->err = errno; 348 return -1; 349 } 350 351 static int 352 _ftp_writefn(void *v, const char *buf, int len) 353 { 354 struct ftpio *io; 355 int w; 356 357 io = (struct ftpio *)v; 358 if (io->csd == -1 || io->dsd == -1 || io->dir == O_RDONLY) { 359 errno = EBADF; 360 return -1; 361 } 362 if (io->err) { 363 errno = io->err; 364 return -1; 365 } 366 w = write(io->dsd, buf, len); 367 if (w >= 0) 368 return w; 369 io->err = errno; 370 return -1; 371 } 372 373 static fpos_t 374 _ftp_seekfn(void *v, fpos_t pos, int whence) 375 { 376 errno = ESPIPE; 377 return -1; 378 } 379 380 static int 381 _ftp_closefn(void *v) 382 { 383 struct ftpio *io; 384 385 io = (struct ftpio *)v; 386 if (io->dir == -1) 387 return 0; 388 if (io->csd == -1 || io->dsd == -1) { 389 errno = EBADF; 390 return -1; 391 } 392 io->err = _ftp_chkerr(io->csd); 393 io->dir = -1; 394 if (close(io->dsd) == -1) 395 return -1; 396 io->dsd = -1; 397 close(io->csd); 398 io->csd = -1; 399 return io->err ? -1 : 0; 400 } 401 402 static FILE * 403 _ftp_setup(int csd, int dsd, int mode) 404 { 405 struct ftpio *io; 406 FILE *f; 407 408 if ((io = malloc(sizeof *io)) == NULL) 409 return NULL; 410 io->csd = dup(csd); 411 io->dsd = dsd; 412 io->dir = mode; 413 io->eof = io->err = 0; 414 f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn); 415 if (f == NULL) 416 free(io); 417 return f; 418 } 419 420 /* 421 * Transfer file 422 */ 423 static FILE * 424 _ftp_transfer(int cd, char *oper, char *file, 425 int mode, off_t offset, char *flags) 426 { 427 struct sockaddr_storage sin; 428 struct sockaddr_in6 *sin6; 429 struct sockaddr_in *sin4; 430 int pasv, high, verbose; 431 int e, sd = -1; 432 socklen_t l; 433 char *s; 434 FILE *df; 435 436 /* check flags */ 437 pasv = (flags && strchr(flags, 'p')); 438 high = (flags && strchr(flags, 'h')); 439 verbose = (flags && strchr(flags, 'v')); 440 441 /* passive mode */ 442 if (!pasv) 443 pasv = ((s = getenv("FTP_PASSIVE_MODE")) == NULL || 444 strncasecmp(s, "no", 2) != 0); 445 446 /* find our own address, bind, and listen */ 447 l = sizeof sin; 448 if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1) 449 goto sysouch; 450 if (sin.ss_family == AF_INET6) 451 unmappedaddr((struct sockaddr_in6 *)&sin); 452 453 /* open data socket */ 454 if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { 455 _fetch_syserr(); 456 return NULL; 457 } 458 459 if (pasv) { 460 u_char addr[64]; 461 char *ln, *p; 462 int i; 463 int port; 464 465 /* send PASV command */ 466 if (verbose) 467 _fetch_info("setting passive mode"); 468 switch (sin.ss_family) { 469 case AF_INET: 470 if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE) 471 goto ouch; 472 break; 473 case AF_INET6: 474 if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) { 475 if (e == -1) 476 goto ouch; 477 if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE) 478 goto ouch; 479 } 480 break; 481 default: 482 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 483 goto ouch; 484 } 485 486 /* 487 * Find address and port number. The reply to the PASV command 488 * is IMHO the one and only weak point in the FTP protocol. 489 */ 490 ln = last_reply; 491 switch (e) { 492 case FTP_PASSIVE_MODE: 493 case FTP_LPASSIVE_MODE: 494 for (p = ln + 3; *p && !isdigit(*p); p++) 495 /* nothing */ ; 496 if (!*p) { 497 e = FTP_PROTOCOL_ERROR; 498 goto ouch; 499 } 500 l = (e == FTP_PASSIVE_MODE ? 6 : 21); 501 for (i = 0; *p && i < l; i++, p++) 502 addr[i] = strtol(p, &p, 10); 503 if (i < l) { 504 e = FTP_PROTOCOL_ERROR; 505 goto ouch; 506 } 507 break; 508 case FTP_EPASSIVE_MODE: 509 for (p = ln + 3; *p && *p != '('; p++) 510 /* nothing */ ; 511 if (!*p) { 512 e = FTP_PROTOCOL_ERROR; 513 goto ouch; 514 } 515 ++p; 516 if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 517 &port, &addr[3]) != 5 || 518 addr[0] != addr[1] || 519 addr[0] != addr[2] || addr[0] != addr[3]) { 520 e = FTP_PROTOCOL_ERROR; 521 goto ouch; 522 } 523 break; 524 } 525 526 /* seek to required offset */ 527 if (offset) 528 if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 529 goto sysouch; 530 531 /* construct sockaddr for data socket */ 532 l = sizeof sin; 533 if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1) 534 goto sysouch; 535 if (sin.ss_family == AF_INET6) 536 unmappedaddr((struct sockaddr_in6 *)&sin); 537 switch (sin.ss_family) { 538 case AF_INET6: 539 sin6 = (struct sockaddr_in6 *)&sin; 540 if (e == FTP_EPASSIVE_MODE) 541 sin6->sin6_port = htons(port); 542 else { 543 bcopy(addr + 2, (char *)&sin6->sin6_addr, 16); 544 bcopy(addr + 19, (char *)&sin6->sin6_port, 2); 545 } 546 break; 547 case AF_INET: 548 sin4 = (struct sockaddr_in *)&sin; 549 if (e == FTP_EPASSIVE_MODE) 550 sin4->sin_port = htons(port); 551 else { 552 bcopy(addr, (char *)&sin4->sin_addr, 4); 553 bcopy(addr + 4, (char *)&sin4->sin_port, 2); 554 } 555 break; 556 default: 557 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 558 break; 559 } 560 561 /* connect to data port */ 562 if (verbose) 563 _fetch_info("opening data connection"); 564 if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1) 565 goto sysouch; 566 567 /* make the server initiate the transfer */ 568 if (verbose) 569 _fetch_info("initiating transfer"); 570 e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file)); 571 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) 572 goto ouch; 573 574 } else { 575 u_int32_t a; 576 u_short p; 577 int arg, d; 578 char *ap; 579 char hname[INET6_ADDRSTRLEN]; 580 581 switch (sin.ss_family) { 582 case AF_INET6: 583 ((struct sockaddr_in6 *)&sin)->sin6_port = 0; 584 #ifdef IPV6_PORTRANGE 585 arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; 586 if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, 587 (char *)&arg, sizeof(arg)) == -1) 588 goto sysouch; 589 #endif 590 break; 591 case AF_INET: 592 ((struct sockaddr_in *)&sin)->sin_port = 0; 593 arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; 594 if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, 595 (char *)&arg, sizeof arg) == -1) 596 goto sysouch; 597 break; 598 } 599 if (verbose) 600 _fetch_info("binding data socket"); 601 if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1) 602 goto sysouch; 603 if (listen(sd, 1) == -1) 604 goto sysouch; 605 606 /* find what port we're on and tell the server */ 607 if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) 608 goto sysouch; 609 switch (sin.ss_family) { 610 case AF_INET: 611 sin4 = (struct sockaddr_in *)&sin; 612 a = ntohl(sin4->sin_addr.s_addr); 613 p = ntohs(sin4->sin_port); 614 e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d", 615 (a >> 24) & 0xff, (a >> 16) & 0xff, 616 (a >> 8) & 0xff, a & 0xff, 617 (p >> 8) & 0xff, p & 0xff); 618 break; 619 case AF_INET6: 620 #define UC(b) (((int)b)&0xff) 621 e = -1; 622 sin6 = (struct sockaddr_in6 *)&sin; 623 if (getnameinfo((struct sockaddr *)&sin, sin.ss_len, 624 hname, sizeof(hname), 625 NULL, 0, NI_NUMERICHOST) == 0) { 626 e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname, 627 htons(sin6->sin6_port)); 628 if (e == -1) 629 goto ouch; 630 } 631 if (e != FTP_OK) { 632 ap = (char *)&sin6->sin6_addr; 633 e = _ftp_cmd(cd, 634 "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 635 6, 16, 636 UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), 637 UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), 638 UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), 639 UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 640 2, 641 (ntohs(sin6->sin6_port) >> 8) & 0xff, 642 ntohs(sin6->sin6_port) & 0xff); 643 } 644 break; 645 default: 646 e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ 647 goto ouch; 648 } 649 if (e != FTP_OK) 650 goto ouch; 651 652 /* seek to required offset */ 653 if (offset) 654 if (_ftp_cmd(cd, "REST %lu", (u_long)offset) != FTP_FILE_OK) 655 goto sysouch; 656 657 /* make the server initiate the transfer */ 658 if (verbose) 659 _fetch_info("initiating transfer"); 660 e = _ftp_cmd(cd, "%s %s", oper, _ftp_filename(file)); 661 if (e != FTP_OPEN_DATA_CONNECTION) 662 goto ouch; 663 664 /* accept the incoming connection and go to town */ 665 if ((d = accept(sd, NULL, NULL)) == -1) 666 goto sysouch; 667 close(sd); 668 sd = d; 669 } 670 671 if ((df = _ftp_setup(cd, sd, mode)) == NULL) 672 goto sysouch; 673 return df; 674 675 sysouch: 676 _fetch_syserr(); 677 if (sd >= 0) 678 close(sd); 679 return NULL; 680 681 ouch: 682 if (e != -1) 683 _ftp_seterr(e); 684 if (sd >= 0) 685 close(sd); 686 return NULL; 687 } 688 689 /* 690 * Return default port 691 */ 692 static int 693 _ftp_default_port(void) 694 { 695 struct servent *se; 696 697 if ((se = getservbyname(SCHEME_FTP, "tcp")) != NULL) 698 return ntohs(se->s_port); 699 return FTP_DEFAULT_PORT; 700 } 701 702 /* 703 * Log on to FTP server 704 */ 705 static int 706 _ftp_connect(struct url *url, struct url *purl, char *flags) 707 { 708 int cd, e, direct, verbose; 709 #ifdef INET6 710 int af = AF_UNSPEC; 711 #else 712 int af = AF_INET; 713 #endif 714 const char *logname; 715 char *user, *pwd; 716 char localhost[MAXHOSTNAMELEN]; 717 char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; 718 719 direct = (flags && strchr(flags, 'd')); 720 verbose = (flags && strchr(flags, 'v')); 721 if ((flags && strchr(flags, '4'))) 722 af = AF_INET; 723 else if ((flags && strchr(flags, '6'))) 724 af = AF_INET6; 725 726 if (direct) 727 purl = NULL; 728 729 /* check for proxy */ 730 if (purl) { 731 /* XXX proxy authentication! */ 732 cd = _fetch_connect(purl->host, purl->port, af, verbose); 733 } else { 734 /* no proxy, go straight to target */ 735 cd = _fetch_connect(url->host, url->port, af, verbose); 736 purl = NULL; 737 } 738 739 /* check connection */ 740 if (cd == -1) { 741 _fetch_syserr(); 742 return NULL; 743 } 744 745 /* expect welcome message */ 746 if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY) 747 goto fouch; 748 749 /* XXX FTP_AUTH, and maybe .netrc */ 750 751 /* send user name and password */ 752 user = url->user; 753 if (!user || !*user) 754 user = FTP_ANONYMOUS_USER; 755 if (purl && url->port == FTP_DEFAULT_PORT) 756 e = _ftp_cmd(cd, "USER %s@%s", user, url->host); 757 else if (purl) 758 e = _ftp_cmd(cd, "USER %s@%s@%d", user, url->host, url->port); 759 else 760 e = _ftp_cmd(cd, "USER %s", user); 761 762 /* did the server request a password? */ 763 if (e == FTP_NEED_PASSWORD) { 764 pwd = url->pwd; 765 if (!pwd || !*pwd) 766 pwd = getenv("FTP_PASSWORD"); 767 if (!pwd || !*pwd) { 768 if ((logname = getlogin()) == 0) 769 logname = FTP_ANONYMOUS_PASSWORD; 770 gethostname(localhost, sizeof localhost); 771 snprintf(pbuf, sizeof pbuf, "%s@%s", logname, localhost); 772 pwd = pbuf; 773 } 774 e = _ftp_cmd(cd, "PASS %s", pwd); 775 } 776 777 /* did the server request an account? */ 778 if (e == FTP_NEED_ACCOUNT) 779 goto fouch; 780 781 /* we should be done by now */ 782 if (e != FTP_LOGGED_IN) 783 goto fouch; 784 785 /* might as well select mode and type at once */ 786 #ifdef FTP_FORCE_STREAM_MODE 787 if ((e = _ftp_cmd(cd, "MODE S")) != FTP_OK) /* default is S */ 788 goto fouch; 789 #endif 790 if ((e = _ftp_cmd(cd, "TYPE I")) != FTP_OK) /* default is A */ 791 goto fouch; 792 793 /* done */ 794 return cd; 795 796 fouch: 797 if (e != -1) 798 _ftp_seterr(e); 799 close(cd); 800 return NULL; 801 } 802 803 /* 804 * Disconnect from server 805 */ 806 static void 807 _ftp_disconnect(int cd) 808 { 809 (void)_ftp_cmd(cd, "QUIT"); 810 close(cd); 811 } 812 813 /* 814 * Check if we're already connected 815 */ 816 static int 817 _ftp_isconnected(struct url *url) 818 { 819 return (cached_socket 820 && (strcmp(url->host, cached_host.host) == 0) 821 && (strcmp(url->user, cached_host.user) == 0) 822 && (strcmp(url->pwd, cached_host.pwd) == 0) 823 && (url->port == cached_host.port)); 824 } 825 826 /* 827 * Check the cache, reconnect if no luck 828 */ 829 static int 830 _ftp_cached_connect(struct url *url, struct url *purl, char *flags) 831 { 832 int e, cd; 833 834 cd = -1; 835 836 /* set default port */ 837 if (!url->port) 838 url->port = _ftp_default_port(); 839 840 /* try to use previously cached connection */ 841 if (_ftp_isconnected(url)) { 842 e = _ftp_cmd(cached_socket, "NOOP"); 843 if (e == FTP_OK || e == FTP_SYNTAX_ERROR) 844 return cached_socket; 845 } 846 847 /* connect to server */ 848 if ((cd = _ftp_connect(url, purl, flags)) == -1) 849 return -1; 850 if (cached_socket) 851 _ftp_disconnect(cached_socket); 852 cached_socket = cd; 853 memcpy(&cached_host, url, sizeof *url); 854 return cd; 855 } 856 857 /* 858 * Check the proxy settings 859 */ 860 static struct url * 861 _ftp_get_proxy(void) 862 { 863 struct url *purl; 864 char *p; 865 866 if (((p = getenv("FTP_PROXY")) || (p = getenv("HTTP_PROXY"))) && 867 *p && (purl = fetchParseURL(p)) != NULL) { 868 if (!*purl->scheme) 869 strcpy(purl->scheme, SCHEME_FTP); 870 if (!purl->port) 871 purl->port = _ftp_default_port(); 872 if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || 873 strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 874 return purl; 875 fetchFreeURL(purl); 876 } 877 return NULL; 878 } 879 880 /* 881 * Get and stat file 882 */ 883 FILE * 884 fetchXGetFTP(struct url *url, struct url_stat *us, char *flags) 885 { 886 struct url *purl; 887 int cd; 888 889 /* get the proxy URL, and check if we should use HTTP instead */ 890 if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) { 891 if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 892 return _http_request(url, "GET", us, purl, flags); 893 } else { 894 purl = NULL; 895 } 896 897 /* connect to server */ 898 cd = _ftp_cached_connect(url, purl, flags); 899 if (purl) 900 fetchFreeURL(purl); 901 if (cd == NULL) 902 return NULL; 903 904 /* change directory */ 905 if (_ftp_cwd(cd, url->doc) == -1) 906 return NULL; 907 908 /* stat file */ 909 if (us && _ftp_stat(cd, url->doc, us) == -1 910 && fetchLastErrCode != FETCH_PROTO 911 && fetchLastErrCode != FETCH_UNAVAIL) 912 return NULL; 913 914 /* initiate the transfer */ 915 return _ftp_transfer(cd, "RETR", url->doc, O_RDONLY, url->offset, flags); 916 } 917 918 /* 919 * Get file 920 */ 921 FILE * 922 fetchGetFTP(struct url *url, char *flags) 923 { 924 return fetchXGetFTP(url, NULL, flags); 925 } 926 927 /* 928 * Put file 929 */ 930 FILE * 931 fetchPutFTP(struct url *url, char *flags) 932 { 933 struct url *purl; 934 int cd; 935 936 /* get the proxy URL, and check if we should use HTTP instead */ 937 if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) { 938 if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) 939 /* XXX HTTP PUT is not implemented, so try without the proxy */ 940 purl = NULL; 941 } else { 942 purl = NULL; 943 } 944 945 /* connect to server */ 946 cd = _ftp_cached_connect(url, purl, flags); 947 if (purl) 948 fetchFreeURL(purl); 949 if (cd == NULL) 950 return NULL; 951 952 /* change directory */ 953 if (_ftp_cwd(cd, url->doc) == -1) 954 return NULL; 955 956 /* initiate the transfer */ 957 return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", 958 url->doc, O_WRONLY, url->offset, flags); 959 } 960 961 /* 962 * Get file stats 963 */ 964 int 965 fetchStatFTP(struct url *url, struct url_stat *us, char *flags) 966 { 967 struct url *purl; 968 int cd; 969 970 /* get the proxy URL, and check if we should use HTTP instead */ 971 if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) { 972 if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { 973 FILE *f; 974 975 if ((f = _http_request(url, "HEAD", us, purl, flags)) == NULL) 976 return -1; 977 fclose(f); 978 return 0; 979 } 980 } else { 981 purl = NULL; 982 } 983 984 /* connect to server */ 985 cd = _ftp_cached_connect(url, purl, flags); 986 if (purl) 987 fetchFreeURL(purl); 988 if (cd == NULL) 989 return NULL; 990 991 /* change directory */ 992 if (_ftp_cwd(cd, url->doc) == -1) 993 return -1; 994 995 /* stat file */ 996 return _ftp_stat(cd, url->doc, us); 997 } 998 999 /* 1000 * List a directory 1001 */ 1002 extern void warnx(char *, ...); 1003 struct url_ent * 1004 fetchListFTP(struct url *url, char *flags) 1005 { 1006 warnx("fetchListFTP(): not implemented"); 1007 return NULL; 1008 } 1009