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