1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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#ifndef lint
33static const char copyright[] =
34"@(#) Copyright (c) 1983, 1993\n\
35	The Regents of the University of California.  All rights reserved.\n";
36#endif
37
38#if 0
39#ifndef lint
40static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
41#endif
42#endif
43
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD$");
46
47/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
48
49/*
50 * TFTP User Program -- Command Interface.
51 */
52#include <sys/param.h>
53#include <sys/types.h>
54#include <sys/socket.h>
55#include <sys/sysctl.h>
56#include <sys/file.h>
57#include <sys/stat.h>
58
59#include <netinet/in.h>
60#include <arpa/inet.h>
61#include <arpa/tftp.h>
62
63#include <ctype.h>
64#include <err.h>
65#include <histedit.h>
66#include <netdb.h>
67#include <setjmp.h>
68#include <signal.h>
69#include <stdbool.h>
70#include <stdio.h>
71#include <stdlib.h>
72#include <string.h>
73#include <unistd.h>
74
75#include "tftp-utils.h"
76#include "tftp-io.h"
77#include "tftp-options.h"
78#include "tftp.h"
79
80#define	MAXLINE		(2 * MAXPATHLEN)
81#define	TIMEOUT		5		/* secs between rexmt's */
82
83typedef struct	sockaddr_storage peeraddr;
84static int	connected;
85static char	mode[32];
86static jmp_buf	toplevel;
87volatile int	txrx_error;
88static int	peer;
89
90#define	MAX_MARGV	20
91static int	margc;
92static char	*margv[MAX_MARGV];
93
94int		verbose;
95static char	*port = NULL;
96
97static void	get(int, char **);
98static void	help(int, char **);
99static void	intr(int);
100static void	modecmd(int, char **);
101static void	put(int, char **);
102static void	quit(int, char **);
103static void	setascii(int, char **);
104static void	setbinary(int, char **);
105static void	setpeer0(char *, const char *);
106static void	setpeer(int, char **);
107static void	settimeoutpacket(int, char **);
108static void	settimeoutnetwork(int, char **);
109static void	setdebug(int, char **);
110static void	setverbose(int, char **);
111static void	showstatus(int, char **);
112static void	setblocksize(int, char **);
113static void	setblocksize2(int, char **);
114static void	setoptions(int, char **);
115static void	setrollover(int, char **);
116static void	setpacketdrop(int, char **);
117static void	setwindowsize(int, char **);
118
119static void command(bool, EditLine *, History *, HistEvent *) __dead2;
120static const char *command_prompt(void);
121
122static void urihandling(char *URI);
123static void getusage(char *);
124static void makeargv(char *line);
125static void putusage(char *);
126static void settftpmode(const char *);
127
128static char	*tail(char *);
129static struct	cmd *getcmd(char *);
130
131#define HELPINDENT (sizeof("connect"))
132
133struct cmd {
134	const char	*name;
135	void	(*handler)(int, char **);
136	const char	*help;
137};
138
139static struct cmd cmdtab[] = {
140	{ "connect",	setpeer,	"connect to remote tftp"	},
141	{ "mode",	modecmd,	"set file transfer mode"	},
142	{ "put",	put,		"send file"			},
143	{ "get",	get,		"receive file"			},
144	{ "quit",	quit,		"exit tftp"			},
145	{ "verbose",	setverbose,	"toggle verbose mode"		},
146	{ "status",	showstatus,	"show current status"		},
147	{ "binary",     setbinary,	"set mode to octet"		},
148	{ "ascii",      setascii,	"set mode to netascii"		},
149	{ "rexmt",	settimeoutpacket,
150	  "set per-packet retransmission timeout[-]" },
151	{ "timeout",	settimeoutnetwork,
152	  "set total retransmission timeout" },
153	{ "trace",	setdebug,	"enable 'debug packet'[-]"	},
154	{ "debug",	setdebug,	"enable verbose output"		},
155	{ "blocksize",	setblocksize,	"set blocksize[*]"		},
156	{ "blocksize2",	setblocksize2,	"set blocksize as a power of 2[**]" },
157	{ "rollover",	setrollover,	"rollover after 64K packets[**]" },
158	{ "options",	setoptions,
159	  "enable or disable RFC2347 style options" },
160	{ "help",	help,		"print help information"	},
161	{ "packetdrop",	setpacketdrop,	"artificial packetloss feature"	},
162	{ "windowsize",	setwindowsize,	"set windowsize[*]"		},
163	{ "?",		help,		"print help information"	},
164	{ NULL,		NULL,		NULL				}
165};
166
167static struct	modes {
168	const char *m_name;
169	const char *m_mode;
170} modes[] = {
171	{ "ascii",	"netascii" },
172	{ "netascii",	"netascii" },
173	{ "binary",	"octet" },
174	{ "image",	"octet" },
175	{ "octet",	"octet" },
176	{ NULL,		NULL }
177};
178
179int
180main(int argc, char *argv[])
181{
182	HistEvent he;
183	static EditLine *el;
184	static History *hist;
185	bool interactive;
186
187	acting_as_client = 1;
188	peer = -1;
189	strcpy(mode, "octet");
190	signal(SIGINT, intr);
191
192	interactive = isatty(STDIN_FILENO);
193	if (interactive) {
194		el = el_init("tftp", stdin, stdout, stderr);
195		hist = history_init();
196		history(hist, &he, H_SETSIZE, 100);
197		el_set(el, EL_HIST, history, hist);
198		el_set(el, EL_EDITOR, "emacs");
199		el_set(el, EL_PROMPT, command_prompt);
200		el_set(el, EL_SIGNAL, 1);
201		el_source(el, NULL);
202	}
203
204	if (argc > 1) {
205		if (setjmp(toplevel) != 0)
206			exit(txrx_error);
207
208		if (strncmp(argv[1], "tftp://", 7) == 0) {
209			urihandling(argv[1]);
210			exit(txrx_error);
211		}
212
213		setpeer(argc, argv);
214	}
215
216	if (setjmp(toplevel) != 0) {
217		if (interactive)
218			el_reset(el);
219		(void)putchar('\n');
220	}
221
222	init_options();
223	command(interactive, el, hist, &he);
224}
225
226/*
227 * RFC3617 handling of TFTP URIs:
228 *
229 *    tftpURI         = "tftp://" host "/" file [ mode ]
230 *    mode            = ";"  "mode=" ( "netascii" / "octet" )
231 *    file            = *( unreserved / escaped )
232 *    host            = <as specified by RFC 2732>
233 *    unreserved      = <as specified in RFC 2396>
234 *    escaped         = <as specified in RFC 2396>
235 *
236 * We are cheating a little bit by allowing any mode as specified in the
237 * modes table defined earlier on in this file and mapping it on the real
238 * mode.
239 */
240static void
241urihandling(char *URI)
242{
243	char	uri[ARG_MAX];
244	char	*host = NULL;
245	char	*path = NULL;
246	char	*opts = NULL;
247	const char *tmode = "octet";
248	char	*s;
249	char	line[MAXLINE];
250	int	i;
251
252	strlcpy(uri, URI, ARG_MAX);
253	host = uri + 7;
254
255	if ((s = strchr(host, '/')) == NULL) {
256		fprintf(stderr,
257		    "Invalid URI: Couldn't find / after hostname\n");
258		exit(1);
259	}
260	*s = '\0';
261	path = s + 1;
262
263	if ((s = strchr(path, ';')) != NULL) {
264		*s = '\0';
265		opts = s + 1;
266
267		if (strncmp(opts, "mode=", 5) == 0) {
268			tmode = opts;
269			tmode += 5;
270
271			for (i = 0; modes[i].m_name != NULL; i++) {
272				if (strcmp(modes[i].m_name, tmode) == 0)
273					break;
274			}
275			if (modes[i].m_name == NULL) {
276				fprintf(stderr, "Invalid mode: '%s'\n", mode);
277				exit(1);
278			}
279			settftpmode(modes[i].m_mode);
280		}
281	} else {
282		settftpmode("octet");
283	}
284
285	setpeer0(host, NULL);
286
287	sprintf(line, "get %s", path);
288	makeargv(line);
289	get(margc, margv);
290}
291
292static char    hostname[MAXHOSTNAMELEN];
293
294static void
295setpeer0(char *host, const char *lport)
296{
297	struct addrinfo hints, *res0, *res;
298	int error;
299	const char *cause = "unknown";
300
301	if (connected) {
302		close(peer);
303		peer = -1;
304	}
305	connected = 0;
306
307	memset(&hints, 0, sizeof(hints));
308	hints.ai_family = PF_UNSPEC;
309	hints.ai_socktype = SOCK_DGRAM;
310	hints.ai_protocol = IPPROTO_UDP;
311	hints.ai_flags = AI_CANONNAME;
312	if (!lport)
313		lport = "tftp";
314	error = getaddrinfo(host, lport, &hints, &res0);
315	if (error) {
316		warnx("%s", gai_strerror(error));
317		return;
318	}
319
320	for (res = res0; res; res = res->ai_next) {
321		if (res->ai_addrlen > sizeof(peeraddr))
322			continue;
323		peer = socket(res->ai_family, res->ai_socktype,
324			res->ai_protocol);
325		if (peer < 0) {
326			cause = "socket";
327			continue;
328		}
329
330		memset(&peer_sock, 0, sizeof(peer_sock));
331		peer_sock.ss_family = res->ai_family;
332		peer_sock.ss_len = res->ai_addrlen;
333		if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) {
334			cause = "bind";
335			close(peer);
336			peer = -1;
337			continue;
338		}
339
340		break;
341	}
342
343	if (peer < 0)
344		warn("%s", cause);
345	else {
346		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
347		memcpy(&peer_sock, res->ai_addr, res->ai_addrlen);
348		if (res->ai_canonname) {
349			(void) strlcpy(hostname, res->ai_canonname,
350				sizeof(hostname));
351		} else
352			(void) strlcpy(hostname, host, sizeof(hostname));
353		connected = 1;
354	}
355
356	freeaddrinfo(res0);
357}
358
359static void
360setpeer(int argc, char *argv[])
361{
362	char	line[MAXLINE];
363
364	if (argc < 2) {
365		strcpy(line, "Connect ");
366		printf("(to) ");
367		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
368		makeargv(line);
369		argc = margc;
370		argv = margv;
371	}
372	if ((argc < 2) || (argc > 3)) {
373		printf("usage: %s [host [port]]\n", argv[0]);
374		return;
375	}
376	if (argc == 3) {
377		port = argv[2];
378		setpeer0(argv[1], argv[2]);
379	} else
380		setpeer0(argv[1], NULL);
381}
382
383static void
384modecmd(int argc, char *argv[])
385{
386	struct modes *p;
387	const char *sep;
388
389	if (argc < 2) {
390		printf("Using %s mode to transfer files.\n", mode);
391		return;
392	}
393	if (argc == 2) {
394		for (p = modes; p->m_name; p++)
395			if (strcmp(argv[1], p->m_name) == 0)
396				break;
397		if (p->m_name) {
398			settftpmode(p->m_mode);
399			return;
400		}
401		printf("%s: unknown mode\n", argv[1]);
402		/* drop through and print usage message */
403	}
404
405	printf("usage: %s [", argv[0]);
406	sep = " ";
407	for (p = modes; p->m_name != NULL; p++) {
408		printf("%s%s", sep, p->m_name);
409		if (*sep == ' ')
410			sep = " | ";
411	}
412	printf(" ]\n");
413	return;
414}
415
416static void
417setbinary(int argc __unused, char *argv[] __unused)
418{
419
420	settftpmode("octet");
421}
422
423static void
424setascii(int argc __unused, char *argv[] __unused)
425{
426
427	settftpmode("netascii");
428}
429
430static void
431settftpmode(const char *newmode)
432{
433
434	strlcpy(mode, newmode, sizeof(mode));
435	if (verbose)
436		printf("mode set to %s\n", mode);
437}
438
439
440/*
441 * Send file(s).
442 */
443static void
444put(int argc, char *argv[])
445{
446	int	fd;
447	int	n;
448	char	*cp, *targ;
449	char	line[MAXLINE];
450	struct stat sb;
451
452	if (argc < 2) {
453		strcpy(line, "send ");
454		printf("(file) ");
455		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
456		makeargv(line);
457		argc = margc;
458		argv = margv;
459	}
460	if (argc < 2) {
461		putusage(argv[0]);
462		return;
463	}
464	targ = argv[argc - 1];
465	if (strrchr(argv[argc - 1], ':')) {
466		char *lcp;
467
468		for (n = 1; n < argc - 1; n++)
469			if (strchr(argv[n], ':')) {
470				putusage(argv[0]);
471				return;
472			}
473		lcp = argv[argc - 1];
474		targ = strrchr(lcp, ':');
475		*targ++ = 0;
476		if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
477			lcp[strlen(lcp) - 1] = '\0';
478			lcp++;
479		}
480		setpeer0(lcp, NULL);
481	}
482	if (!connected) {
483		printf("No target machine specified.\n");
484		return;
485	}
486	if (argc < 4) {
487		cp = argc == 2 ? tail(targ) : argv[1];
488		fd = open(cp, O_RDONLY);
489		if (fd < 0) {
490			warn("%s", cp);
491			return;
492		}
493
494		if (fstat(fd, &sb) < 0) {
495			warn("%s", cp);
496			close(fd);
497			return;
498		}
499		asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size);
500
501		if (verbose)
502			printf("putting %s to %s:%s [%s]\n",
503			    cp, hostname, targ, mode);
504		xmitfile(peer, port, fd, targ, mode);
505		close(fd);
506		return;
507	}
508				/* this assumes the target is a directory */
509				/* on a remote unix system.  hmmmm.  */
510	cp = strchr(targ, '\0');
511	*cp++ = '/';
512	for (n = 1; n < argc - 1; n++) {
513		strcpy(cp, tail(argv[n]));
514		fd = open(argv[n], O_RDONLY);
515		if (fd < 0) {
516			warn("%s", argv[n]);
517			continue;
518		}
519
520		if (fstat(fd, &sb) < 0) {
521			warn("%s", argv[n]);
522			continue;
523		}
524		asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size);
525
526		if (verbose)
527			printf("putting %s to %s:%s [%s]\n",
528			    argv[n], hostname, targ, mode);
529		xmitfile(peer, port, fd, targ, mode);
530	}
531}
532
533static void
534putusage(char *s)
535{
536
537	printf("usage: %s file [remotename]\n", s);
538	printf("       %s file host:remotename\n", s);
539	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
540}
541
542/*
543 * Receive file(s).
544 */
545static void
546get(int argc, char *argv[])
547{
548	int fd;
549	int n;
550	char *cp;
551	char *src;
552	char	line[MAXLINE];
553
554	if (argc < 2) {
555		strcpy(line, "get ");
556		printf("(files) ");
557		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
558		makeargv(line);
559		argc = margc;
560		argv = margv;
561	}
562	if (argc < 2) {
563		getusage(argv[0]);
564		return;
565	}
566	if (!connected) {
567		for (n = 1; n < argc ; n++)
568			if (strrchr(argv[n], ':') == 0) {
569				printf("No remote host specified and "
570				    "no host given for file '%s'\n", argv[n]);
571				getusage(argv[0]);
572				return;
573			}
574	}
575	for (n = 1; n < argc ; n++) {
576		src = strrchr(argv[n], ':');
577		if (src == NULL)
578			src = argv[n];
579		else {
580			char *lcp;
581
582			*src++ = 0;
583			lcp = argv[n];
584			if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
585				lcp[strlen(lcp) - 1] = '\0';
586				lcp++;
587			}
588			setpeer0(lcp, NULL);
589			if (!connected)
590				continue;
591		}
592		if (argc < 4) {
593			cp = argc == 3 ? argv[2] : tail(src);
594			fd = creat(cp, 0644);
595			if (fd < 0) {
596				warn("%s", cp);
597				return;
598			}
599			if (verbose)
600				printf("getting from %s:%s to %s [%s]\n",
601				    hostname, src, cp, mode);
602			recvfile(peer, port, fd, src, mode);
603			break;
604		}
605		cp = tail(src);         /* new .. jdg */
606		fd = creat(cp, 0644);
607		if (fd < 0) {
608			warn("%s", cp);
609			continue;
610		}
611		if (verbose)
612			printf("getting from %s:%s to %s [%s]\n",
613			    hostname, src, cp, mode);
614		recvfile(peer, port, fd, src, mode);
615	}
616}
617
618static void
619getusage(char *s)
620{
621
622	printf("usage: %s file [localname]\n", s);
623	printf("       %s [host:]file [localname]\n", s);
624	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
625}
626
627static void
628settimeoutpacket(int argc, char *argv[])
629{
630	int t;
631	char	line[MAXLINE];
632
633	if (argc < 2) {
634		strcpy(line, "Packet timeout ");
635		printf("(value) ");
636		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
637		makeargv(line);
638		argc = margc;
639		argv = margv;
640	}
641	if (argc != 2) {
642		printf("usage: %s value\n", argv[0]);
643		return;
644	}
645	t = atoi(argv[1]);
646	if (t < 0) {
647		printf("%s: bad value\n", argv[1]);
648		return;
649	}
650
651	settimeouts(t, timeoutnetwork, maxtimeouts);
652}
653
654static void
655settimeoutnetwork(int argc, char *argv[])
656{
657	int t;
658	char	line[MAXLINE];
659
660	if (argc < 2) {
661		strcpy(line, "Network timeout ");
662		printf("(value) ");
663		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
664		makeargv(line);
665		argc = margc;
666		argv = margv;
667	}
668	if (argc != 2) {
669		printf("usage: %s value\n", argv[0]);
670		return;
671	}
672	t = atoi(argv[1]);
673	if (t < 0) {
674		printf("%s: bad value\n", argv[1]);
675		return;
676	}
677
678	settimeouts(timeoutpacket, t, maxtimeouts);
679}
680
681static void
682showstatus(int argc __unused, char *argv[] __unused)
683{
684
685	printf("Remote host: %s\n",
686	    connected ? hostname : "none specified yet");
687	printf("RFC2347 Options support: %s\n",
688	    options_rfc_enabled ? "enabled" : "disabled");
689	printf("Non-RFC defined options support: %s\n",
690	    options_extra_enabled ? "enabled" : "disabled");
691	printf("Mode: %s\n", mode);
692	printf("Verbose: %s\n", verbose ? "on" : "off");
693	printf("Debug: %s\n", debug_show(debug));
694	printf("Artificial packetloss: %d in 100 packets\n",
695	    packetdroppercentage);
696	printf("Segment size: %d bytes\n", segsize);
697	printf("Network timeout: %d seconds\n", timeoutpacket);
698	printf("Maximum network timeout: %d seconds\n", timeoutnetwork);
699	printf("Maximum timeouts: %d \n", maxtimeouts);
700}
701
702static void
703intr(int dummy __unused)
704{
705
706	signal(SIGALRM, SIG_IGN);
707	alarm(0);
708	longjmp(toplevel, -1);
709}
710
711static char *
712tail(char *filename)
713{
714	char *s;
715
716	while (*filename) {
717		s = strrchr(filename, '/');
718		if (s == NULL)
719			break;
720		if (s[1])
721			return (s + 1);
722		*s = '\0';
723	}
724	return (filename);
725}
726
727static const char *
728command_prompt(void)
729{
730
731	return ("tftp> ");
732}
733
734/*
735 * Command parser.
736 */
737static void
738command(bool interactive, EditLine *el, History *hist, HistEvent *hep)
739{
740	struct cmd *c;
741	const char *bp;
742	char *cp;
743	int len, num;
744	char	line[MAXLINE];
745
746	for (;;) {
747		if (interactive) {
748			if ((bp = el_gets(el, &num)) == NULL || num == 0)
749				exit(0);
750			len = MIN(MAXLINE, num);
751			memcpy(line, bp, len);
752			line[len - 1] = '\0';
753			history(hist, hep, H_ENTER, bp);
754		} else {
755			line[0] = 0;
756			if (fgets(line, sizeof line , stdin) == NULL) {
757				if (feof(stdin)) {
758					exit(txrx_error);
759				} else {
760					continue;
761				}
762			}
763		}
764		if ((cp = strchr(line, '\n')))
765			*cp = '\0';
766		if (line[0] == 0)
767			continue;
768		makeargv(line);
769		if (margc == 0)
770			continue;
771		c = getcmd(margv[0]);
772		if (c == (struct cmd *)-1) {
773			printf("?Ambiguous command\n");
774			continue;
775		}
776		if (c == NULL) {
777			printf("?Invalid command\n");
778			continue;
779		}
780		(*c->handler)(margc, margv);
781	}
782}
783
784static struct cmd *
785getcmd(char *name)
786{
787	const char *p, *q;
788	struct cmd *c, *found;
789	int nmatches, longest;
790
791	longest = 0;
792	nmatches = 0;
793	found = 0;
794	for (c = cmdtab; (p = c->name) != NULL; c++) {
795		for (q = name; *q == *p++; q++)
796			if (*q == 0)		/* exact match? */
797				return (c);
798		if (!*q) {			/* the name was a prefix */
799			if (q - name > longest) {
800				longest = q - name;
801				nmatches = 1;
802				found = c;
803			} else if (q - name == longest)
804				nmatches++;
805		}
806	}
807	if (nmatches > 1)
808		return ((struct cmd *)-1);
809	return (found);
810}
811
812/*
813 * Slice a string up into argc/argv.
814 */
815static void
816makeargv(char *line)
817{
818	char *cp;
819	char **argp = margv;
820
821	margc = 0;
822	if ((cp = strchr(line, '\n')) != NULL)
823		*cp = '\0';
824	for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) {
825		while (isspace(*cp))
826			cp++;
827		if (*cp == '\0')
828			break;
829		*argp++ = cp;
830		margc += 1;
831		while (*cp != '\0' && !isspace(*cp))
832			cp++;
833		if (*cp == '\0')
834			break;
835		*cp++ = '\0';
836	}
837	*argp++ = 0;
838}
839
840static void
841quit(int argc __unused, char *argv[] __unused)
842{
843
844	exit(txrx_error);
845}
846
847/*
848 * Help command.
849 */
850static void
851help(int argc, char *argv[])
852{
853	struct cmd *c;
854
855	if (argc == 1) {
856		printf("Commands may be abbreviated.  Commands are:\n\n");
857		for (c = cmdtab; c->name; c++)
858			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
859
860		printf("\n[-] : You shouldn't use these ones anymore.\n");
861		printf("[*] : RFC2347 options support required.\n");
862		printf("[**] : Non-standard RFC2347 option.\n");
863		return;
864	}
865	while (--argc > 0) {
866		char *arg;
867		arg = *++argv;
868		c = getcmd(arg);
869		if (c == (struct cmd *)-1)
870			printf("?Ambiguous help command: %s\n", arg);
871		else if (c == (struct cmd *)0)
872			printf("?Invalid help command: %s\n", arg);
873		else
874			printf("%s\n", c->help);
875	}
876}
877
878static void
879setverbose(int argc __unused, char *argv[] __unused)
880{
881
882	verbose = !verbose;
883	printf("Verbose mode %s.\n", verbose ? "on" : "off");
884}
885
886static void
887setoptions(int argc, char *argv[])
888{
889
890	if (argc == 2) {
891		if (strcasecmp(argv[1], "enable") == 0 ||
892		    strcasecmp(argv[1], "on") == 0) {
893			options_extra_enabled = 1;
894			options_rfc_enabled = 1;
895		}
896		if (strcasecmp(argv[1], "disable") == 0 ||
897		    strcasecmp(argv[1], "off") == 0) {
898			options_extra_enabled = 0;
899			options_rfc_enabled = 0;
900		}
901		if (strcasecmp(argv[1], "extra") == 0)
902			options_extra_enabled = !options_extra_enabled;
903	}
904	printf("Support for RFC2347 style options are now %s.\n",
905	    options_rfc_enabled ? "enabled" : "disabled");
906	printf("Support for non-RFC defined options are now %s.\n",
907	    options_extra_enabled ? "enabled" : "disabled");
908
909	printf("\nThe following options are available:\n"
910	    "\toptions on	: enable support for RFC2347 style options\n"
911	    "\toptions off	: disable support for RFC2347 style options\n"
912	    "\toptions extra	: toggle support for non-RFC defined options\n"
913	);
914}
915
916static void
917setrollover(int argc, char *argv[])
918{
919
920	if (argc == 2) {
921		if (strcasecmp(argv[1], "never") == 0 ||
922		    strcasecmp(argv[1], "none") == 0) {
923			free(options[OPT_ROLLOVER].o_request);
924			options[OPT_ROLLOVER].o_request = NULL;
925		}
926		if (strcasecmp(argv[1], "1") == 0) {
927			free(options[OPT_ROLLOVER].o_request);
928			options[OPT_ROLLOVER].o_request = strdup("1");
929		}
930		if (strcasecmp(argv[1], "0") == 0) {
931			free(options[OPT_ROLLOVER].o_request);
932			options[OPT_ROLLOVER].o_request = strdup("0");
933		}
934	}
935	printf("Support for the rollover options is %s.\n",
936	    options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled");
937	if (options[OPT_ROLLOVER].o_request != NULL)
938		printf("Block rollover will be to block %s.\n",
939		    options[OPT_ROLLOVER].o_request);
940
941
942	printf("\nThe following rollover options are available:\n"
943	    "\trollover 0	: rollover to block zero (default)\n"
944	    "\trollover 1	: rollover to block one\n"
945	    "\trollover never	: do not support the rollover option\n"
946	    "\trollover none	: do not support the rollover option\n"
947	);
948}
949
950static void
951setdebug(int argc, char *argv[])
952{
953	int i;
954
955	if (argc != 1) {
956		i = 1;
957		while (i < argc)
958			debug ^= debug_find(argv[i++]);
959	}
960	printf("The following debugging is enabled: %s\n", debug_show(debug));
961
962	printf("\nThe following debugs are available:\n");
963	i = 0;
964	while (debugs[i].name != NULL) {
965		printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc);
966		i++;
967	}
968}
969
970static void
971setblocksize(int argc, char *argv[])
972{
973
974	if (!options_rfc_enabled)
975		printf("RFC2347 style options are not enabled "
976		    "(but proceeding anyway)\n");
977
978	if (argc != 1) {
979		int size = atoi(argv[1]);
980		size_t max;
981		u_long maxdgram;
982
983		max = sizeof(maxdgram);
984		if (sysctlbyname("net.inet.udp.maxdgram",
985			&maxdgram, &max, NULL, 0) < 0) {
986			perror("sysctl: net.inet.udp.maxdgram");
987			return;
988		}
989
990		if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
991			printf("Blocksize should be between %d and %d bytes.\n",
992				BLKSIZE_MIN, BLKSIZE_MAX);
993			return;
994		} else if (size > (int)maxdgram - 4) {
995			printf("Blocksize can't be bigger than %ld bytes due "
996			    "to the net.inet.udp.maxdgram sysctl limitation.\n",
997			    maxdgram - 4);
998			asprintf(&options[OPT_BLKSIZE].o_request,
999			    "%ld", maxdgram - 4);
1000		} else {
1001			asprintf(&options[OPT_BLKSIZE].o_request, "%d", size);
1002		}
1003	}
1004	printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request);
1005}
1006
1007static void
1008setblocksize2(int argc, char *argv[])
1009{
1010
1011	if (!options_rfc_enabled || !options_extra_enabled)
1012		printf(
1013		    "RFC2347 style or non-RFC defined options are not enabled "
1014		    "(but proceeding anyway)\n");
1015
1016	if (argc != 1) {
1017		int size = atoi(argv[1]);
1018		int i;
1019		size_t max;
1020		u_long maxdgram;
1021
1022		int sizes[] = {
1023			8, 16, 32, 64, 128, 256, 512, 1024,
1024			2048, 4096, 8192, 16384, 32768, 0
1025		};
1026
1027		max = sizeof(maxdgram);
1028		if (sysctlbyname("net.inet.udp.maxdgram",
1029			&maxdgram, &max, NULL, 0) < 0) {
1030			perror("sysctl: net.inet.udp.maxdgram");
1031			return;
1032		}
1033
1034		for (i = 0; sizes[i] != 0; i++) {
1035			if (sizes[i] == size) break;
1036		}
1037		if (sizes[i] == 0) {
1038			printf("Blocksize2 should be a power of two between "
1039			    "8 and 32768.\n");
1040			return;
1041		}
1042
1043		if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
1044			printf("Blocksize2 should be between "
1045			    "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX);
1046			return;
1047		} else if (size > (int)maxdgram - 4) {
1048			printf("Blocksize2 can't be bigger than %ld bytes due "
1049			    "to the net.inet.udp.maxdgram sysctl limitation.\n",
1050			    maxdgram - 4);
1051			for (i = 0; sizes[i+1] != 0; i++) {
1052				if ((int)maxdgram < sizes[i+1]) break;
1053			}
1054			asprintf(&options[OPT_BLKSIZE2].o_request,
1055			    "%d", sizes[i]);
1056		} else {
1057			asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size);
1058		}
1059	}
1060	printf("Blocksize2 is now %s bytes.\n",
1061	    options[OPT_BLKSIZE2].o_request);
1062}
1063
1064static void
1065setpacketdrop(int argc, char *argv[])
1066{
1067
1068	if (argc != 1)
1069		packetdroppercentage = atoi(argv[1]);
1070
1071	printf("Randomly %d in 100 packets will be dropped\n",
1072	    packetdroppercentage);
1073}
1074
1075static void
1076setwindowsize(int argc, char *argv[])
1077{
1078
1079	if (!options_rfc_enabled)
1080		printf("RFC2347 style options are not enabled "
1081		    "(but proceeding anyway)\n");
1082
1083	if (argc != 1) {
1084		int size = atoi(argv[1]);
1085
1086		if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) {
1087			printf("Windowsize should be between %d and %d "
1088			    "blocks.\n", WINDOWSIZE_MIN, WINDOWSIZE_MAX);
1089			return;
1090		} else {
1091			asprintf(&options[OPT_WINDOWSIZE].o_request, "%d",
1092			    size);
1093		}
1094	}
1095	printf("Windowsize is now %s blocks.\n",
1096	    options[OPT_WINDOWSIZE].o_request);
1097}
1098