xref: /netbsd/usr.bin/tftp/main.c (revision b9c2822d)
1 /*	$NetBSD: main.c,v 1.32 2012/07/16 09:20:26 he Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #if 0
37 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
38 #else
39 __RCSID("$NetBSD: main.c,v 1.32 2012/07/16 09:20:26 he Exp $");
40 #endif
41 #endif /* not lint */
42 
43 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
44 
45 /*
46  * TFTP User Program -- Command Interface.
47  */
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 
52 #include <netinet/in.h>
53 
54 #include <arpa/inet.h>
55 #include <arpa/tftp.h>
56 
57 #include <ctype.h>
58 #include <fcntl.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <netdb.h>
62 #include <setjmp.h>
63 #include <signal.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <unistd.h>
68 
69 #include "extern.h"
70 
71 #define	TIMEOUT		5		/* secs between rexmt's */
72 #define	LBUFLEN		200		/* size of input buffer */
73 
74 struct	sockaddr_storage peeraddr;
75 int	f;
76 int	mf;
77 int	trace;
78 int	verbose;
79 int	tsize=0;
80 int	tout=0;
81 size_t	def_blksize = SEGSIZE;
82 size_t	blksize = SEGSIZE;
83 in_addr_t	mcaddr = INADDR_NONE;
84 uint16_t	mcport;
85 u_int	def_rexmtval = TIMEOUT;
86 u_int	rexmtval = TIMEOUT;
87 ushort	mcmasterslave;
88 int	maxtimeout = 5 * TIMEOUT;
89 
90 jmp_buf	toplevel;
91 
92 static int	connected;
93 static char	mode[32];
94 static char	line[LBUFLEN];
95 static int	margc;
96 static char	*margv[20];
97 static const	char *prompt = "tftp";
98 static char    hostname[MAXHOSTNAMELEN];
99 
100 static void	get(int, char **);
101 static void	help(int, char **);
102 static void	modecmd(int, char **);
103 static void	put(int, char **);
104 static __dead void quit(int, char **);
105 static void	setascii(int, char **);
106 static void	setbinary(int, char **);
107 static void	setpeer0(const char *, const char *);
108 static void	setpeer(int, char **);
109 static void	setrexmt(int, char **);
110 static void	settimeout(int, char **);
111 static void	settrace(int, char **);
112 static void	setverbose(int, char **);
113 static void	setblksize(int, char **);
114 static void	settsize(int, char **);
115 static void	settimeoutopt(int, char **);
116 static void	status(int, char **);
117 static char	*tail(char *);
118 static __dead void intr(int);
119 static const	struct cmd *getcmd(const char *);
120 
121 static __dead void command(void);
122 
123 static void getUsage(char *);
124 static void makeargv(void);
125 static void putUsage(const char *);
126 static void settftpmode(const char *);
127 
128 #define HELPINDENT sizeof("connect")
129 
130 struct cmd {
131 	const char *name;
132 	const char *help;
133 	void	(*handler)(int, char **);
134 };
135 
136 static const char vhelp[] = "toggle verbose mode";
137 static const char thelp[] = "toggle packet tracing";
138 static const char tshelp[] = "toggle extended tsize option";
139 static const char tohelp[] = "toggle extended timeout option";
140 static const char blhelp[] = "set an alternative blocksize (def. 512)";
141 static const char chelp[] = "connect to remote tftp";
142 static const char qhelp[] = "exit tftp";
143 static const char hhelp[] = "print help information";
144 static const char shelp[] = "send file";
145 static const char rhelp[] = "receive file";
146 static const char mhelp[] = "set file transfer mode";
147 static const char sthelp[] = "show current status";
148 static const char xhelp[] = "set per-packet retransmission timeout";
149 static const char ihelp[] = "set total retransmission timeout";
150 static const char ashelp[] = "set mode to netascii";
151 static const char bnhelp[] = "set mode to octet";
152 
153 static const struct cmd cmdtab[] = {
154 	{ "connect",	chelp,		setpeer },
155 	{ "mode",       mhelp,          modecmd },
156 	{ "put",	shelp,		put },
157 	{ "get",	rhelp,		get },
158 	{ "quit",	qhelp,		quit },
159 	{ "verbose",	vhelp,		setverbose },
160 	{ "blksize",	blhelp,		setblksize },
161 	{ "tsize",	tshelp,		settsize },
162 	{ "trace",	thelp,		settrace },
163 	{ "status",	sthelp,		status },
164 	{ "binary",     bnhelp,         setbinary },
165 	{ "ascii",      ashelp,         setascii },
166 	{ "rexmt",	xhelp,		setrexmt },
167 	{ "timeout",	ihelp,		settimeout },
168 	{ "tout",	tohelp,		settimeoutopt },
169 	{ "?",		hhelp,		help },
170 	{ .name = NULL }
171 };
172 
173 static struct	modes {
174 	const char *m_name;
175 	const char *m_mode;
176 } modes[] = {
177 	{ "ascii",	"netascii" },
178 	{ "netascii",   "netascii" },
179 	{ "binary",     "octet" },
180 	{ "image",      "octet" },
181 	{ "octet",     "octet" },
182 /*      { "mail",       "mail" },       */
183 	{ 0,		0 }
184 };
185 
186 int
main(int argc,char * argv[])187 main(int argc, char *argv[])
188 {
189 	int	c;
190 
191 	f = mf = -1;
192 	(void)strlcpy(mode, "netascii", sizeof(mode));
193 	(void)signal(SIGINT, intr);
194 
195 	setprogname(argv[0]);
196 	while ((c = getopt(argc, argv, "e")) != -1) {
197 		switch (c) {
198 		case 'e':
199 			blksize = MAXSEGSIZE;
200 			(void)strlcpy(mode, "octet", sizeof(mode));
201 			tsize = 1;
202 			tout = 1;
203 			break;
204 		default:
205 			(void)fprintf(stderr,
206 			    "Usage: %s [-e] host-name [port]\n", getprogname());
207 			exit(1);
208 		}
209 	}
210 	argc -= optind;
211 	argv += optind;
212 
213 	if (argc >= 1) {
214 		if (setjmp(toplevel) != 0)
215 			exit(0);
216 		argc++;
217 		argv--;
218 		setpeer(argc, argv);
219 	}
220 	if (setjmp(toplevel) != 0)
221 		(void)putchar('\n');
222 	command();
223 	return 0;
224 }
225 
226 static void
getmore(const char * cmd,const char * prm)227 getmore(const char *cmd, const char *prm)
228 {
229 	(void)strlcpy(line, cmd, sizeof(line));
230 	(void)printf("%s", prm);
231 	(void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
232 	makeargv();
233 }
234 
235 static void
setpeer0(const char * host,const char * port)236 setpeer0(const char *host, const char *port)
237 {
238 	struct addrinfo hints, *res0, *res;
239 	int error, soopt;
240 	struct sockaddr_storage ss;
241 	const char *cause = "unknown";
242 
243 	if (connected) {
244 		(void)close(f);
245 		f = -1;
246 	}
247 	connected = 0;
248 
249 	(void)memset(&hints, 0, sizeof(hints));
250 	hints.ai_family = PF_UNSPEC;
251 	hints.ai_socktype = SOCK_DGRAM;
252 	hints.ai_protocol = IPPROTO_UDP;
253 	hints.ai_flags = AI_CANONNAME;
254 	if (!port)
255 		port = "tftp";
256 	error = getaddrinfo(host, port, &hints, &res0);
257 	if (error) {
258 		warnx("%s", gai_strerror(error));
259 		return;
260 	}
261 
262 	for (res = res0; res; res = res->ai_next) {
263 		if (res->ai_addrlen > sizeof(peeraddr))
264 			continue;
265 		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
266 		if (f == -1) {
267 			cause = "socket";
268 			continue;
269 		}
270 
271 		(void)memset(&ss, 0, sizeof(ss));
272 		ss.ss_family = res->ai_family;
273 		ss.ss_len = res->ai_addrlen;
274 		if (bind(f, (struct sockaddr *)(void *)&ss,
275 		    (socklen_t)ss.ss_len) == -1) {
276 			cause = "bind";
277 			(void)close(f);
278 			f = -1;
279 			continue;
280 		}
281 
282 		break;
283 	}
284 
285 	if (f >= 0) {
286 		soopt = 65536;
287 		if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt))
288 		    == -1) {
289 			(void)close(f);
290 			f = -1;
291 			cause = "setsockopt SNDBUF";
292 		}
293 		else if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt,
294 		    sizeof(soopt)) == -1) {
295 			(void)close(f);
296 			f = -1;
297 			cause = "setsockopt RCVBUF";
298 		}
299 	}
300 
301 	if (f == -1 || res == NULL)
302 		warn("%s", cause);
303 	else {
304 		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
305 		(void)memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
306 		if (res->ai_canonname) {
307 			(void)strlcpy(hostname, res->ai_canonname,
308 			    sizeof(hostname));
309 		} else
310 			(void)strlcpy(hostname, host, sizeof(hostname));
311 		connected = 1;
312 	}
313 
314 	freeaddrinfo(res0);
315 }
316 
317 static void
setpeer(int argc,char * argv[])318 setpeer(int argc, char *argv[])
319 {
320 
321 	if (argc < 2) {
322 		getmore("Connect ", "(to) ");
323 		argc = margc;
324 		argv = margv;
325 	}
326 	if (argc < 2 || argc > 3) {
327 		(void)printf("Usage: %s [-e] host-name [port]\n",
328 		    getprogname());
329 		return;
330 	}
331 	if (argc == 2)
332 		setpeer0(argv[1], NULL);
333 	else
334 		setpeer0(argv[1], argv[2]);
335 }
336 
337 static void
modecmd(int argc,char * argv[])338 modecmd(int argc, char *argv[])
339 {
340 	struct modes *p;
341 	const char *sep;
342 
343 	if (argc < 2) {
344 		(void)printf("Using %s mode to transfer files.\n", mode);
345 		return;
346 	}
347 	if (argc == 2) {
348 		for (p = modes; p->m_name; p++)
349 			if (strcmp(argv[1], p->m_name) == 0)
350 				break;
351 		if (p->m_name) {
352 			settftpmode(p->m_mode);
353 			return;
354 		}
355 		(void)printf("%s: unknown mode\n", argv[1]);
356 		/* drop through and print Usage message */
357 	}
358 
359 	(void)printf("Usage: %s [", argv[0]);
360 	sep = " ";
361 	for (p = modes; p->m_name; p++) {
362 		(void)printf("%s%s", sep, p->m_name);
363 		if (*sep == ' ')
364 			sep = " | ";
365 	}
366 	(void)printf(" ]\n");
367 	return;
368 }
369 
370 static void
371 /*ARGSUSED*/
setbinary(int argc,char * argv[])372 setbinary(int argc, char *argv[])
373 {
374 
375 	settftpmode("octet");
376 }
377 
378 static void
379 /*ARGSUSED*/
setascii(int argc,char * argv[])380 setascii(int argc, char *argv[])
381 {
382 
383 	settftpmode("netascii");
384 }
385 
386 static void
settftpmode(const char * newmode)387 settftpmode(const char *newmode)
388 {
389 	(void)strlcpy(mode, newmode, sizeof(mode));
390 	if (verbose)
391 		(void)printf("mode set to %s\n", mode);
392 }
393 
394 
395 /*
396  * Send file(s).
397  */
398 static void
put(int argc,char * argv[])399 put(int argc, char *argv[])
400 {
401 	int fd;
402 	int n;
403 	char *targ, *p;
404 
405 	if (argc < 2) {
406 		getmore("send ", "(file) ");
407 		argc = margc;
408 		argv = margv;
409 	}
410 	if (argc < 2) {
411 		putUsage(argv[0]);
412 		return;
413 	}
414 	targ = argv[argc - 1];
415 	if (strrchr(argv[argc - 1], ':')) {
416 		char *cp;
417 
418 		for (n = 1; n < argc - 1; n++)
419 			if (strchr(argv[n], ':')) {
420 				putUsage(argv[0]);
421 				return;
422 			}
423 		cp = argv[argc - 1];
424 		targ = strrchr(cp, ':');
425 		*targ++ = 0;
426 		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
427 			cp[strlen(cp) - 1] = '\0';
428 			cp++;
429 		}
430 		setpeer0(cp, NULL);
431 	}
432 	if (!connected) {
433 		(void)printf("No target machine specified.\n");
434 		return;
435 	}
436 	if (argc < 4) {
437 		char *cp = argc == 2 ? tail(targ) : argv[1];
438 		fd = open(cp, O_RDONLY);
439 		if (fd == -1) {
440 			warn("%s", cp);
441 			return;
442 		}
443 		if (verbose)
444 			(void)printf("putting %s to %s:%s [%s]\n",
445 				cp, hostname, targ, mode);
446 		sendfile(fd, targ, mode);
447 		return;
448 	}
449 				/* this assumes the target is a directory */
450 				/* on a remote unix system.  hmmmm.  */
451 	p = strchr(targ, '\0');
452 	*p++ = '/';
453 	for (n = 1; n < argc - 1; n++) {
454 		(void)strcpy(p, tail(argv[n]));
455 		fd = open(argv[n], O_RDONLY);
456 		if (fd == -1) {
457 			warn("%s", argv[n]);
458 			continue;
459 		}
460 		if (verbose)
461 			(void)printf("putting %s to %s:%s [%s]\n",
462 				argv[n], hostname, targ, mode);
463 		sendfile(fd, targ, mode);
464 	}
465 }
466 
467 static void
putUsage(const char * s)468 putUsage(const char *s)
469 {
470 	(void)printf("Usage: %s file ... host:target, or\n", s);
471 	(void)printf("       %s file ... target (when already connected)\n", s);
472 }
473 
474 /*
475  * Receive file(s).
476  */
477 static void
get(int argc,char * argv[])478 get(int argc, char *argv[])
479 {
480 	int fd;
481 	int n;
482 	char *p;
483 	char *src;
484 
485 	if (argc < 2) {
486 		getmore("get ", "(files) ");
487 		argc = margc;
488 		argv = margv;
489 	}
490 	if (argc < 2) {
491 		getUsage(argv[0]);
492 		return;
493 	}
494 	if (!connected) {
495 		for (n = 1; n < argc ; n++)
496 			if (strrchr(argv[n], ':') == 0) {
497 				getUsage(argv[0]);
498 				return;
499 			}
500 	}
501 	for (n = 1; n < argc ; n++) {
502 		src = strrchr(argv[n], ':');
503 		if (src == NULL)
504 			src = argv[n];
505 		else {
506 			char *cp;
507 			*src++ = 0;
508 			cp = argv[n];
509 			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
510 				cp[strlen(cp) - 1] = '\0';
511 				cp++;
512 			}
513 			setpeer0(cp, NULL);
514 			if (!connected)
515 				continue;
516 		}
517 		if (argc < 4) {
518 			char *cp = argc == 3 ? argv[2] : tail(src);
519 			fd = creat(cp, 0644);
520 			if (fd == -1) {
521 				warn("%s", cp);
522 				return;
523 			}
524 			if (verbose)
525 				(void)printf("getting from %s:%s to %s [%s]\n",
526 					hostname, src, cp, mode);
527 			recvfile(fd, src, mode);
528 			break;
529 		}
530 		p = tail(src);         /* new .. jdg */
531 		fd = creat(p, 0644);
532 		if (fd == -1) {
533 			warn("%s", p);
534 			continue;
535 		}
536 		if (verbose)
537 			(void)printf("getting from %s:%s to %s [%s]\n",
538 				hostname, src, p, mode);
539 		recvfile(fd, src, mode);
540 	}
541 }
542 
543 static void
getUsage(char * s)544 getUsage(char *s)
545 {
546 	(void)printf("Usage: %s host:file host:file ... file, or\n", s);
547 	(void)printf("       %s file file ... file if connected\n", s);
548 }
549 
550 void
setblksize(int argc,char * argv[])551 setblksize(int argc, char *argv[])
552 {
553 	int t;
554 
555 	if (argc < 2) {
556 		getmore("blksize ", "(blksize) ");
557 		argc = margc;
558 		argv = margv;
559 	}
560 	if (argc != 2) {
561 		(void)printf("Usage: %s value\n", argv[0]);
562 		return;
563 	}
564 	t = atoi(argv[1]);
565 	if (t < 8 || t > 65464)
566 		(void)printf("%s: bad value\n", argv[1]);
567 	else
568 		blksize = t;
569 }
570 
571 static void
setrexmt(int argc,char * argv[])572 setrexmt(int argc, char *argv[])
573 {
574 	int t;
575 
576 	if (argc < 2) {
577 		getmore("Rexmt-timeout ", "(value) ");
578 		argc = margc;
579 		argv = margv;
580 	}
581 	if (argc != 2) {
582 		(void)printf("Usage: %s value\n", argv[0]);
583 		return;
584 	}
585 	t = atoi(argv[1]);
586 	if (t < 0)
587 		(void)printf("%s: bad value\n", argv[1]);
588 	else
589 		rexmtval = t;
590 }
591 
592 static void
settimeout(int argc,char * argv[])593 settimeout(int argc, char *argv[])
594 {
595 	int t;
596 
597 	if (argc < 2) {
598 		getmore("Maximum-timeout ", "(value) ");
599 		argc = margc;
600 		argv = margv;
601 	}
602 	if (argc != 2) {
603 		(void)printf("Usage: %s value\n", argv[0]);
604 		return;
605 	}
606 	t = atoi(argv[1]);
607 	if (t < 0)
608 		(void)printf("%s: bad value\n", argv[1]);
609 	else
610 		maxtimeout = t;
611 }
612 
613 static void
614 /*ARGSUSED*/
status(int argc,char * argv[])615 status(int argc, char *argv[])
616 {
617 	if (connected)
618 		(void)printf("Connected to %s.\n", hostname);
619 	else
620 		(void)printf("Not connected.\n");
621 	(void)printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
622 		verbose ? "on" : "off", trace ? "on" : "off");
623 	(void)printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
624 		rexmtval, maxtimeout);
625 }
626 
627 static void
628 /*ARGSUSED*/
intr(int dummy)629 intr(int dummy)
630 {
631 
632 	(void)signal(SIGALRM, SIG_IGN);
633 	(void)alarm(0);
634 	longjmp(toplevel, -1);
635 }
636 
637 static char *
tail(char * filename)638 tail(char *filename)
639 {
640 	char *s;
641 
642 	while (*filename) {
643 		s = strrchr(filename, '/');
644 		if (s == NULL)
645 			break;
646 		if (s[1])
647 			return s + 1;
648 		*s = '\0';
649 	}
650 	return filename;
651 }
652 
653 /*
654  * Command parser.
655  */
656 static __dead void
command(void)657 command(void)
658 {
659 	const struct cmd *c;
660 
661 	for (;;) {
662 		(void)printf("%s> ", prompt);
663 		if (fgets(line, LBUFLEN, stdin) == NULL) {
664 			if (feof(stdin)) {
665 				exit(0);
666 			} else {
667 				continue;
668 			}
669 		}
670 		if (line[0] == '\0' || line[0] == '\n')
671 			continue;
672 		makeargv();
673 		if (margc == 0)
674 			continue;
675 		c = getcmd(margv[0]);
676 		if (c == (struct cmd *)-1) {
677 			(void)printf("?Ambiguous command\n");
678 			continue;
679 		}
680 		if (c == 0) {
681 			(void)printf("?Invalid command\n");
682 			continue;
683 		}
684 		(*c->handler)(margc, margv);
685 	}
686 }
687 
688 static const struct cmd *
getcmd(const char * name)689 getcmd(const char *name)
690 {
691 	const char *p, *q;
692 	const struct cmd *c, *found;
693 	int nmatches, longest;
694 
695 	longest = 0;
696 	nmatches = 0;
697 	found = 0;
698 	for (c = cmdtab; (p = c->name) != NULL; c++) {
699 		for (q = name; *q == *p++; q++)
700 			if (*q == 0)		/* exact match? */
701 				return c;
702 		if (!*q) {			/* the name was a prefix */
703 			if (q - name > longest) {
704 				longest = q - name;
705 				nmatches = 1;
706 				found = c;
707 			} else if (q - name == longest)
708 				nmatches++;
709 		}
710 	}
711 	if (nmatches > 1)
712 		return (struct cmd *)-1;
713 	return found;
714 }
715 
716 /*
717  * Slice a string up into argc/argv.
718  */
719 static void
makeargv(void)720 makeargv(void)
721 {
722 	char *cp;
723 	char **argp = margv;
724 
725 	margc = 0;
726 	for (cp = line; *cp;) {
727 		while (isspace((unsigned char)*cp))
728 			cp++;
729 		if (*cp == '\0')
730 			break;
731 		*argp++ = cp;
732 		margc += 1;
733 		while (*cp != '\0' && !isspace((unsigned char)*cp))
734 			cp++;
735 		if (*cp == '\0')
736 			break;
737 		*cp++ = '\0';
738 	}
739 	*argp++ = NULL;
740 }
741 
742 static void
743 /*ARGSUSED*/
quit(int argc,char * argv[])744 quit(int argc, char *argv[])
745 {
746 
747 	exit(0);
748 }
749 
750 /*
751  * Help command.
752  */
753 static void
help(int argc,char * argv[])754 help(int argc, char *argv[])
755 {
756 	const struct cmd *c;
757 
758 	if (argc == 1) {
759 		(void)printf("Commands may be abbreviated.  Commands are:\n\n");
760 		for (c = cmdtab; c->name; c++)
761 			(void)printf("%-*s\t%s\n", (int)HELPINDENT, c->name,
762 			    c->help);
763 		return;
764 	}
765 	while (--argc > 0) {
766 		char *arg;
767 		arg = *++argv;
768 		c = getcmd(arg);
769 		if (c == (struct cmd *)-1)
770 			(void)printf("?Ambiguous help command %s\n", arg);
771 		else if (c == NULL)
772 			(void)printf("?Invalid help command %s\n", arg);
773 		else
774 			(void)printf("%s\n", c->help);
775 	}
776 }
777 
778 static void
779 /*ARGSUSED*/
settrace(int argc,char ** argv)780 settrace(int argc, char **argv)
781 {
782 	trace = !trace;
783 	(void)printf("Packet tracing %s.\n", trace ? "on" : "off");
784 }
785 
786 static void
787 /*ARGSUSED*/
setverbose(int argc,char ** argv)788 setverbose(int argc, char **argv)
789 {
790 	verbose = !verbose;
791 	(void)printf("Verbose mode %s.\n", verbose ? "on" : "off");
792 }
793 
794 static void
795 /*ARGSUSED*/
settsize(int argc,char ** argv)796 settsize(int argc, char **argv)
797 {
798 	tsize = !tsize;
799 	(void)printf("Tsize mode %s.\n", tsize ? "on" : "off");
800 }
801 
802 static void
803 /*ARGSUSED*/
settimeoutopt(int argc,char ** argv)804 settimeoutopt(int argc, char **argv)
805 {
806 	tout = !tout;
807 	(void)printf("Timeout option %s.\n", tout ? "on" : "off");
808 }
809