xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/main.c (revision 0245b61fd282e95735b173b8d95be0d6688163b4)
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