1158115Sume/*-
2158115Sume * SPDX-License-Identifier: BSD-3-Clause
3158115Sume *
4158115Sume * Copyright (c) 1983, 1993
5158115Sume *	The Regents of the University of California.  All rights reserved.
6158115Sume *
7158115Sume * Redistribution and use in source and binary forms, with or without
8158115Sume * modification, are permitted provided that the following conditions
9158115Sume * are met:
10158115Sume * 1. Redistributions of source code must retain the above copyright
11158115Sume *    notice, this list of conditions and the following disclaimer.
12158115Sume * 2. Redistributions in binary form must reproduce the above copyright
13158115Sume *    notice, this list of conditions and the following disclaimer in the
14158115Sume *    documentation and/or other materials provided with the distribution.
15158115Sume * 3. Neither the name of the University nor the names of its contributors
16158115Sume *    may be used to endorse or promote products derived from this software
17158115Sume *    without specific prior written permission.
18158115Sume *
19158115Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20158115Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21158115Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22158115Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23158115Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24158115Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25158115Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26158115Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27158115Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28158115Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29158115Sume * SUCH DAMAGE.
30158115Sume */
31194089Sdes
32158115Sume/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
33158115Sume
34194089Sdes/*
35158115Sume * TFTP User Program -- Command Interface.
36158115Sume */
37194089Sdes#include <sys/param.h>
38158115Sume#include <sys/file.h>
39158115Sume#include <sys/socket.h>
40158115Sume#include <sys/stat.h>
41158115Sume#include <sys/sysctl.h>
42158115Sume
43158115Sume#include <netinet/in.h>
44158115Sume#include <arpa/inet.h>
45158115Sume#include <arpa/tftp.h>
46158115Sume
47158115Sume#include <ctype.h>
48158115Sume#include <err.h>
49158115Sume#include <histedit.h>
50158115Sume#include <netdb.h>
51158115Sume#include <setjmp.h>
52158115Sume#include <signal.h>
53158115Sume#include <stdbool.h>
54158115Sume#include <stddef.h>
55158115Sume#include <stdio.h>
56158115Sume#include <stdlib.h>
57171795Sbushman#include <string.h>
58158115Sume#include <unistd.h>
59158115Sume
60158115Sume#include "tftp-utils.h"
61158115Sume#include "tftp-io.h"
62158115Sume#include "tftp-options.h"
63171795Sbushman#include "tftp.h"
64158115Sume
65171795Sbushman#define	MAXLINE		(2 * MAXPATHLEN)
66158115Sume#define	TIMEOUT		5		/* secs between rexmt's */
67158115Sume
68158115Sumetypedef struct	sockaddr_storage peeraddr;
69158115Sumestatic int	connected;
70158115Sumestatic char	mode[32];
71158115Sumestatic jmp_buf	toplevel;
72158115Sumestatic int	txrx_error;
73158115Sumestatic int	peer;
74158115Sume
75158115Sume#define	MAX_MARGV	20
76158115Sumestatic int	margc;
77158115Sumestatic char	*margv[MAX_MARGV];
78158115Sume
79158115Sumeint		verbose;
80158115Sumestatic char	*port = NULL;
81158115Sume
82158115Sumestatic void	get(int, char **);
83158115Sumestatic void	help(int, char **);
84158115Sumestatic void	intr(int);
85158115Sumestatic void	modecmd(int, char **);
86158115Sumestatic void	put(int, char **);
87158115Sumestatic void	quit(int, char **);
88158115Sumestatic void	setascii(int, char **);
89158115Sumestatic void	setbinary(int, char **);
90158115Sumestatic void	setpeer0(char *, const char *);
91158115Sumestatic void	setpeer(int, char **);
92158115Sumestatic void	settimeoutpacket(int, char **);
93158115Sumestatic void	settimeoutnetwork(int, char **);
94158115Sumestatic void	setdebug(int, char **);
95162891Srustatic void	setverbose(int, char **);
96171795Sbushmanstatic void	showstatus(int, char **);
97158115Sumestatic void	setblocksize(int, char **);
98158115Sumestatic void	setblocksize2(int, char **);
99158115Sumestatic void	setoptions(int, char **);
100158115Sumestatic void	setrollover(int, char **);
101158115Sumestatic void	setpacketdrop(int, char **);
102158115Sumestatic void	setwindowsize(int, char **);
103158115Sume
104158115Sumestatic void command(bool, EditLine *, History *, HistEvent *) __dead2;
105158115Sumestatic const char *command_prompt(void);
106158115Sume
107158115Sumestatic void urihandling(char *URI);
108158115Sumestatic void getusage(char *);
109158115Sumestatic void makeargv(char *line);
110158115Sumestatic void putusage(char *);
111158115Sumestatic void settftpmode(const char *);
112158115Sume
113158115Sumestatic char	*tail(char *);
114158115Sumestatic const struct cmd *getcmd(const char *);
115158115Sume
116158115Sume#define HELPINDENT (sizeof("connect"))
117158115Sume
118158115Sumestruct cmd {
119158115Sume	const char	*name;
120158115Sume	void	(*handler)(int, char **);
121158115Sume	const char	*help;
122158115Sume};
123158115Sume
124158115Sumestatic struct cmd cmdtab[] = {
125158115Sume	{ "connect",	setpeer,	"connect to remote tftp"	},
126194097Sdes	{ "mode",	modecmd,	"set file transfer mode"	},
127158115Sume	{ "put",	put,		"send file"			},
128158115Sume	{ "get",	get,		"receive file"			},
129158115Sume	{ "quit",	quit,		"exit tftp"			},
130158115Sume	{ "verbose",	setverbose,	"toggle verbose mode"		},
131158115Sume	{ "status",	showstatus,	"show current status"		},
132158115Sume	{ "binary",     setbinary,	"set mode to octet"		},
133194097Sdes	{ "ascii",      setascii,	"set mode to netascii"		},
134158115Sume	{ "rexmt",	settimeoutpacket,
135158115Sume	  "set per-packet retransmission timeout[-]" },
136158115Sume	{ "timeout",	settimeoutnetwork,
137158115Sume	  "set total retransmission timeout" },
138158115Sume	{ "trace",	setdebug,	"enable 'debug packet'[-]"	},
139158115Sume	{ "debug",	setdebug,	"enable verbose output"		},
140158115Sume	{ "blocksize",	setblocksize,	"set blocksize[*]"		},
141158115Sume	{ "blocksize2",	setblocksize2,	"set blocksize as a power of 2[**]" },
142158115Sume	{ "rollover",	setrollover,	"rollover after 64K packets[**]" },
143158115Sume	{ "options",	setoptions,
144158115Sume	  "enable or disable RFC2347 style options" },
145158115Sume	{ "help",	help,		"print help information"	},
146158115Sume	{ "packetdrop",	setpacketdrop,	"artificial packetloss feature"	},
147158115Sume	{ "windowsize",	setwindowsize,	"set windowsize[*]"		},
148158115Sume	{ "?",		help,		"print help information"	},
149158115Sume	{ NULL,		NULL,		NULL				}
150158115Sume};
151158115Sume
152158115Sumestatic struct	modes {
153158115Sume	const char *m_name;
154158115Sume	const char *m_mode;
155158115Sume} modes[] = {
156158115Sume	{ "ascii",	"netascii" },
157158115Sume	{ "netascii",	"netascii" },
158158115Sume	{ "binary",	"octet" },
159158115Sume	{ "image",	"octet" },
160158115Sume	{ "octet",	"octet" },
161158115Sume	{ NULL,		NULL }
162158115Sume};
163158115Sume
164158115Sumeint
165158115Sumemain(int argc, char *argv[])
166158115Sume{
167194104Sdes	HistEvent he;
168158115Sume	static EditLine *el;
169158115Sume	static History *hist;
170158115Sume	bool interactive;
171158115Sume
172158115Sume	acting_as_client = 1;
173158115Sume	peer = -1;
174158115Sume	strcpy(mode, "octet");
175158115Sume	signal(SIGINT, intr);
176158115Sume
177184187Sdelphij	interactive = isatty(STDIN_FILENO);
178158115Sume	if (interactive) {
179158115Sume		el = el_init("tftp", stdin, stdout, stderr);
180158115Sume		hist = history_init();
181158115Sume		history(hist, &he, H_SETSIZE, 100);
182158115Sume		el_set(el, EL_HIST, history, hist);
183158115Sume		el_set(el, EL_EDITOR, "emacs");
184158115Sume		el_set(el, EL_PROMPT, command_prompt);
185158115Sume		el_set(el, EL_SIGNAL, 1);
186158115Sume		el_source(el, NULL);
187158115Sume	}
188158115Sume
189158115Sume	if (argc > 1) {
190158115Sume		if (setjmp(toplevel) != 0)
191158115Sume			exit(txrx_error);
192158115Sume
193158115Sume		if (strncmp(argv[1], "tftp://", 7) == 0) {
194158115Sume			urihandling(argv[1]);
195158115Sume			exit(txrx_error);
196158115Sume		}
197158115Sume
198158115Sume		setpeer(argc, argv);
199158115Sume	}
200158115Sume
201158115Sume	if (setjmp(toplevel) != 0) {
202158115Sume		if (interactive)
203158115Sume			el_reset(el);
204158115Sume		(void)putchar('\n');
205158115Sume	}
206158115Sume
207158115Sume	init_options();
208158115Sume	command(interactive, el, hist, &he);
209158115Sume}
210158115Sume
211158115Sume/*
212158115Sume * RFC3617 handling of TFTP URIs:
213158115Sume *
214158115Sume *    tftpURI         = "tftp://" host "/" file [ mode ]
215158115Sume *    mode            = ";"  "mode=" ( "netascii" / "octet" )
216158115Sume *    file            = *( unreserved / escaped )
217158115Sume *    host            = <as specified by RFC 2732>
218158115Sume *    unreserved      = <as specified in RFC 2396>
219158115Sume *    escaped         = <as specified in RFC 2396>
220158115Sume *
221158115Sume * We are cheating a little bit by allowing any mode as specified in the
222158115Sume * modes table defined earlier on in this file and mapping it on the real
223158115Sume * mode.
224158115Sume */
225158115Sumestatic void
226158115Sumeurihandling(char *URI)
227158115Sume{
228158115Sume	char	uri[ARG_MAX];
229158115Sume	char	*host = NULL;
230158115Sume	char	*path = NULL;
231158115Sume	char	*opts = NULL;
232158115Sume	const char *tmode = "octet";
233158115Sume	char	*s;
234158115Sume	char	line[MAXLINE];
235158115Sume	int	i;
236158115Sume
237158115Sume	strlcpy(uri, URI, ARG_MAX);
238158115Sume	host = uri + 7;
239158115Sume
240158115Sume	if ((s = strchr(host, '/')) == NULL) {
241158115Sume		fprintf(stderr,
242158115Sume		    "Invalid URI: Couldn't find / after hostname\n");
243158115Sume		exit(1);
244158115Sume	}
245158115Sume	*s = '\0';
246158115Sume	path = s + 1;
247158115Sume
248158115Sume	if ((s = strchr(path, ';')) != NULL) {
249158115Sume		*s = '\0';
250158115Sume		opts = s + 1;
251158115Sume
252158115Sume		if (strncmp(opts, "mode=", 5) == 0) {
253158115Sume			tmode = opts;
254158115Sume			tmode += 5;
255158115Sume
256158115Sume			for (i = 0; modes[i].m_name != NULL; i++) {
257158115Sume				if (strcmp(modes[i].m_name, tmode) == 0)
258158115Sume					break;
259158115Sume			}
260158115Sume			if (modes[i].m_name == NULL) {
261158115Sume				fprintf(stderr, "Invalid mode: '%s'\n", mode);
262158115Sume				exit(1);
263158115Sume			}
264158115Sume			settftpmode(modes[i].m_mode);
265158115Sume		}
266158115Sume	} else {
267158115Sume		settftpmode("octet");
268158115Sume	}
269158115Sume
270158115Sume	setpeer0(host, NULL);
271158115Sume
272158115Sume	sprintf(line, "get %s", path);
273158115Sume	makeargv(line);
274158115Sume	get(margc, margv);
275158115Sume}
276158115Sume
277158115Sumestatic char    hostname[MAXHOSTNAMELEN];
278158115Sume
279158115Sumestatic void
280158115Sumesetpeer0(char *host, const char *lport)
281158115Sume{
282158115Sume	struct addrinfo hints, *res0, *res;
283158115Sume	int error;
284158115Sume	const char *cause = "unknown";
285158115Sume
286158115Sume	if (connected) {
287158115Sume		close(peer);
288158115Sume		peer = -1;
289158115Sume	}
290158115Sume	connected = 0;
291158115Sume
292158115Sume	memset(&hints, 0, sizeof(hints));
293158115Sume	hints.ai_family = PF_UNSPEC;
294158115Sume	hints.ai_socktype = SOCK_DGRAM;
295158115Sume	hints.ai_protocol = IPPROTO_UDP;
296158115Sume	hints.ai_flags = AI_CANONNAME;
297158115Sume	if (!lport)
298158115Sume		lport = "tftp";
299158115Sume	error = getaddrinfo(host, lport, &hints, &res0);
300158115Sume	if (error) {
301158115Sume		warnx("%s", gai_strerror(error));
302158115Sume		return;
303158115Sume	}
304158115Sume
305158115Sume	for (res = res0; res; res = res->ai_next) {
306158115Sume		if (res->ai_addrlen > sizeof(peeraddr))
307158115Sume			continue;
308158115Sume		peer = socket(res->ai_family, res->ai_socktype,
309158115Sume			res->ai_protocol);
310158115Sume		if (peer < 0) {
311158115Sume			cause = "socket";
312158115Sume			continue;
313158115Sume		}
314158115Sume
315158115Sume		memset(&peer_sock, 0, sizeof(peer_sock));
316158115Sume		peer_sock.ss_family = res->ai_family;
317158115Sume		peer_sock.ss_len = res->ai_addrlen;
318158115Sume		if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) {
319194096Sdes			cause = "bind";
320158115Sume			close(peer);
321194096Sdes			peer = -1;
322158115Sume			continue;
323158115Sume		}
324158115Sume
325158115Sume		break;
326158115Sume	}
327158115Sume
328158115Sume	if (peer < 0)
329158115Sume		warn("%s", cause);
330158115Sume	else {
331158115Sume		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
332158115Sume		memcpy(&peer_sock, res->ai_addr, res->ai_addrlen);
333158115Sume		if (res->ai_canonname) {
334158115Sume			(void) strlcpy(hostname, res->ai_canonname,
335158115Sume				sizeof(hostname));
336158115Sume		} else
337158115Sume			(void) strlcpy(hostname, host, sizeof(hostname));
338158115Sume		connected = 1;
339158115Sume	}
340158115Sume
341158115Sume	freeaddrinfo(res0);
342158115Sume}
343158115Sume
344158115Sumestatic void
345158115Sumesetpeer(int argc, char *argv[])
346158115Sume{
347158115Sume	char	line[MAXLINE];
348158115Sume
349158115Sume	if (argc < 2) {
350158115Sume		strcpy(line, "Connect ");
351158115Sume		printf("(to) ");
352158115Sume		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
353158115Sume		makeargv(line);
354158115Sume		argc = margc;
355158115Sume		argv = margv;
356158115Sume	}
357158115Sume	if ((argc < 2) || (argc > 3)) {
358158115Sume		printf("usage: %s [host [port]]\n", argv[0]);
359158115Sume		return;
360158115Sume	}
361158115Sume	if (argc == 3) {
362158115Sume		port = argv[2];
363158115Sume		setpeer0(argv[1], argv[2]);
364158115Sume	} else
365158115Sume		setpeer0(argv[1], NULL);
366158115Sume}
367158115Sume
368158115Sumestatic void
369158115Sumemodecmd(int argc, char *argv[])
370158115Sume{
371158115Sume	struct modes *p;
372158115Sume	const char *sep;
373158115Sume
374158115Sume	if (argc < 2) {
375158115Sume		printf("Using %s mode to transfer files.\n", mode);
376158115Sume		return;
377158115Sume	}
378158115Sume	if (argc == 2) {
379158115Sume		for (p = modes; p->m_name; p++)
380158115Sume			if (strcmp(argv[1], p->m_name) == 0)
381158115Sume				break;
382158115Sume		if (p->m_name) {
383158115Sume			settftpmode(p->m_mode);
384158115Sume			return;
385158115Sume		}
386158115Sume		printf("%s: unknown mode\n", argv[1]);
387158115Sume		/* drop through and print usage message */
388158115Sume	}
389158115Sume
390158115Sume	printf("usage: %s [", argv[0]);
391158115Sume	sep = " ";
392158115Sume	for (p = modes; p->m_name != NULL; p++) {
393158115Sume		printf("%s%s", sep, p->m_name);
394158115Sume		if (*sep == ' ')
395158115Sume			sep = " | ";
396158115Sume	}
397158115Sume	printf(" ]\n");
398158115Sume	return;
399158115Sume}
400158115Sume
401158115Sumestatic void
402158115Sumesetbinary(int argc __unused, char *argv[] __unused)
403158115Sume{
404158115Sume
405158115Sume	settftpmode("octet");
406158115Sume}
407158115Sume
408158115Sumestatic void
409158115Sumesetascii(int argc __unused, char *argv[] __unused)
410158115Sume{
411194104Sdes
412158115Sume	settftpmode("netascii");
413158115Sume}
414158115Sume
415158115Sumestatic void
416158115Sumesettftpmode(const char *newmode)
417158115Sume{
418158115Sume
419158115Sume	strlcpy(mode, newmode, sizeof(mode));
420158115Sume	if (verbose)
421158115Sume		printf("mode set to %s\n", mode);
422158115Sume}
423158115Sume
424158115Sume
425158115Sume/*
426158115Sume * Send file(s).
427158115Sume */
428158115Sumestatic void
429158115Sumeput(int argc, char *argv[])
430158115Sume{
431158115Sume	int	fd;
432158115Sume	int	n;
433158115Sume	char	*cp, *targ, *path;
434158115Sume	char	line[MAXLINE];
435158115Sume	struct stat sb;
436158115Sume
437158115Sume	if (argc < 2) {
438158115Sume		strcpy(line, "send ");
439158115Sume		printf("(file) ");
440158115Sume		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
441158115Sume		makeargv(line);
442158115Sume		argc = margc;
443158115Sume		argv = margv;
444158115Sume	}
445158115Sume	if (argc < 2) {
446158115Sume		putusage(argv[0]);
447158115Sume		return;
448158115Sume	}
449158115Sume	targ = argv[argc - 1];
450158115Sume	if (strrchr(argv[argc - 1], ':')) {
451158115Sume		char *lcp;
452158115Sume
453158115Sume		for (n = 1; n < argc - 1; n++)
454158115Sume			if (strchr(argv[n], ':')) {
455158115Sume				putusage(argv[0]);
456158115Sume				return;
457158115Sume			}
458158115Sume		lcp = argv[argc - 1];
459158115Sume		targ = strrchr(lcp, ':');
460158115Sume		*targ++ = 0;
461158115Sume		if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
462158115Sume			lcp[strlen(lcp) - 1] = '\0';
463158115Sume			lcp++;
464158115Sume		}
465158115Sume		setpeer0(lcp, NULL);
466158115Sume	}
467158115Sume	if (!connected) {
468158115Sume		printf("No target machine specified.\n");
469158115Sume		return;
470158115Sume	}
471158115Sume	if (argc < 4) {
472158115Sume		cp = argc == 2 ? tail(targ) : argv[1];
473158115Sume		fd = open(cp, O_RDONLY);
474158115Sume		if (fd < 0) {
475158115Sume			warn("%s", cp);
476158115Sume			return;
477158115Sume		}
478158115Sume
479158115Sume		if (fstat(fd, &sb) < 0) {
480158115Sume			warn("%s", cp);
481158115Sume			close(fd);
482158115Sume			return;
483158115Sume		}
484158115Sume		options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size);
485158115Sume
486158115Sume		if (verbose)
487158115Sume			printf("putting %s to %s:%s [%s]\n",
488158115Sume			    cp, hostname, targ, mode);
489158115Sume		if (xmitfile(peer, port, fd, targ, mode))
490158115Sume			txrx_error = 1;
491158115Sume		close(fd);
492158115Sume		return;
493158115Sume	}
494158115Sume				/* this assumes the target is a directory */
495158115Sume				/* on a remote unix system.  hmmmm.  */
496158115Sume	for (n = 1; n < argc - 1; n++) {
497158115Sume		if (asprintf(&path, "%s/%s", targ, tail(argv[n])) < 0)
498158115Sume			err(1, "malloc");
499158115Sume
500158115Sume		fd = open(argv[n], O_RDONLY);
501158115Sume		if (fd < 0) {
502158115Sume			warn("%s", argv[n]);
503194096Sdes			free(path);
504158115Sume			continue;
505158115Sume		}
506158115Sume
507158115Sume		if (fstat(fd, &sb) < 0) {
508158115Sume			warn("%s", argv[n]);
509158115Sume			close(fd);
510158115Sume			free(path);
511158115Sume			continue;
512158115Sume		}
513158115Sume		options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size);
514158115Sume
515158115Sume		if (verbose)
516158115Sume			printf("putting %s to %s:%s [%s]\n",
517158115Sume			    argv[n], hostname, path, mode);
518158115Sume		if (xmitfile(peer, port, fd, path, mode) != 0)
519158115Sume			txrx_error = 1;
520158115Sume		close(fd);
521158115Sume
522158115Sume		free(path);
523158115Sume	}
524158115Sume}
525158115Sume
526158115Sumestatic void
527158115Sumeputusage(char *s)
528158115Sume{
529158115Sume
530158115Sume	printf("usage: %s file [remotename]\n", s);
531158115Sume	printf("       %s file host:remotename\n", s);
532158115Sume	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
533158115Sume}
534158115Sume
535158115Sume/*
536158115Sume * Receive file(s).
537158115Sume */
538158115Sumestatic void
539158115Sumeget(int argc, char *argv[])
540158115Sume{
541158115Sume	int fd;
542158115Sume	int n;
543158115Sume	char *cp;
544158115Sume	char *src;
545158115Sume	char	line[MAXLINE];
546158115Sume
547158115Sume	if (argc < 2) {
548158115Sume		strcpy(line, "get ");
549158115Sume		printf("(files) ");
550158115Sume		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
551158115Sume		makeargv(line);
552158115Sume		argc = margc;
553158115Sume		argv = margv;
554158115Sume	}
555158115Sume	if (argc < 2) {
556158115Sume		getusage(argv[0]);
557158115Sume		return;
558158115Sume	}
559158115Sume	if (!connected) {
560158115Sume		for (n = 1; n < argc ; n++)
561158115Sume			if (strrchr(argv[n], ':') == 0) {
562158115Sume				printf("No remote host specified and "
563158115Sume				    "no host given for file '%s'\n", argv[n]);
564158115Sume				getusage(argv[0]);
565158115Sume				return;
566158115Sume			}
567158115Sume	}
568158115Sume	for (n = 1; n < argc ; n++) {
569158115Sume		src = strrchr(argv[n], ':');
570158115Sume		if (src == NULL)
571158115Sume			src = argv[n];
572158115Sume		else {
573158115Sume			char *lcp;
574158115Sume
575158115Sume			*src++ = 0;
576158115Sume			lcp = argv[n];
577194086Sdes			if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
578194086Sdes				lcp[strlen(lcp) - 1] = '\0';
579194086Sdes				lcp++;
580194086Sdes			}
581194086Sdes			setpeer0(lcp, NULL);
582194086Sdes			if (!connected)
583194086Sdes				continue;
584194086Sdes		}
585194086Sdes		if (argc < 4) {
586158115Sume			cp = argc == 3 ? argv[2] : tail(src);
587194086Sdes			fd = creat(cp, 0644);
588158115Sume			if (fd < 0) {
589158115Sume				warn("%s", cp);
590158115Sume				return;
591158115Sume			}
592158115Sume			if (verbose)
593158115Sume				printf("getting from %s:%s to %s [%s]\n",
594158115Sume				    hostname, src, cp, mode);
595158115Sume			if (recvfile(peer, port, fd, src, mode) != 0)
596158115Sume				txrx_error = 1;
597158115Sume			break;
598158115Sume		}
599158115Sume		cp = tail(src);         /* new .. jdg */
600158115Sume		fd = creat(cp, 0644);
601158115Sume		if (fd < 0) {
602158115Sume			warn("%s", cp);
603158115Sume			continue;
604158115Sume		}
605158115Sume		if (verbose)
606158115Sume			printf("getting from %s:%s to %s [%s]\n",
607158115Sume			    hostname, src, cp, mode);
608158115Sume		if (recvfile(peer, port, fd, src, mode) != 0)
609158115Sume			txrx_error = 1;
610158115Sume	}
611158115Sume}
612158115Sume
613158115Sumestatic void
614158115Sumegetusage(char *s)
615158115Sume{
616158115Sume
617158115Sume	printf("usage: %s file [localname]\n", s);
618158115Sume	printf("       %s [host:]file [localname]\n", s);
619158115Sume	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
620158115Sume}
621158115Sume
622158115Sumestatic void
623158115Sumesettimeoutpacket(int argc, char *argv[])
624158115Sume{
625158115Sume	int t;
626158115Sume	char	line[MAXLINE];
627158115Sume
628158115Sume	if (argc < 2) {
629158115Sume		strcpy(line, "Packet timeout ");
630158115Sume		printf("(value) ");
631158115Sume		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
632158115Sume		makeargv(line);
633158115Sume		argc = margc;
634158115Sume		argv = margv;
635158115Sume	}
636158115Sume	if (argc != 2) {
637158115Sume		printf("usage: %s value\n", argv[0]);
638158115Sume		return;
639158115Sume	}
640158115Sume	t = atoi(argv[1]);
641158115Sume	if (t < 0) {
642158115Sume		printf("%s: bad value\n", argv[1]);
643158115Sume		return;
644158115Sume	}
645158115Sume
646158115Sume	settimeouts(t, timeoutnetwork, maxtimeouts);
647158115Sume}
648158115Sume
649158115Sumestatic void
650158115Sumesettimeoutnetwork(int argc, char *argv[])
651158115Sume{
652158115Sume	int t;
653158115Sume	char	line[MAXLINE];
654158115Sume
655158115Sume	if (argc < 2) {
656158115Sume		strcpy(line, "Network timeout ");
657158115Sume		printf("(value) ");
658158115Sume		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
659158115Sume		makeargv(line);
660158115Sume		argc = margc;
661158115Sume		argv = margv;
662158115Sume	}
663158115Sume	if (argc != 2) {
664158115Sume		printf("usage: %s value\n", argv[0]);
665158115Sume		return;
666158115Sume	}
667158115Sume	t = atoi(argv[1]);
668158115Sume	if (t < 0) {
669158115Sume		printf("%s: bad value\n", argv[1]);
670158115Sume		return;
671158115Sume	}
672158115Sume
673171795Sbushman	settimeouts(timeoutpacket, t, maxtimeouts);
674171795Sbushman}
675158115Sume
676158115Sumestatic void
677158115Sumeshowstatus(int argc __unused, char *argv[] __unused)
678158115Sume{
679158115Sume
680158115Sume	printf("Remote host: %s\n",
681158115Sume	    connected ? hostname : "none specified yet");
682158115Sume	printf("RFC2347 Options support: %s\n",
683158115Sume	    options_rfc_enabled ? "enabled" : "disabled");
684158115Sume	printf("Non-RFC defined options support: %s\n",
685158115Sume	    options_extra_enabled ? "enabled" : "disabled");
686158115Sume	printf("Mode: %s\n", mode);
687158115Sume	printf("Verbose: %s\n", verbose ? "on" : "off");
688158115Sume	printf("Debug: %s\n", debug_show(debug));
689158115Sume	printf("Artificial packetloss: %d in 100 packets\n",
690158115Sume	    packetdroppercentage);
691158115Sume	printf("Segment size: %d bytes\n", segsize);
692158115Sume	printf("Network timeout: %d seconds\n", timeoutpacket);
693158115Sume	printf("Maximum network timeout: %d seconds\n", timeoutnetwork);
694171795Sbushman	printf("Maximum timeouts: %d \n", maxtimeouts);
695158115Sume}
696171795Sbushman
697171795Sbushmanstatic void
698158115Sumeintr(int dummy __unused)
699158115Sume{
700158115Sume
701171795Sbushman	signal(SIGALRM, SIG_IGN);
702158115Sume	alarm(0);
703158115Sume	longjmp(toplevel, -1);
704158115Sume}
705158115Sume
706158115Sumestatic char *
707158115Sumetail(char *filename)
708158115Sume{
709158115Sume	char *s;
710158115Sume
711158115Sume	while (*filename) {
712158115Sume		s = strrchr(filename, '/');
713158115Sume		if (s == NULL)
714158115Sume			break;
715158115Sume		if (s[1])
716158115Sume			return (s + 1);
717171795Sbushman		*s = '\0';
718158115Sume	}
719158115Sume	return (filename);
720158115Sume}
721158115Sume
722158115Sumestatic const char *
723158115Sumecommand_prompt(void)
724158115Sume{
725158115Sume
726158115Sume	return ("tftp> ");
727158115Sume}
728158115Sume
729171795Sbushman/*
730158115Sume * Command parser.
731158115Sume */
732158115Sumestatic void
733158115Sumecommand(bool interactive, EditLine *el, History *hist, HistEvent *hep)
734158115Sume{
735158115Sume	const struct cmd *c;
736158115Sume	const char *bp;
737158115Sume	char *cp;
738158115Sume	int len, num;
739158115Sume	char	line[MAXLINE];
740158115Sume
741158115Sume	for (;;) {
742158115Sume		if (interactive) {
743158115Sume			if ((bp = el_gets(el, &num)) == NULL || num == 0)
744158115Sume				exit(0);
745158115Sume			len = MIN(MAXLINE, num);
746158115Sume			memcpy(line, bp, len);
747158115Sume			line[len - 1] = '\0';
748158115Sume			history(hist, hep, H_ENTER, bp);
749158115Sume		} else {
750158115Sume			line[0] = 0;
751158115Sume			if (fgets(line, sizeof line , stdin) == NULL) {
752158115Sume				if (feof(stdin)) {
753158115Sume					exit(txrx_error);
754158115Sume				} else {
755158115Sume					continue;
756158115Sume				}
757158115Sume			}
758158115Sume		}
759158115Sume		if ((cp = strchr(line, '\n')))
760158115Sume			*cp = '\0';
761158115Sume		if (line[0] == 0)
762158115Sume			continue;
763158115Sume		makeargv(line);
764158115Sume		if (margc == 0)
765158115Sume			continue;
766158115Sume		c = getcmd(margv[0]);
767158115Sume		if (c == (struct cmd *)-1) {
768158115Sume			printf("?Ambiguous command\n");
769158115Sume			continue;
770158115Sume		}
771158115Sume		if (c == NULL) {
772158115Sume			printf("?Invalid command\n");
773158115Sume			continue;
774158115Sume		}
775158115Sume		(*c->handler)(margc, margv);
776158115Sume	}
777158115Sume}
778158115Sume
779158115Sumestatic const struct cmd *
780158115Sumegetcmd(const char *name)
781158115Sume{
782158115Sume	const char *p, *q;
783158115Sume	const struct cmd *c, *found;
784158115Sume	ptrdiff_t longest;
785158115Sume	int nmatches;
786158115Sume
787158115Sume	longest = 0;
788158115Sume	nmatches = 0;
789158115Sume	found = 0;
790158115Sume	for (c = cmdtab; (p = c->name) != NULL; c++) {
791158115Sume		for (q = name; *q == *p++; q++)
792158115Sume			if (*q == '\0')		/* exact match? */
793158115Sume				return (c);
794158115Sume		if (*q == '\0') {		/* the name was a prefix */
795158115Sume			if (q - name > longest) {
796158115Sume				longest = q - name;
797158115Sume				nmatches = 1;
798158115Sume				found = c;
799158115Sume			} else if (q - name == longest)
800158115Sume				nmatches++;
801158115Sume		}
802158115Sume	}
803158115Sume	if (nmatches > 1)
804158115Sume		return ((struct cmd *)-1);
805158115Sume	return (found);
806158115Sume}
807158115Sume
808158115Sume/*
809158115Sume * Slice a string up into argc/argv.
810158115Sume */
811158115Sumestatic void
812158115Sumemakeargv(char *line)
813158115Sume{
814158115Sume	char *cp;
815158115Sume	char **argp = margv;
816158115Sume
817158115Sume	margc = 0;
818158115Sume	if ((cp = strchr(line, '\n')) != NULL)
819158115Sume		*cp = '\0';
820158115Sume	for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) {
821158115Sume		while (isspace(*cp))
822158115Sume			cp++;
823158115Sume		if (*cp == '\0')
824158115Sume			break;
825158115Sume		*argp++ = cp;
826158115Sume		margc += 1;
827158115Sume		while (*cp != '\0' && !isspace(*cp))
828158115Sume			cp++;
829158115Sume		if (*cp == '\0')
830158115Sume			break;
831194104Sdes		*cp++ = '\0';
832158115Sume	}
833158115Sume	*argp++ = 0;
834194104Sdes}
835194104Sdes
836158115Sumestatic void
837158115Sumequit(int argc __unused, char *argv[] __unused)
838158115Sume{
839158115Sume
840158115Sume	exit(txrx_error);
841158115Sume}
842158115Sume
843158115Sume/*
844158115Sume * Help command.
845158115Sume */
846158115Sumestatic void
847158115Sumehelp(int argc, char *argv[])
848158115Sume{
849158115Sume	const struct cmd *c;
850158115Sume
851158115Sume	if (argc == 1) {
852158115Sume		printf("Commands may be abbreviated.  Commands are:\n\n");
853158115Sume		for (c = cmdtab; c->name; c++)
854158115Sume			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
855158115Sume
856158115Sume		printf("\n[-] : You shouldn't use these ones anymore.\n");
857158115Sume		printf("[*] : RFC2347 options support required.\n");
858158115Sume		printf("[**] : Non-standard RFC2347 option.\n");
859158115Sume		return;
860158115Sume	}
861158115Sume	while (--argc > 0) {
862158115Sume		char *arg;
863158115Sume		arg = *++argv;
864158115Sume		c = getcmd(arg);
865158115Sume		if (c == (struct cmd *)-1)
866158115Sume			printf("?Ambiguous help command: %s\n", arg);
867158115Sume		else if (c == (struct cmd *)0)
868158115Sume			printf("?Invalid help command: %s\n", arg);
869158115Sume		else
870158115Sume			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