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
main(int argc,char * argv[])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
usage(void)339 usage(void)
340 {
341 (void) fprintf(stderr, "usage: ftp %s\n", USAGE_STR);
342 exit(1);
343 }
344
345 void
reset_timer()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
stop_timer()354 stop_timer()
355 {
356 if (timeout)
357 alarm(0);
358 }
359
360 /*ARGSUSED*/
361 static void
timeout_sig(int sig)362 timeout_sig(int sig)
363 {
364 longjmp(timeralarm, 1);
365 }
366
367 /*ARGSUSED*/
368 static void
intr(int sig)369 intr(int sig)
370 {
371 longjmp(toplevel, 1);
372 }
373
374 /*ARGSUSED*/
375 void
lostpeer(int sig)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
cmdscanner(int top)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 *
getcmd(char * name)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
makeargv(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 *
slurpstring(void)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
help(int argc,char * argv[])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
call(void (* routine)(int argc,char * argv[]),...)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