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