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