1/* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * Major Changelog: 10 * 11 * Jordan K. Hubbard 12 * 17 Jan 1996 13 * 14 * Turned inside out. Now returns xfers as new file ids, not as a special 15 * `state' of FTP_t 16 */ 17 18#include <sys/cdefs.h> 19__FBSDID("$FreeBSD$"); 20 21#include <sys/types.h> 22#include <sys/socket.h> 23 24#include <netinet/in.h> 25 26#include <arpa/inet.h> 27 28#include <ctype.h> 29#include <errno.h> 30#include <ftpio.h> 31#include <netdb.h> 32#include <signal.h> 33#include <stdarg.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37#include <unistd.h> 38 39#define SUCCESS 0 40#define FAILURE -1 41 42#ifndef TRUE 43#define TRUE (1) 44#define FALSE (0) 45#endif 46 47/* How to see by a given code whether or not the connection has timed out */ 48#define FTP_TIMEOUT(code) (FtpTimedOut || code == FTP_TIMED_OUT) 49 50/* Internal routines - deal only with internal FTP_t type */ 51static FTP_t ftp_new(void); 52static void check_passive(FILE *fp); 53static int ftp_read_method(void *n, char *buf, int nbytes); 54static int ftp_write_method(void *n, const char *buf, int nbytes); 55static int ftp_close_method(void *n); 56static int writes(int fd, char *s); 57static __inline char *get_a_line(FTP_t ftp); 58static int get_a_number(FTP_t ftp, char **q); 59static int botch(char *func, char *botch_state); 60static int cmd(FTP_t ftp, const char *fmt, ...); 61static int ftp_login_session(FTP_t ftp, char *host, int af, char *user, char *passwd, int port, int verbose); 62static int ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto); 63static int ftp_close(FTP_t ftp); 64static int get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret); 65static void ftp_timeout(int sig); 66static void ftp_set_timeout(void); 67static void ftp_clear_timeout(void); 68static void ai_unmapped(struct addrinfo *); 69 70 71/* Global status variable - ick */ 72int FtpTimedOut; 73 74/* FTP happy status codes */ 75#define FTP_GENERALLY_HAPPY 200 76#define FTP_ASCII_HAPPY FTP_GENERALLY_HAPPY 77#define FTP_BINARY_HAPPY FTP_GENERALLY_HAPPY 78#define FTP_PORT_HAPPY FTP_GENERALLY_HAPPY 79#define FTP_HAPPY_COMMENT 220 80#define FTP_QUIT_HAPPY 221 81#define FTP_TRANSFER_HAPPY 226 82#define FTP_PASSIVE_HAPPY 227 83#define FTP_LPASSIVE_HAPPY 228 84#define FTP_EPASSIVE_HAPPY 229 85#define FTP_CHDIR_HAPPY 250 86 87/* FTP unhappy status codes */ 88#define FTP_TIMED_OUT 421 89 90/* 91 * XXX 92 * gross! evil! bad! We really need an access primitive for cookie in stdio itself. 93 * it's too convenient a hook to bury and it's already exported through funopen as it is, so... 94 * XXX 95 */ 96#define fcookie(fp) ((fp)->_cookie) 97 98/* Placeholder in case we want to do any pre-init stuff at some point */ 99int 100networkInit() 101{ 102 return SUCCESS; /* XXX dummy function for now XXX */ 103} 104 105/* Check a return code with some lenience for back-dated garbage that might be in the buffer */ 106static int 107check_code(FTP_t ftp, int var, int preferred) 108{ 109 ftp->error = 0; 110 while (1) { 111 if (var == preferred) 112 return 0; 113 else if (var == FTP_TRANSFER_HAPPY) /* last operation succeeded */ 114 var = get_a_number(ftp, NULL); 115 else if (var == FTP_HAPPY_COMMENT) /* chit-chat */ 116 var = get_a_number(ftp, NULL); 117 else if (var == FTP_GENERALLY_HAPPY) /* general success code */ 118 var = get_a_number(ftp, NULL); 119 else { 120 ftp->error = var; 121 return 1; 122 } 123 } 124} 125 126int 127ftpAscii(FILE *fp) 128{ 129 FTP_t ftp = fcookie(fp); 130 int i; 131 132 if (!ftp->is_binary) 133 return SUCCESS; 134 i = cmd(ftp, "TYPE A"); 135 if (i < 0 || check_code(ftp, i, FTP_ASCII_HAPPY)) 136 return i; 137 ftp->is_binary = FALSE; 138 return SUCCESS; 139} 140 141int 142ftpBinary(FILE *fp) 143{ 144 FTP_t ftp = fcookie(fp); 145 int i; 146 147 if (ftp->is_binary) 148 return SUCCESS; 149 i = cmd(ftp, "TYPE I"); 150 if (i < 0 || check_code(ftp, i, FTP_BINARY_HAPPY)) 151 return i; 152 ftp->is_binary = TRUE; 153 return SUCCESS; 154} 155void 156ftpVerbose(FILE *fp, int status) 157{ 158 FTP_t ftp = fcookie(fp); 159 ftp->is_verbose = status; 160} 161 162int 163ftpChdir(FILE *fp, char *dir) 164{ 165 int i; 166 FTP_t ftp = fcookie(fp); 167 168 i = cmd(ftp, "CWD %s", dir); 169 if (i < 0 || check_code(ftp, i, FTP_CHDIR_HAPPY)) 170 return i; 171 return SUCCESS; 172} 173 174int 175ftpErrno(FILE *fp) 176{ 177 FTP_t ftp = fcookie(fp); 178 return ftp->error; 179} 180 181const char * 182ftpErrString(int error) 183{ 184 int k; 185 186 if (error == -1) 187 return("connection in wrong state"); 188 if (error < 100) 189 /* XXX soon UNIX errnos will catch up with FTP protocol errnos */ 190 return strerror(error); 191 for (k = 0; k < ftpErrListLength; k++) 192 if (ftpErrList[k].num == error) 193 return(ftpErrList[k].string); 194 return("Unknown error"); 195} 196 197off_t 198ftpGetSize(FILE *fp, char *name) 199{ 200 int i; 201 char p[BUFSIZ], *cp, *ep; 202 FTP_t ftp = fcookie(fp); 203 off_t size; 204 205 check_passive(fp); 206 sprintf(p, "SIZE %s\r\n", name); 207 if (ftp->is_verbose) 208 fprintf(stderr, "Sending %s", p); 209 if (writes(ftp->fd_ctrl, p)) 210 return (off_t)-1; 211 i = get_a_number(ftp, &cp); 212 if (check_code(ftp, i, 213)) 213 return (off_t)-1; 214 215 errno = 0; /* to check for ERANGE */ 216 size = (off_t)strtoq(cp, &ep, 10); 217 if (*ep != '\0' || errno == ERANGE) 218 return (off_t)-1; 219 return size; 220} 221 222time_t 223ftpGetModtime(FILE *fp, char *name) 224{ 225 char p[BUFSIZ], *cp; 226 struct tm t; 227 time_t t0 = time (0); 228 FTP_t ftp = fcookie(fp); 229 int i; 230 231 check_passive(fp); 232 sprintf(p, "MDTM %s\r\n", name); 233 if (ftp->is_verbose) 234 fprintf(stderr, "Sending %s", p); 235 if (writes(ftp->fd_ctrl, p)) 236 return (time_t)0; 237 i = get_a_number(ftp, &cp); 238 if (check_code(ftp, i, 213)) 239 return (time_t)0; 240 while (*cp && !isdigit(*cp)) 241 cp++; 242 if (!*cp) 243 return (time_t)0; 244 t0 = localtime (&t0)->tm_gmtoff; 245 sscanf(cp, "%04d%02d%02d%02d%02d%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec); 246 t.tm_mon--; 247 t.tm_year -= 1900; 248 t.tm_isdst=-1; 249 t.tm_gmtoff = 0; 250 t0 += mktime (&t); 251 return t0; 252} 253 254FILE * 255ftpGet(FILE *fp, char *file, off_t *seekto) 256{ 257 FILE *fp2; 258 FTP_t ftp = fcookie(fp); 259 260 check_passive(fp); 261 if (ftpBinary(fp) != SUCCESS) 262 return NULL; 263 264 if (ftp_file_op(ftp, "RETR", file, &fp2, "r", seekto) == SUCCESS) 265 return fp2; 266 return NULL; 267} 268 269/* Returns a standard FILE pointer type representing an open control connection */ 270FILE * 271ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retcode) 272{ 273#ifdef INET6 274 return ftpLoginAf(host, AF_UNSPEC, user, passwd, port, verbose, retcode); 275#else 276 return ftpLoginAf(host, AF_INET, user, passwd, port, verbose, retcode); 277#endif 278} 279 280FILE * 281ftpLoginAf(char *host, int af, char *user, char *passwd, int port, int verbose, int *retcode) 282{ 283 FTP_t n; 284 FILE *fp; 285 286 if (retcode) 287 *retcode = 0; 288 if (networkInit() != SUCCESS) 289 return NULL; 290 291 n = ftp_new(); 292 fp = NULL; 293 if (n && ftp_login_session(n, host, af, user, passwd, port, verbose) == SUCCESS) { 294 fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method); /* BSD 4.4 function! */ 295 } 296 if (retcode) { 297 if (!n) 298 *retcode = (FtpTimedOut ? FTP_TIMED_OUT : -1); 299 /* Poor attempt at mapping real errnos to FTP error codes */ 300 else switch(n->error) { 301 case EADDRNOTAVAIL: 302 *retcode = FTP_TIMED_OUT; /* Actually no such host, but we have no way of saying that. :-( */ 303 break; 304 305 case ETIMEDOUT: 306 *retcode = FTP_TIMED_OUT; 307 break; 308 309 default: 310 *retcode = n->error; 311 break; 312 } 313 } 314 return fp; 315} 316 317FILE * 318ftpPut(FILE *fp, char *file) 319{ 320 FILE *fp2; 321 FTP_t ftp = fcookie(fp); 322 323 check_passive(fp); 324 if (ftp_file_op(ftp, "STOR", file, &fp2, "w", NULL) == SUCCESS) 325 return fp2; 326 return NULL; 327} 328 329int 330ftpPassive(FILE *fp, int st) 331{ 332 FTP_t ftp = fcookie(fp); 333 334 ftp->is_passive = !!st; /* normalize "st" to zero or one */ 335 return SUCCESS; 336} 337 338FILE * 339ftpGetURL(char *url, char *user, char *passwd, int *retcode) 340{ 341#ifdef INET6 342 return ftpGetURLAf(url, AF_UNSPEC, user, passwd, retcode); 343#else 344 return ftpGetURLAf(url, AF_INET, user, passwd, retcode); 345#endif 346} 347 348FILE * 349ftpGetURLAf(char *url, int af, char *user, char *passwd, int *retcode) 350{ 351 char host[255], name[255]; 352 int port; 353 FILE *fp2; 354 static FILE *fp = NULL; 355 static char *prev_host; 356 357 if (retcode) 358 *retcode = 0; 359 if (get_url_info(url, host, &port, name) == SUCCESS) { 360 if (fp && prev_host) { 361 if (!strcmp(prev_host, host)) { 362 /* Try to use cached connection */ 363 fp2 = ftpGet(fp, name, NULL); 364 if (!fp2) { 365 /* Connection timed out or was no longer valid */ 366 fclose(fp); 367 free(prev_host); 368 prev_host = NULL; 369 } 370 else 371 return fp2; 372 } 373 else { 374 /* It's a different host now, flush old */ 375 fclose(fp); 376 free(prev_host); 377 prev_host = NULL; 378 } 379 } 380 fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode); 381 if (fp) { 382 fp2 = ftpGet(fp, name, NULL); 383 if (!fp2) { 384 /* Connection timed out or was no longer valid */ 385 if (retcode) 386 *retcode = ftpErrno(fp); 387 fclose(fp); 388 fp = NULL; 389 } 390 else 391 prev_host = strdup(host); 392 return fp2; 393 } 394 } 395 return NULL; 396} 397 398FILE * 399ftpPutURL(char *url, char *user, char *passwd, int *retcode) 400{ 401#ifdef INET6 402 return ftpPutURLAf(url, AF_UNSPEC, user, passwd, retcode); 403#else 404 return ftpPutURLAf(url, AF_INET, user, passwd, retcode); 405#endif 406 407} 408 409FILE * 410ftpPutURLAf(char *url, int af, char *user, char *passwd, int *retcode) 411{ 412 char host[255], name[255]; 413 int port; 414 static FILE *fp = NULL; 415 FILE *fp2; 416 417 if (retcode) 418 *retcode = 0; 419 if (fp) { /* Close previous managed connection */ 420 fclose(fp); 421 fp = NULL; 422 } 423 if (get_url_info(url, host, &port, name) == SUCCESS) { 424 fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode); 425 if (fp) { 426 fp2 = ftpPut(fp, name); 427 if (!fp2) { 428 if (retcode) 429 *retcode = ftpErrno(fp); 430 fclose(fp); 431 fp = NULL; 432 } 433 return fp2; 434 } 435 } 436 return NULL; 437} 438 439/* Internal workhorse function for dissecting URLs. Takes a URL as the first argument and returns the 440 result of such disection in the host, user, passwd, port and name variables. */ 441static int 442get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret) 443{ 444 char *name, *host, *cp, url[BUFSIZ]; 445 int port; 446 447 name = host = NULL; 448 /* XXX add http:// here or somewhere reasonable at some point XXX */ 449 if (strncmp("ftp://", url_in, 6) != 0) 450 return FAILURE; 451 /* We like to stomp a lot on the URL string in dissecting it, so copy it first */ 452 strncpy(url, url_in, BUFSIZ); 453 host = url + 6; 454 if ((cp = index(host, ':')) != NULL) { 455 *(cp++) = '\0'; 456 port = strtol(cp, 0, 0); 457 } 458 else 459 port = 0; /* use default */ 460 if (port_ret) 461 *port_ret = port; 462 463 if ((name = index(cp ? cp : host, '/')) != NULL) 464 *(name++) = '\0'; 465 if (host_ret) 466 strcpy(host_ret, host); 467 if (name && name_ret) 468 strcpy(name_ret, name); 469 return SUCCESS; 470} 471 472static FTP_t 473ftp_new(void) 474{ 475 FTP_t ftp; 476 477 ftp = (FTP_t)malloc(sizeof *ftp); 478 if (!ftp) 479 return NULL; 480 memset(ftp, 0, sizeof *ftp); 481 ftp->fd_ctrl = -1; 482 ftp->con_state = init; 483 ftp->is_binary = FALSE; 484 ftp->is_passive = FALSE; 485 ftp->is_verbose = FALSE; 486 ftp->error = 0; 487 return ftp; 488} 489 490static int 491ftp_read_method(void *vp, char *buf, int nbytes) 492{ 493 int i, fd; 494 FTP_t n = (FTP_t)vp; 495 496 fd = n->fd_ctrl; 497 i = (fd >= 0) ? read(fd, buf, nbytes) : EOF; 498 return i; 499} 500 501static int 502ftp_write_method(void *vp, const char *buf, int nbytes) 503{ 504 int i, fd; 505 FTP_t n = (FTP_t)vp; 506 507 fd = n->fd_ctrl; 508 i = (fd >= 0) ? write(fd, buf, nbytes) : EOF; 509 return i; 510} 511 512static int 513ftp_close_method(void *n) 514{ 515 int i; 516 517 i = ftp_close((FTP_t)n); 518 free(n); 519 return i; 520} 521 522/* 523 * This function checks whether the FTP_PASSIVE_MODE environment 524 * variable is set, and, if so, enforces the desired mode. 525 */ 526static void 527check_passive(FILE *fp) 528{ 529 const char *cp = getenv("FTP_PASSIVE_MODE"); 530 531 if (cp != NULL) 532 ftpPassive(fp, strncasecmp(cp, "no", 2)); 533} 534 535static void 536ftp_timeout(int sig) 537{ 538 FtpTimedOut = TRUE; 539 /* Debug("ftp_pkg: ftp_timeout called - operation timed out"); */ 540} 541 542static void 543ftp_set_timeout(void) 544{ 545 struct sigaction new; 546 char *cp; 547 int ival; 548 549 FtpTimedOut = FALSE; 550 sigemptyset(&new.sa_mask); 551 new.sa_flags = 0; 552 new.sa_handler = ftp_timeout; 553 sigaction(SIGALRM, &new, NULL); 554 cp = getenv("FTP_TIMEOUT"); 555 if (!cp || !(ival = atoi(cp))) 556 ival = 120; 557 alarm(ival); 558} 559 560static void 561ftp_clear_timeout(void) 562{ 563 struct sigaction new; 564 565 alarm(0); 566 sigemptyset(&new.sa_mask); 567 new.sa_flags = 0; 568 new.sa_handler = SIG_DFL; 569 sigaction(SIGALRM, &new, NULL); 570} 571 572static int 573writes(int fd, char *s) 574{ 575 int n, i = strlen(s); 576 577 ftp_set_timeout(); 578 n = write(fd, s, i); 579 ftp_clear_timeout(); 580 if (FtpTimedOut || i != n) 581 return TRUE; 582 return FALSE; 583} 584 585static __inline char * 586get_a_line(FTP_t ftp) 587{ 588 static char buf[BUFSIZ]; 589 int i,j; 590 591 /* Debug("ftp_pkg: trying to read a line from %d", ftp->fd_ctrl); */ 592 for(i = 0; i < BUFSIZ;) { 593 ftp_set_timeout(); 594 j = read(ftp->fd_ctrl, buf + i, 1); 595 ftp_clear_timeout(); 596 if (FtpTimedOut || j != 1) 597 return NULL; 598 if (buf[i] == '\r' || buf[i] == '\n') { 599 if (!i) 600 continue; 601 buf[i] = '\0'; 602 if (ftp->is_verbose == TRUE) 603 fprintf(stderr, "%s\n",buf+4); 604 return buf; 605 } 606 i++; 607 } 608 /* Debug("ftp_pkg: read string \"%s\" from %d", buf, ftp->fd_ctrl); */ 609 return buf; 610} 611 612static int 613get_a_number(FTP_t ftp, char **q) 614{ 615 char *p; 616 int i = -1, j; 617 618 while(1) { 619 p = get_a_line(ftp); 620 if (!p) { 621 ftp_close(ftp); 622 if (FtpTimedOut) 623 return FTP_TIMED_OUT; 624 return FAILURE; 625 } 626 if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2]))) 627 continue; 628 if (i == -1 && p[3] == '-') { 629 i = strtol(p, 0, 0); 630 continue; 631 } 632 if (p[3] != ' ' && p[3] != '\t') 633 continue; 634 j = strtol(p, 0, 0); 635 if (i == -1) { 636 if (q) *q = p+4; 637 /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */ 638 return j; 639 } else if (j == i) { 640 if (q) *q = p+4; 641 /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */ 642 return j; 643 } 644 } 645} 646 647static int 648ftp_close(FTP_t ftp) 649{ 650 int i, rcode; 651 652 rcode = FAILURE; 653 if (ftp->con_state == isopen) { 654 ftp->con_state = quit; 655 /* If last operation timed out, don't try to quit - just close */ 656 if (ftp->error != FTP_TIMED_OUT) 657 i = cmd(ftp, "QUIT"); 658 else 659 i = FTP_QUIT_HAPPY; 660 if (!check_code(ftp, i, FTP_QUIT_HAPPY)) 661 rcode = SUCCESS; 662 close(ftp->fd_ctrl); 663 ftp->fd_ctrl = -1; 664 } 665 else if (ftp->con_state == quit) 666 rcode = SUCCESS; 667 return rcode; 668} 669 670static int 671botch(char *func, char *botch_state) 672{ 673 /* Debug("ftp_pkg: botch: %s(%s)", func, botch_state); */ 674 return FAILURE; 675} 676 677static int 678cmd(FTP_t ftp, const char *fmt, ...) 679{ 680 char p[BUFSIZ]; 681 int i; 682 683 va_list ap; 684 va_start(ap, fmt); 685 (void)vsnprintf(p, sizeof p, fmt, ap); 686 va_end(ap); 687 688 if (ftp->con_state == init) 689 return botch("cmd", "open"); 690 691 strcat(p, "\r\n"); 692 if (ftp->is_verbose) 693 fprintf(stderr, "Sending: %s", p); 694 if (writes(ftp->fd_ctrl, p)) { 695 if (FtpTimedOut) 696 return FTP_TIMED_OUT; 697 return FAILURE; 698 } 699 while ((i = get_a_number(ftp, NULL)) == FTP_HAPPY_COMMENT); 700 return i; 701} 702 703static int 704ftp_login_session(FTP_t ftp, char *host, int af, 705 char *user, char *passwd, int port, int verbose) 706{ 707 char pbuf[10]; 708 struct addrinfo hints, *res, *res0; 709 int err; 710 int s; 711 int i; 712 713 if (networkInit() != SUCCESS) 714 return FAILURE; 715 716 if (ftp->con_state != init) { 717 ftp_close(ftp); 718 ftp->error = -1; 719 return FAILURE; 720 } 721 722 if (!user) 723 user = "ftp"; 724 725 if (!passwd) 726 passwd = "setup@"; 727 728 if (!port) 729 port = 21; 730 731 snprintf(pbuf, sizeof(pbuf), "%d", port); 732 memset(&hints, 0, sizeof(hints)); 733 hints.ai_family = af; 734 hints.ai_socktype = SOCK_STREAM; 735 hints.ai_protocol = 0; 736 err = getaddrinfo(host, pbuf, &hints, &res0); 737 if (err) { 738 ftp->error = 0; 739 return FAILURE; 740 } 741 742 s = -1; 743 for (res = res0; res; res = res->ai_next) { 744 ai_unmapped(res); 745 ftp->addrtype = res->ai_family; 746 747 if ((s = socket(res->ai_family, res->ai_socktype, 748 res->ai_protocol)) < 0) 749 continue; 750 751 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 752 (void)close(s); 753 s = -1; 754 continue; 755 } 756 757 break; 758 } 759 freeaddrinfo(res0); 760 if (s < 0) { 761 ftp->error = errno; 762 return FAILURE; 763 } 764 765 ftp->fd_ctrl = s; 766 ftp->con_state = isopen; 767 ftp->is_verbose = verbose; 768 769 i = cmd(ftp, "USER %s", user); 770 if (i >= 300 && i < 400) 771 i = cmd(ftp, "PASS %s", passwd); 772 if (i >= 299 || i < 0) { 773 ftp_close(ftp); 774 if (i > 0) 775 ftp->error = i; 776 return FAILURE; 777 } 778 return SUCCESS; 779} 780 781static int 782ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto) 783{ 784 socklen_t sinlen; 785 int i,l,s; 786 char *q; 787 unsigned char addr[64]; 788 union sockaddr_cmn { 789 struct sockaddr_in sin4; 790 struct sockaddr_in6 sin6; 791 } sin; 792 char *cmdstr; 793 794 if (!fp) 795 return FAILURE; 796 *fp = NULL; 797 798 if (ftp->con_state != isopen) 799 return botch("ftp_file_op", "open"); 800 801 if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) { 802 ftp->error = errno; 803 return FAILURE; 804 } 805 806 if (ftp->is_passive) { 807 if (ftp->addrtype == AF_INET) { 808 if (ftp->is_verbose) 809 fprintf(stderr, "Sending PASV\n"); 810 if (writes(ftp->fd_ctrl, "PASV\r\n")) { 811 ftp_close(ftp); 812 if (FtpTimedOut) 813 ftp->error = FTP_TIMED_OUT; 814 return FTP_TIMED_OUT; 815 } 816 i = get_a_number(ftp, &q); 817 if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) { 818 ftp_close(ftp); 819 return i; 820 } 821 cmdstr = "PASV"; 822 } else { 823 if (ftp->is_verbose) 824 fprintf(stderr, "Sending EPSV\n"); 825 if (writes(ftp->fd_ctrl, "EPSV\r\n")) { 826 ftp_close(ftp); 827 if (FtpTimedOut) 828 ftp->error = FTP_TIMED_OUT; 829 return FTP_TIMED_OUT; 830 } 831 i = get_a_number(ftp, &q); 832 if (check_code(ftp, i, FTP_EPASSIVE_HAPPY)) { 833 if (ftp->is_verbose) 834 fprintf(stderr, "Sending LPSV\n"); 835 if (writes(ftp->fd_ctrl, "LPSV\r\n")) { 836 ftp_close(ftp); 837 if (FtpTimedOut) 838 ftp->error = FTP_TIMED_OUT; 839 return FTP_TIMED_OUT; 840 } 841 i = get_a_number(ftp, &q); 842 if (check_code(ftp, i, FTP_LPASSIVE_HAPPY)) { 843 ftp_close(ftp); 844 return i; 845 } 846 cmdstr = "LPSV"; 847 } else 848 cmdstr = "EPSV"; 849 } 850 if (strcmp(cmdstr, "PASV") == 0 || strcmp(cmdstr, "LPSV") == 0) { 851 while (*q && !isdigit(*q)) 852 q++; 853 if (!*q) { 854 ftp_close(ftp); 855 return FAILURE; 856 } 857 q--; 858 l = (ftp->addrtype == AF_INET ? 6 : 21); 859 for (i = 0; i < l; i++) { 860 q++; 861 addr[i] = strtol(q, &q, 10); 862 } 863 864 sin.sin4.sin_family = ftp->addrtype; 865 if (ftp->addrtype == AF_INET6) { 866 sin.sin6.sin6_len = sizeof(struct sockaddr_in6); 867 bcopy(addr + 2, (char *)&sin.sin6.sin6_addr, 16); 868 bcopy(addr + 19, (char *)&sin.sin6.sin6_port, 2); 869 } else { 870 sin.sin4.sin_len = sizeof(struct sockaddr_in); 871 bcopy(addr, (char *)&sin.sin4.sin_addr, 4); 872 bcopy(addr + 4, (char *)&sin.sin4.sin_port, 2); 873 } 874 } else if (strcmp(cmdstr, "EPSV") == 0) { 875 int port; 876 while (*q && *q != '(') /* ) */ 877 q++; 878 if (!*q) { 879 ftp_close(ftp); 880 return FAILURE; 881 } 882 q++; 883 if (sscanf(q, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], 884 &port, &addr[3]) != 5 885 || addr[0] != addr[1] || addr[0] != addr[2] || addr[0] != addr[3]) { 886 ftp_close(ftp); 887 return FAILURE; 888 } 889 sinlen = sizeof(sin); 890 if (getpeername(ftp->fd_ctrl, (struct sockaddr *)&sin, &sinlen) < 0) { 891 ftp_close(ftp); 892 return FAILURE; 893 } 894 switch (sin.sin4.sin_family) { 895 case AF_INET: 896 sin.sin4.sin_port = htons(port); 897 break; 898 case AF_INET6: 899 sin.sin6.sin6_port = htons(port); 900 break; 901 default: 902 ftp_close(ftp); 903 return FAILURE; 904 } 905 } 906 907 if (connect(s, (struct sockaddr *)&sin, sin.sin4.sin_len) < 0) { 908 (void)close(s); 909 return FAILURE; 910 } 911 912 if (seekto && *seekto) { 913 i = cmd(ftp, "REST %d", *seekto); 914 if (i < 0 || FTP_TIMEOUT(i)) { 915 close(s); 916 ftp->error = i; 917 *seekto = (off_t)0; 918 return i; 919 } 920 } 921 i = cmd(ftp, "%s %s", operation, file); 922 if (i < 0 || i > 299) { 923 close(s); 924 ftp->error = i; 925 return i; 926 } 927 *fp = fdopen(s, mode); 928 } 929 else { 930 int fd,portrange; 931 932#ifdef IPV6_PORTRANGE 933 if (ftp->addrtype == AF_INET6) { 934 portrange = IPV6_PORTRANGE_HIGH; 935 if (setsockopt(s, IPPROTO_IPV6, IPV6_PORTRANGE, (char *) 936 &portrange, sizeof(portrange)) < 0) { 937 close(s); 938 return FAILURE; 939 } 940 } 941#endif 942#ifdef IP_PORTRANGE 943 if (ftp->addrtype == AF_INET) { 944 portrange = IP_PORTRANGE_HIGH; 945 if (setsockopt(s, IPPROTO_IP, IP_PORTRANGE, (char *) 946 &portrange, sizeof(portrange)) < 0) { 947 close(s); 948 return FAILURE; 949 } 950 } 951#endif 952 953 sinlen = sizeof sin; 954 getsockname(ftp->fd_ctrl, (struct sockaddr *)&sin, &sinlen); 955 sin.sin4.sin_port = 0; 956 i = ((struct sockaddr *)&sin)->sa_len; 957 if (bind(s, (struct sockaddr *)&sin, i) < 0) { 958 close(s); 959 return FAILURE; 960 } 961 sinlen = sizeof sin; 962 getsockname(s, (struct sockaddr *)&sin, &sinlen); 963 if (listen(s, 1) < 0) { 964 close(s); 965 return FAILURE; 966 } 967 if (sin.sin4.sin_family == AF_INET) { 968 u_long a; 969 a = ntohl(sin.sin4.sin_addr.s_addr); 970 i = cmd(ftp, "PORT %d,%d,%d,%d,%d,%d", 971 (a >> 24) & 0xff, 972 (a >> 16) & 0xff, 973 (a >> 8) & 0xff, 974 a & 0xff, 975 (ntohs(sin.sin4.sin_port) >> 8) & 0xff, 976 ntohs(sin.sin4.sin_port) & 0xff); 977 if (check_code(ftp, i, FTP_PORT_HAPPY)) { 978 close(s); 979 return i; 980 } 981 } else { 982#define UC(b) (((int)b)&0xff) 983 char *a; 984 char hname[INET6_ADDRSTRLEN]; 985 986 sin.sin6.sin6_scope_id = 0; 987 if (getnameinfo((struct sockaddr *)&sin, sin.sin6.sin6_len, 988 hname, sizeof(hname), 989 NULL, 0, NI_NUMERICHOST) != 0) { 990 goto try_lprt; 991 } 992 i = cmd(ftp, "EPRT |%d|%s|%d|", 2, hname, 993 htons(sin.sin6.sin6_port)); 994 if (check_code(ftp, i, FTP_PORT_HAPPY)) { 995try_lprt: 996 a = (char *)&sin.sin6.sin6_addr; 997 i = cmd(ftp, 998"LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 999 6, 16, 1000 UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), 1001 UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]), 1002 UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]), 1003 UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]), 1004 2, 1005 (ntohs(sin.sin4.sin_port) >> 8) & 0xff, 1006 ntohs(sin.sin4.sin_port) & 0xff); 1007 if (check_code(ftp, i, FTP_PORT_HAPPY)) { 1008 close(s); 1009 return i; 1010 } 1011 } 1012 } 1013 if (seekto && *seekto) { 1014 i = cmd(ftp, "REST %d", *seekto); 1015 if (i < 0 || FTP_TIMEOUT(i)) { 1016 close(s); 1017 ftp->error = i; 1018 return i; 1019 } 1020 else if (i != 350) 1021 *seekto = (off_t)0; 1022 } 1023 i = cmd(ftp, "%s %s", operation, file); 1024 if (i < 0 || i > 299) { 1025 close(s); 1026 ftp->error = i; 1027 return FAILURE; 1028 } 1029 fd = accept(s, 0, 0); 1030 if (fd < 0) { 1031 close(s); 1032 ftp->error = 401; 1033 return FAILURE; 1034 } 1035 close(s); 1036 *fp = fdopen(fd, mode); 1037 } 1038 if (*fp) 1039 return SUCCESS; 1040 else 1041 return FAILURE; 1042} 1043 1044static void 1045ai_unmapped(struct addrinfo *ai) 1046{ 1047 struct sockaddr_in6 *sin6; 1048 struct sockaddr_in sin; 1049 1050 if (ai->ai_family != AF_INET6) 1051 return; 1052 if (ai->ai_addrlen != sizeof(struct sockaddr_in6) || 1053 sizeof(sin) > ai->ai_addrlen) 1054 return; 1055 sin6 = (struct sockaddr_in6 *)ai->ai_addr; 1056 if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) 1057 return; 1058 1059 memset(&sin, 0, sizeof(sin)); 1060 sin.sin_family = AF_INET; 1061 sin.sin_len = sizeof(struct sockaddr_in); 1062 memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], 1063 sizeof(sin.sin_addr)); 1064 sin.sin_port = sin6->sin6_port; 1065 1066 ai->ai_family = AF_INET; 1067 memcpy(ai->ai_addr, &sin, sin.sin_len); 1068 ai->ai_addrlen = sin.sin_len; 1069} 1070