1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 /* 40 * FTP User Program -- Command Interface. 41 */ 42 #include "ftp_var.h" 43 #include <deflt.h> /* macros that make using libcmd easier */ 44 45 int trace; 46 int hash; 47 int sendport; 48 int verbose; 49 int connected; 50 int fromatty; 51 int interactive; 52 int debug; 53 int bell; 54 int doglob; 55 int autologin; 56 int proxy; 57 int proxflag; 58 int sunique; 59 int runique; 60 int mcase; 61 int ntflag; 62 int mapflag; 63 int code; 64 int crflag; 65 char pasv[64]; 66 char *altarg; 67 char ntin[17]; 68 char ntout[17]; 69 char mapin[MAXPATHLEN]; 70 char mapout[MAXPATHLEN]; 71 char typename[32]; 72 int type; 73 char structname[32]; 74 int stru; 75 char formname[32]; 76 int form; 77 char modename[32]; 78 int mode; 79 char bytename[32]; 80 int bytesize; 81 int passivemode; 82 off_t restart_point; 83 int tcpwindowsize; 84 boolean_t ls_invokes_NLST; 85 char *hostname; 86 char *home; 87 char *globerr; 88 struct sockaddr_in6 myctladdr; 89 struct sockaddr_in6 remctladdr; 90 int clevel; 91 int dlevel; 92 int autoauth; 93 int auth_error; 94 int autoencrypt; 95 int fflag; 96 boolean_t goteof; 97 int skipsyst; 98 char mechstr[MECH_SZ]; 99 char *buf; 100 jmp_buf toplevel; 101 char line[BUFSIZE]; 102 char *stringbase; 103 char argbuf[BUFSIZE]; 104 char *argbase; 105 int margc; 106 char **margv; 107 int cpend; 108 int mflag; 109 char reply_buf[FTPBUFSIZ]; 110 char *reply_ptr; 111 int options; 112 int timeout; 113 int timeoutms; 114 jmp_buf timeralarm; 115 int macnum; 116 struct macel macros[16]; 117 char macbuf[4096]; 118 119 static void usage(void); 120 static void timeout_sig(int sig); 121 static void cmdscanner(int top); 122 static void intr(int sig); 123 static char *slurpstring(void); 124 extern int use_eprt; 125 126 boolean_t ls_invokes_NLST = B_TRUE; 127 128 #include <gssapi/gssapi.h> 129 #include <gssapi/gssapi_ext.h> 130 #define GETOPT_STR "dginpstvET:axfm:" 131 #define USAGE_STR "[-adfginpstvx] [-m mech] [-T timeout] " \ 132 "[hostname [port]]" 133 134 int 135 main(int argc, char *argv[]) 136 { 137 char *cp; 138 int c, top; 139 struct passwd *pw = NULL; 140 char homedir[MAXPATHLEN]; 141 char *temp_string = NULL; 142 143 (void) setlocale(LC_ALL, ""); 144 145 buf = (char *)memalign(getpagesize(), FTPBUFSIZ); 146 if (buf == NULL) { 147 (void) fprintf(stderr, "ftp: memory allocation failed\n"); 148 return (1); 149 } 150 151 timeoutms = timeout = 0; 152 doglob = 1; 153 interactive = 1; 154 autologin = 1; 155 156 autoauth = 0; 157 /* by default SYST command will be sent to determine system type */ 158 skipsyst = 0; 159 fflag = 0; 160 autoencrypt = 0; 161 goteof = 0; 162 mechstr[0] = '\0'; 163 164 sendport = -1; /* tri-state variable. start out in "automatic" mode. */ 165 passivemode = 0; 166 167 while ((c = getopt(argc, argv, GETOPT_STR)) != EOF) { 168 switch (c) { 169 case 'd': 170 options |= SO_DEBUG; 171 debug++; 172 break; 173 174 case 'g': 175 doglob = 0; 176 break; 177 178 case 'i': 179 interactive = 0; 180 break; 181 182 case 'n': 183 autologin = 0; 184 break; 185 186 case 'p': 187 passivemode = 1; 188 break; 189 190 case 't': 191 trace++; 192 break; 193 194 case 'v': 195 verbose++; 196 break; 197 198 /* undocumented option: allows testing of EPRT */ 199 case 'E': 200 use_eprt = 1; 201 break; 202 203 case 'T': 204 if (!isdigit(*optarg)) { 205 (void) fprintf(stderr, 206 "ftp: bad timeout: \"%s\"\n", optarg); 207 break; 208 } 209 timeout = atoi(optarg); 210 timeoutms = timeout * MILLISEC; 211 break; 212 213 case 'a': 214 autoauth = 1; 215 break; 216 217 case 'f': 218 autoauth = 1; 219 fflag = 1; 220 break; 221 222 case 'm': 223 autoauth = 1; 224 call(setmech, "ftp", optarg, 0); 225 if (code != 0) 226 exit(1); 227 break; 228 229 case 'x': 230 autoauth = 1; 231 autoencrypt = 1; 232 break; 233 234 case 's': 235 skipsyst = 1; 236 break; 237 238 case '?': 239 default: 240 usage(); 241 } 242 } 243 argc -= optind; 244 argv += optind; 245 246 if (argc > 2) 247 usage(); 248 249 fromatty = isatty(fileno(stdin)); 250 /* 251 * Scan env, then DEFAULTFTPFILE 252 * for FTP_LS_SENDS_NLST 253 */ 254 temp_string = getenv("FTP_LS_SENDS_NLST"); 255 if (temp_string == NULL) { /* env var not set */ 256 if (defopen(DEFAULTFTPFILE) == 0) { 257 /* 258 * turn off case sensitivity 259 */ 260 int flags = defcntl(DC_GETFLAGS, 0); 261 262 TURNOFF(flags, DC_CASE); 263 (void) defcntl(DC_SETFLAGS, flags); 264 265 temp_string = defread("FTP_LS_SENDS_NLST="); 266 (void) defopen(NULL); /* close default file */ 267 } 268 } 269 if (temp_string != NULL && 270 strncasecmp(temp_string, "n", 1) == 0) 271 ls_invokes_NLST = B_FALSE; 272 273 /* 274 * Set up defaults for FTP. 275 */ 276 (void) strcpy(typename, "ascii"), type = TYPE_A; 277 (void) strcpy(formname, "non-print"), form = FORM_N; 278 (void) strcpy(modename, "stream"), mode = MODE_S; 279 (void) strcpy(structname, "file"), stru = STRU_F; 280 (void) strcpy(bytename, "8"), bytesize = 8; 281 if (fromatty) 282 verbose++; 283 cpend = 0; /* no pending replies */ 284 proxy = 0; /* proxy not active */ 285 crflag = 1; /* strip c.r. on ascii gets */ 286 287 if (mechstr[0] == '\0') { 288 strlcpy(mechstr, FTP_DEF_MECH, MECH_SZ); 289 } 290 291 /* 292 * Set up the home directory in case we're globbing. 293 */ 294 cp = getlogin(); 295 if (cp != NULL) { 296 pw = getpwnam(cp); 297 } 298 if (pw == NULL) 299 pw = getpwuid(getuid()); 300 if (pw != NULL) { 301 home = homedir; 302 (void) strcpy(home, pw->pw_dir); 303 } 304 if (setjmp(timeralarm)) { 305 (void) fflush(stdout); 306 (void) printf("Connection timeout\n"); 307 exit(1); 308 } 309 (void) signal(SIGALRM, timeout_sig); 310 reset_timer(); 311 if (argc > 0) { 312 int nargc = 0; 313 char *nargv[4]; 314 315 if (setjmp(toplevel)) 316 return (0); 317 (void) signal(SIGINT, intr); 318 (void) signal(SIGPIPE, lostpeer); 319 nargv[nargc++] = "ftp"; 320 nargv[nargc++] = argv[0]; /* hostname */ 321 if (argc > 1) 322 nargv[nargc++] = argv[1]; /* port */ 323 nargv[nargc] = NULL; 324 setpeer(nargc, nargv); 325 } 326 top = setjmp(toplevel) == 0; 327 if (top) { 328 (void) signal(SIGINT, intr); 329 (void) signal(SIGPIPE, lostpeer); 330 } 331 332 for (;;) { 333 cmdscanner(top); 334 top = 1; 335 } 336 } 337 338 static void 339 usage(void) 340 { 341 (void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR); 342 exit(1); 343 } 344 345 void 346 reset_timer() 347 { 348 /* The test is just to reduce syscalls if timeouts aren't used */ 349 if (timeout) 350 alarm(timeout); 351 } 352 353 void 354 stop_timer() 355 { 356 if (timeout) 357 alarm(0); 358 } 359 360 /*ARGSUSED*/ 361 static void 362 timeout_sig(int sig) 363 { 364 longjmp(timeralarm, 1); 365 } 366 367 /*ARGSUSED*/ 368 static void 369 intr(int sig) 370 { 371 longjmp(toplevel, 1); 372 } 373 374 /*ARGSUSED*/ 375 void 376 lostpeer(int sig) 377 { 378 extern FILE *ctrl_out; 379 extern int data; 380 381 if (connected) { 382 if (ctrl_out != NULL) { 383 (void) shutdown(fileno(ctrl_out), 1+1); 384 (void) fclose(ctrl_out); 385 ctrl_out = NULL; 386 } 387 if (data >= 0) { 388 (void) shutdown(data, 1+1); 389 (void) close(data); 390 data = -1; 391 } 392 connected = 0; 393 394 auth_type = AUTHTYPE_NONE; 395 clevel = dlevel = PROT_C; 396 goteof = 0; 397 } 398 pswitch(1); 399 if (connected) { 400 if (ctrl_out != NULL) { 401 (void) shutdown(fileno(ctrl_out), 1+1); 402 (void) fclose(ctrl_out); 403 ctrl_out = NULL; 404 } 405 connected = 0; 406 407 auth_type = AUTHTYPE_NONE; 408 clevel = dlevel = PROT_C; 409 goteof = 0; 410 } 411 proxflag = 0; 412 pswitch(0); 413 } 414 415 /* 416 * Command parser. 417 */ 418 static void 419 cmdscanner(int top) 420 { 421 struct cmd *c; 422 423 if (!top) 424 (void) putchar('\n'); 425 for (;;) { 426 stop_timer(); 427 if (fromatty) { 428 (void) printf("ftp> "); 429 (void) fflush(stdout); 430 } 431 if (fgets(line, sizeof (line), stdin) == 0) { 432 if (feof(stdin) || ferror(stdin)) 433 quit(0, NULL); 434 break; 435 } 436 if (line[0] == 0) 437 break; 438 /* If not all, just discard rest of line */ 439 if (line[strlen(line)-1] != '\n') { 440 while (fgetc(stdin) != '\n' && !feof(stdin) && 441 !ferror(stdin)) 442 ; 443 (void) printf("Line too long\n"); 444 continue; 445 } else 446 line[strlen(line)-1] = 0; 447 448 makeargv(); 449 if (margc == 0) { 450 continue; 451 } 452 c = getcmd(margv[0]); 453 if (c == (struct cmd *)-1) { 454 (void) printf("?Ambiguous command\n"); 455 continue; 456 } 457 if (c == 0) { 458 (void) printf("?Invalid command\n"); 459 continue; 460 } 461 if (c->c_conn && !connected) { 462 (void) printf("Not connected.\n"); 463 continue; 464 } 465 reset_timer(); 466 (*c->c_handler)(margc, margv); 467 #ifndef CTRL 468 #define CTRL(c) ((c)&037) 469 #endif 470 stop_timer(); 471 if (bell && c->c_bell) 472 (void) putchar(CTRL('g')); 473 if (c->c_handler != help) 474 break; 475 } 476 (void) signal(SIGINT, intr); 477 (void) signal(SIGPIPE, lostpeer); 478 } 479 480 struct cmd * 481 getcmd(char *name) 482 { 483 char *p, *q; 484 struct cmd *c, *found; 485 int nmatches, longest; 486 extern struct cmd cmdtab[]; 487 488 if (name == NULL) 489 return (0); 490 491 longest = 0; 492 nmatches = 0; 493 found = 0; 494 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 495 for (q = name; *q == *p++; q++) 496 if (*q == 0) /* exact match? */ 497 return (c); 498 if (!*q) { /* the name was a prefix */ 499 if (q - name > longest) { 500 longest = q - name; 501 nmatches = 1; 502 found = c; 503 } else if (q - name == longest) 504 nmatches++; 505 } 506 } 507 if (nmatches > 1) 508 return ((struct cmd *)-1); 509 return (found); 510 } 511 512 /* 513 * Slice a string up into argc/argv. 514 */ 515 516 static int slrflag; 517 #define MARGV_INC 20 518 519 void 520 makeargv(void) 521 { 522 char **argp; 523 static int margv_size; 524 525 margc = 0; 526 stringbase = line; /* scan from first of buffer */ 527 argbase = argbuf; /* store from first of buffer */ 528 slrflag = 0; 529 530 if (!margv) { 531 margv_size = MARGV_INC; 532 if ((margv = malloc(margv_size * sizeof (char *))) == NULL) 533 fatal("Out of memory"); 534 } 535 argp = margv; 536 while (*argp++ = slurpstring()) { 537 margc++; 538 if (margc == margv_size) { 539 margv_size += MARGV_INC; 540 if ((margv = realloc(margv, 541 margv_size * sizeof (char *))) == NULL) 542 fatal("Out of memory"); 543 argp = margv + margc; 544 } 545 } 546 } 547 548 /* 549 * Parse string into argbuf; 550 * implemented with FSM to 551 * handle quoting and strings 552 */ 553 static char * 554 slurpstring(void) 555 { 556 int got_one = 0; 557 char *sb = stringbase; 558 char *ap = argbase; 559 char *tmp = argbase; /* will return this if token found */ 560 int len; 561 562 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 563 switch (slrflag) { /* and $ as token for macro invoke */ 564 case 0: 565 slrflag++; 566 stringbase++; 567 return ((*sb == '!') ? "!" : "$"); 568 case 1: 569 slrflag++; 570 altarg = stringbase; 571 break; 572 default: 573 break; 574 } 575 } 576 577 S0: 578 switch (*sb) { 579 580 case '\0': 581 goto OUT; 582 583 case ' ': 584 case '\t': 585 sb++; goto S0; 586 587 default: 588 switch (slrflag) { 589 case 0: 590 slrflag++; 591 break; 592 case 1: 593 slrflag++; 594 altarg = sb; 595 break; 596 default: 597 break; 598 } 599 goto S1; 600 } 601 602 S1: 603 switch (*sb) { 604 605 case ' ': 606 case '\t': 607 case '\0': 608 goto OUT; /* end of token */ 609 610 case '\\': 611 sb++; goto S2; /* slurp next character */ 612 613 case '"': 614 sb++; goto S3; /* slurp quoted string */ 615 616 default: 617 if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 618 len = 1; 619 memcpy(ap, sb, len); 620 ap += len; 621 sb += len; 622 got_one = 1; 623 goto S1; 624 } 625 626 S2: 627 switch (*sb) { 628 629 case '\0': 630 goto OUT; 631 632 default: 633 if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 634 len = 1; 635 memcpy(ap, sb, len); 636 ap += len; 637 sb += len; 638 got_one = 1; 639 goto S1; 640 } 641 642 S3: 643 switch (*sb) { 644 645 case '\0': 646 goto OUT; 647 648 case '"': 649 sb++; goto S1; 650 651 default: 652 if ((len = mblen(sb, MB_CUR_MAX)) <= 0) 653 len = 1; 654 memcpy(ap, sb, len); 655 ap += len; 656 sb += len; 657 got_one = 1; 658 goto S3; 659 } 660 661 OUT: 662 if (got_one) 663 *ap++ = '\0'; 664 argbase = ap; /* update storage pointer */ 665 stringbase = sb; /* update scan pointer */ 666 if (got_one) { 667 return (tmp); 668 } 669 switch (slrflag) { 670 case 0: 671 slrflag++; 672 break; 673 case 1: 674 slrflag++; 675 altarg = (char *)0; 676 break; 677 default: 678 break; 679 } 680 return ((char *)0); 681 } 682 683 #define HELPINDENT (sizeof ("directory")) 684 685 /* 686 * Help command. 687 * Call each command handler with argc == 0 and argv[0] == name. 688 */ 689 void 690 help(int argc, char *argv[]) 691 { 692 struct cmd *c; 693 extern struct cmd cmdtab[]; 694 695 if (argc == 1) { 696 int i, j, w, k; 697 int columns, width = 0, lines; 698 extern int NCMDS; 699 700 (void) printf( 701 "Commands may be abbreviated. Commands are:\n\n"); 702 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { 703 int len = strlen(c->c_name); 704 705 if (len > width) 706 width = len; 707 } 708 width = (width + 8) &~ 7; 709 columns = 80 / width; 710 if (columns == 0) 711 columns = 1; 712 lines = (NCMDS + columns - 1) / columns; 713 for (i = 0; i < lines; i++) { 714 for (j = 0; j < columns; j++) { 715 c = cmdtab + j * lines + i; 716 if (c->c_name && (!proxy || c->c_proxy)) { 717 (void) printf("%s", c->c_name); 718 } else if (c->c_name) { 719 for (k = 0; k < strlen(c->c_name); 720 k++) { 721 (void) putchar(' '); 722 } 723 } 724 if (c + lines >= &cmdtab[NCMDS]) { 725 (void) printf("\n"); 726 break; 727 } 728 w = strlen(c->c_name); 729 while (w < width) { 730 w = (w + 8) &~ 7; 731 (void) putchar('\t'); 732 } 733 } 734 } 735 return; 736 } 737 while (--argc > 0) { 738 char *arg; 739 arg = *++argv; 740 c = getcmd(arg); 741 if (c == (struct cmd *)-1) 742 (void) printf("?Ambiguous help command %s\n", arg); 743 else if (c == (struct cmd *)0) 744 (void) printf("?Invalid help command %s\n", arg); 745 else 746 (void) printf("%-*s\t%s\n", HELPINDENT, 747 c->c_name, c->c_help); 748 } 749 } 750 751 /* 752 * Call routine with argc, argv set from args (terminated by 0). 753 */ 754 void 755 call(void (*routine)(int argc, char *argv[]), ...) 756 { 757 va_list ap; 758 char *argv[10]; 759 int argc = 0; 760 761 va_start(ap, routine); 762 while ((argv[argc] = va_arg(ap, char *)) != (char *)0) 763 argc++; 764 va_end(ap); 765 (*routine)(argc, argv); 766 } 767