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