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