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