11590Srgrimes/*
21590Srgrimes * Copyright (c) 1983, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3128202Scharnierstatic const char copyright[] =
321590Srgrimes"@(#) Copyright (c) 1983, 1993\n\
331590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3487708Smarkm#endif
351590Srgrimes
3691387Sdwmalone#if 0
371590Srgrimes#ifndef lint
3891387Sdwmalonestatic char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
3928202Scharnier#endif
4091387Sdwmalone#endif
411590Srgrimes
4291387Sdwmalone#include <sys/cdefs.h>
4391387Sdwmalone__FBSDID("$FreeBSD: stable/10/usr.bin/tftp/main.c 339061 2018-10-01 16:10:27Z asomers $");
4491387Sdwmalone
451590Srgrimes/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
461590Srgrimes
471590Srgrimes/*
481590Srgrimes * TFTP User Program -- Command Interface.
491590Srgrimes */
5028202Scharnier#include <sys/param.h>
511590Srgrimes#include <sys/types.h>
521590Srgrimes#include <sys/socket.h>
53207607Simp#include <sys/sysctl.h>
541590Srgrimes#include <sys/file.h>
55207607Simp#include <sys/stat.h>
561590Srgrimes
571590Srgrimes#include <netinet/in.h>
581590Srgrimes#include <arpa/inet.h>
59207607Simp#include <arpa/tftp.h>
601590Srgrimes
611590Srgrimes#include <ctype.h>
6228202Scharnier#include <err.h>
6385155Sbde#include <histedit.h>
641590Srgrimes#include <netdb.h>
651590Srgrimes#include <setjmp.h>
661590Srgrimes#include <signal.h>
671590Srgrimes#include <stdio.h>
681590Srgrimes#include <stdlib.h>
691590Srgrimes#include <string.h>
701590Srgrimes#include <unistd.h>
711590Srgrimes
72207607Simp#include "tftp-utils.h"
73207607Simp#include "tftp-io.h"
74207607Simp#include "tftp-options.h"
75207607Simp#include "tftp.h"
761590Srgrimes
7785155Sbde#define	MAXLINE		200
781590Srgrimes#define	TIMEOUT		5		/* secs between rexmt's */
791590Srgrimes
80223926Sdelphijtypedef struct	sockaddr_storage peeraddr;
81207607Simpstatic int	connected;
82207607Simpstatic char	mode[32];
83241737Sedstatic jmp_buf	toplevel;
84207607Simpvolatile int	txrx_error;
85207607Simpstatic int	peer;
86207607Simp
87121015Stjr#define	MAX_MARGV	20
88207607Simpstatic int	margc;
89207607Simpstatic char	*margv[MAX_MARGV];
901590Srgrimes
91207607Simpint		verbose;
92241737Sedstatic char	*port = NULL;
931590Srgrimes
94207607Simpstatic void	get(int, char **);
95207607Simpstatic void	help(int, char **);
96207607Simpstatic void	intr(int);
97207607Simpstatic void	modecmd(int, char **);
98207607Simpstatic void	put(int, char **);
99207607Simpstatic void	quit(int, char **);
100207607Simpstatic void	setascii(int, char **);
101207607Simpstatic void	setbinary(int, char **);
102207607Simpstatic void	setpeer0(char *, const char *);
103207607Simpstatic void	setpeer(int, char **);
104207607Simpstatic void	settimeoutpacket(int, char **);
105207607Simpstatic void	settimeoutnetwork(int, char **);
106207607Simpstatic void	setdebug(int, char **);
107207607Simpstatic void	setverbose(int, char **);
108207607Simpstatic void	showstatus(int, char **);
109207607Simpstatic void	setblocksize(int, char **);
110207607Simpstatic void	setblocksize2(int, char **);
111207607Simpstatic void	setoptions(int, char **);
112207607Simpstatic void	setrollover(int, char **);
113207607Simpstatic void	setpacketdrop(int, char **);
114207607Simp
11592922Simpstatic void command(void) __dead2;
11692922Simpstatic const char *command_prompt(void);
1171590Srgrimes
118207607Simpstatic void urihandling(char *URI);
119207607Simpstatic void getusage(char *);
120207607Simpstatic void makeargv(char *line);
121207607Simpstatic void putusage(char *);
12292922Simpstatic void settftpmode(const char *);
1231590Srgrimes
124207607Simpstatic char	*tail(char *);
125207607Simpstatic struct	cmd *getcmd(char *);
12687708Smarkm
1271590Srgrimes#define HELPINDENT (sizeof("connect"))
1281590Srgrimes
1291590Srgrimesstruct cmd {
13087708Smarkm	const char	*name;
13192922Simp	void	(*handler)(int, char **);
132207607Simp	const char	*help;
1331590Srgrimes};
1341590Srgrimes
135207607Simpstatic struct cmd cmdtab[] = {
136207607Simp	{ "connect",	setpeer,	"connect to remote tftp"	},
137207607Simp	{ "mode",	modecmd,	"set file transfer mode"	},
138207607Simp	{ "put",	put,		"send file"			},
139207607Simp	{ "get",	get,		"receive file"			},
140207607Simp	{ "quit",	quit,		"exit tftp"			},
141207607Simp	{ "verbose",	setverbose,	"toggle verbose mode"		},
142207607Simp	{ "status",	showstatus,	"show current status"		},
143207607Simp	{ "binary",     setbinary,	"set mode to octet"		},
144207607Simp	{ "ascii",      setascii,	"set mode to netascii"		},
145207607Simp	{ "rexmt",	settimeoutpacket,
146207607Simp	  "set per-packet retransmission timeout[-]" },
147207607Simp	{ "timeout",	settimeoutnetwork,
148207607Simp	  "set total retransmission timeout" },
149207607Simp	{ "trace",	setdebug,	"enable 'debug packet'[-]"	},
150207607Simp	{ "debug",	setdebug,	"enable verbose output"		},
151207607Simp	{ "blocksize",	setblocksize,	"set blocksize[*]"		},
152207607Simp	{ "blocksize2",	setblocksize2,	"set blocksize as a power of 2[**]" },
153207607Simp	{ "rollover",	setrollover,	"rollover after 64K packets[**]" },
154207607Simp	{ "options",	setoptions,
155207607Simp	  "enable or disable RFC2347 style options" },
156207607Simp	{ "help",	help,		"print help information"	},
157222534Simp	{ "packetdrop",	setpacketdrop,	"artificial packetloss feature"	},
158207607Simp	{ "?",		help,		"print help information"	},
159207607Simp	{ NULL,		NULL,		NULL				}
160207607Simp};
1611590Srgrimes
162207607Simpstatic struct	modes {
163207607Simp	const char *m_name;
164207607Simp	const char *m_mode;
165207607Simp} modes[] = {
166207607Simp	{ "ascii",	"netascii" },
167207607Simp	{ "netascii",	"netascii" },
168207607Simp	{ "binary",	"octet" },
169207607Simp	{ "image",	"octet" },
170207607Simp	{ "octet",	"octet" },
171207607Simp	{ NULL,		NULL }
1721590Srgrimes};
1731590Srgrimes
1741590Srgrimesint
175183858Sdelphijmain(int argc, char *argv[])
1761590Srgrimes{
177207607Simp
178207607Simp	acting_as_client = 1;
179207607Simp	peer = -1;
1801590Srgrimes	strcpy(mode, "netascii");
1811590Srgrimes	signal(SIGINT, intr);
1821590Srgrimes	if (argc > 1) {
1831590Srgrimes		if (setjmp(toplevel) != 0)
18496433Sbsd			exit(txrx_error);
185207607Simp
186207607Simp		if (strncmp(argv[1], "tftp://", 7) == 0) {
187207607Simp			urihandling(argv[1]);
188207607Simp			exit(txrx_error);
189207607Simp		}
190207607Simp
1911590Srgrimes		setpeer(argc, argv);
1921590Srgrimes	}
1931590Srgrimes	if (setjmp(toplevel) != 0)
1941590Srgrimes		(void)putchar('\n');
195207607Simp
196207607Simp	init_options();
1971590Srgrimes	command();
1981590Srgrimes}
1991590Srgrimes
200207607Simp/*
201207607Simp * RFC3617 handling of TFTP URIs:
202207607Simp *
203207607Simp *    tftpURI         = "tftp://" host "/" file [ mode ]
204207607Simp *    mode            = ";"  "mode=" ( "netascii" / "octet" )
205207607Simp *    file            = *( unreserved / escaped )
206207607Simp *    host            = <as specified by RFC 2732>
207207607Simp *    unreserved      = <as specified in RFC 2396>
208207607Simp *    escaped         = <as specified in RFC 2396>
209207607Simp *
210207607Simp * We are cheating a little bit by allowing any mode as specified in the
211207607Simp * modes table defined earlier on in this file and mapping it on the real
212207607Simp * mode.
213207607Simp */
214207607Simpstatic void
215207607Simpurihandling(char *URI)
216207607Simp{
217207607Simp	char	uri[ARG_MAX];
218207607Simp	char	*host = NULL;
219207607Simp	char	*path = NULL;
220213099Smarius	char	*opts = NULL;
221213099Smarius	const char *tmode = "octet";
222207607Simp	char	*s;
223207607Simp	char	line[MAXLINE];
224207607Simp	int	i;
2251590Srgrimes
226287795Sdelphij	strlcpy(uri, URI, ARG_MAX);
227207607Simp	host = uri + 7;
228207607Simp
229207607Simp	if ((s = strchr(host, '/')) == NULL) {
230207607Simp		fprintf(stderr,
231207607Simp		    "Invalid URI: Couldn't find / after hostname\n");
232207607Simp		exit(1);
233207607Simp	}
234207607Simp	*s = '\0';
235207607Simp	path = s + 1;
236207607Simp
237207607Simp	if ((s = strchr(path, ';')) != NULL) {
238207607Simp		*s = '\0';
239213099Smarius		opts = s + 1;
240207607Simp
241213099Smarius		if (strncmp(opts, "mode=", 5) == 0) {
242213099Smarius			tmode = opts;
243213099Smarius			tmode += 5;
244207607Simp
245207607Simp			for (i = 0; modes[i].m_name != NULL; i++) {
246213099Smarius				if (strcmp(modes[i].m_name, tmode) == 0)
247207607Simp					break;
248207607Simp			}
249207607Simp			if (modes[i].m_name == NULL) {
250207607Simp				fprintf(stderr, "Invalid mode: '%s'\n", mode);
251207607Simp				exit(1);
252207607Simp			}
253207607Simp			settftpmode(modes[i].m_mode);
254207607Simp		}
255207607Simp	} else {
256207607Simp		settftpmode("octet");
257207607Simp	}
258207607Simp
259207607Simp	setpeer0(host, NULL);
260207607Simp
261207607Simp	sprintf(line, "get %s", path);
262207607Simp	makeargv(line);
263207607Simp	get(margc, margv);
264207607Simp}
265207607Simp
266207607Simpstatic char    hostname[MAXHOSTNAMELEN];
267207607Simp
268207607Simpstatic void
269207607Simpsetpeer0(char *host, const char *lport)
27094443Sume{
27194443Sume	struct addrinfo hints, *res0, *res;
27294443Sume	int error;
27394605Sdwmalone	const char *cause = "unknown";
27494443Sume
27594443Sume	if (connected) {
276207607Simp		close(peer);
277207607Simp		peer = -1;
27894443Sume	}
27994443Sume	connected = 0;
28094443Sume
28194443Sume	memset(&hints, 0, sizeof(hints));
28294443Sume	hints.ai_family = PF_UNSPEC;
28394443Sume	hints.ai_socktype = SOCK_DGRAM;
28494443Sume	hints.ai_protocol = IPPROTO_UDP;
28594443Sume	hints.ai_flags = AI_CANONNAME;
286207607Simp	if (!lport)
287207607Simp		lport = "tftp";
288207607Simp	error = getaddrinfo(host, lport, &hints, &res0);
28994443Sume	if (error) {
29094443Sume		warnx("%s", gai_strerror(error));
29194443Sume		return;
29294443Sume	}
29394443Sume
29494443Sume	for (res = res0; res; res = res->ai_next) {
29594443Sume		if (res->ai_addrlen > sizeof(peeraddr))
29694443Sume			continue;
297207607Simp		peer = socket(res->ai_family, res->ai_socktype,
298207607Simp			res->ai_protocol);
299207607Simp		if (peer < 0) {
30094443Sume			cause = "socket";
30194443Sume			continue;
30294443Sume		}
30394443Sume
304207607Simp		memset(&peer_sock, 0, sizeof(peer_sock));
305207607Simp		peer_sock.ss_family = res->ai_family;
306207607Simp		peer_sock.ss_len = res->ai_addrlen;
307207607Simp		if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) {
30894443Sume			cause = "bind";
309207607Simp			close(peer);
310207607Simp			peer = -1;
31194443Sume			continue;
31294443Sume		}
31394443Sume
31494443Sume		break;
31594443Sume	}
31694443Sume
317207607Simp	if (peer < 0)
31894443Sume		warn("%s", cause);
31994443Sume	else {
32094443Sume		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
321207607Simp		memcpy(&peer_sock, res->ai_addr, res->ai_addrlen);
32294443Sume		if (res->ai_canonname) {
323287795Sdelphij			(void) strlcpy(hostname, res->ai_canonname,
32494443Sume				sizeof(hostname));
32594443Sume		} else
326287795Sdelphij			(void) strlcpy(hostname, host, sizeof(hostname));
32794443Sume		connected = 1;
32894443Sume	}
32994443Sume
33094443Sume	freeaddrinfo(res0);
33194443Sume}
33294443Sume
333207607Simpstatic void
334183858Sdelphijsetpeer(int argc, char *argv[])
3351590Srgrimes{
336207607Simp	char	line[MAXLINE];
3371590Srgrimes
3381590Srgrimes	if (argc < 2) {
3391590Srgrimes		strcpy(line, "Connect ");
3401590Srgrimes		printf("(to) ");
34113068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
342207607Simp		makeargv(line);
3431590Srgrimes		argc = margc;
3441590Srgrimes		argv = margv;
3451590Srgrimes	}
34688883Sguido	if ((argc < 2) || (argc > 3)) {
347120647Ssimon		printf("usage: %s [host [port]]\n", argv[0]);
3481590Srgrimes		return;
3491590Srgrimes	}
350207607Simp	if (argc == 3) {
351207607Simp		port = argv[2];
352112582Sjon		setpeer0(argv[1], argv[2]);
353207607Simp	} else
35494443Sume		setpeer0(argv[1], NULL);
3551590Srgrimes}
3561590Srgrimes
357207607Simpstatic void
358183858Sdelphijmodecmd(int argc, char *argv[])
3591590Srgrimes{
36087708Smarkm	struct modes *p;
36187708Smarkm	const char *sep;
3621590Srgrimes
3631590Srgrimes	if (argc < 2) {
3641590Srgrimes		printf("Using %s mode to transfer files.\n", mode);
3651590Srgrimes		return;
3661590Srgrimes	}
3671590Srgrimes	if (argc == 2) {
3681590Srgrimes		for (p = modes; p->m_name; p++)
3691590Srgrimes			if (strcmp(argv[1], p->m_name) == 0)
3701590Srgrimes				break;
3711590Srgrimes		if (p->m_name) {
3721590Srgrimes			settftpmode(p->m_mode);
3731590Srgrimes			return;
3741590Srgrimes		}
3751590Srgrimes		printf("%s: unknown mode\n", argv[1]);
3761590Srgrimes		/* drop through and print usage message */
3771590Srgrimes	}
3781590Srgrimes
3791590Srgrimes	printf("usage: %s [", argv[0]);
3801590Srgrimes	sep = " ";
381207607Simp	for (p = modes; p->m_name != NULL; p++) {
3821590Srgrimes		printf("%s%s", sep, p->m_name);
3831590Srgrimes		if (*sep == ' ')
3841590Srgrimes			sep = " | ";
3851590Srgrimes	}
3861590Srgrimes	printf(" ]\n");
3871590Srgrimes	return;
3881590Srgrimes}
3891590Srgrimes
390207607Simpstatic void
391183858Sdelphijsetbinary(int argc __unused, char *argv[] __unused)
3928874Srgrimes{
3931590Srgrimes
3941590Srgrimes	settftpmode("octet");
3951590Srgrimes}
3961590Srgrimes
397207607Simpstatic void
398183858Sdelphijsetascii(int argc __unused, char *argv[] __unused)
3991590Srgrimes{
4001590Srgrimes
4011590Srgrimes	settftpmode("netascii");
4021590Srgrimes}
4031590Srgrimes
4041590Srgrimesstatic void
405183858Sdelphijsettftpmode(const char *newmode)
4061590Srgrimes{
407207607Simp
408339060Sasomers	strlcpy(mode, newmode, sizeof(mode));
4091590Srgrimes	if (verbose)
4101590Srgrimes		printf("mode set to %s\n", mode);
4111590Srgrimes}
4121590Srgrimes
4131590Srgrimes
4141590Srgrimes/*
4151590Srgrimes * Send file(s).
4161590Srgrimes */
417207607Simpstatic void
418183858Sdelphijput(int argc, char *argv[])
4191590Srgrimes{
420207607Simp	int	fd;
421207607Simp	int	n;
422207607Simp	char	*cp, *targ;
423207607Simp	char	line[MAXLINE];
424207607Simp	struct stat sb;
4251590Srgrimes
4261590Srgrimes	if (argc < 2) {
4271590Srgrimes		strcpy(line, "send ");
4281590Srgrimes		printf("(file) ");
42913068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
430207607Simp		makeargv(line);
4311590Srgrimes		argc = margc;
4321590Srgrimes		argv = margv;
4331590Srgrimes	}
4341590Srgrimes	if (argc < 2) {
4351590Srgrimes		putusage(argv[0]);
4361590Srgrimes		return;
4371590Srgrimes	}
4381590Srgrimes	targ = argv[argc - 1];
439229403Sed	if (strrchr(argv[argc - 1], ':')) {
44087708Smarkm		char *lcp;
4411590Srgrimes
4421590Srgrimes		for (n = 1; n < argc - 1; n++)
443229403Sed			if (strchr(argv[n], ':')) {
4441590Srgrimes				putusage(argv[0]);
4451590Srgrimes				return;
4461590Srgrimes			}
44787708Smarkm		lcp = argv[argc - 1];
448229403Sed		targ = strrchr(lcp, ':');
4491590Srgrimes		*targ++ = 0;
45094443Sume		if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
45194443Sume			lcp[strlen(lcp) - 1] = '\0';
45294443Sume			lcp++;
4531590Srgrimes		}
45494443Sume		setpeer0(lcp, NULL);
4551590Srgrimes	}
4561590Srgrimes	if (!connected) {
4571590Srgrimes		printf("No target machine specified.\n");
4581590Srgrimes		return;
4591590Srgrimes	}
4601590Srgrimes	if (argc < 4) {
4611590Srgrimes		cp = argc == 2 ? tail(targ) : argv[1];
4621590Srgrimes		fd = open(cp, O_RDONLY);
4631590Srgrimes		if (fd < 0) {
46428202Scharnier			warn("%s", cp);
4651590Srgrimes			return;
4661590Srgrimes		}
467207607Simp
468339060Sasomers		if (fstat(fd, &sb) < 0) {
469339060Sasomers			warn("%s", cp);
470339060Sasomers			return;
471339060Sasomers		}
472207607Simp		asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size);
473207607Simp
4741590Srgrimes		if (verbose)
4751590Srgrimes			printf("putting %s to %s:%s [%s]\n",
476207607Simp			    cp, hostname, targ, mode);
477207607Simp		xmitfile(peer, port, fd, targ, mode);
478339061Sasomers		close(fd);
4791590Srgrimes		return;
4801590Srgrimes	}
4811590Srgrimes				/* this assumes the target is a directory */
4821590Srgrimes				/* on a remote unix system.  hmmmm.  */
483229403Sed	cp = strchr(targ, '\0');
4841590Srgrimes	*cp++ = '/';
4851590Srgrimes	for (n = 1; n < argc - 1; n++) {
4861590Srgrimes		strcpy(cp, tail(argv[n]));
4871590Srgrimes		fd = open(argv[n], O_RDONLY);
4881590Srgrimes		if (fd < 0) {
48928202Scharnier			warn("%s", argv[n]);
4901590Srgrimes			continue;
4911590Srgrimes		}
492207607Simp
493339060Sasomers		if (fstat(fd, &sb) < 0) {
494339060Sasomers			warn("%s", argv[n]);
495339060Sasomers			continue;
496339060Sasomers		}
497207607Simp		asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size);
498207607Simp
4991590Srgrimes		if (verbose)
5001590Srgrimes			printf("putting %s to %s:%s [%s]\n",
501207607Simp			    argv[n], hostname, targ, mode);
502207607Simp		xmitfile(peer, port, fd, targ, mode);
5031590Srgrimes	}
5041590Srgrimes}
5051590Srgrimes
5061590Srgrimesstatic void
507207607Simpputusage(char *s)
5081590Srgrimes{
509207607Simp
510207607Simp	printf("usage: %s file [remotename]\n", s);
511207607Simp	printf("       %s file host:remotename\n", s);
512120647Ssimon	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
5131590Srgrimes}
5141590Srgrimes
5151590Srgrimes/*
5161590Srgrimes * Receive file(s).
5171590Srgrimes */
518207607Simpstatic void
519183858Sdelphijget(int argc, char *argv[])
5201590Srgrimes{
5211590Srgrimes	int fd;
52287708Smarkm	int n;
52387708Smarkm	char *cp;
5241590Srgrimes	char *src;
525207607Simp	char	line[MAXLINE];
5261590Srgrimes
5271590Srgrimes	if (argc < 2) {
5281590Srgrimes		strcpy(line, "get ");
5291590Srgrimes		printf("(files) ");
53013068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
531207607Simp		makeargv(line);
5321590Srgrimes		argc = margc;
5331590Srgrimes		argv = margv;
5341590Srgrimes	}
5351590Srgrimes	if (argc < 2) {
5361590Srgrimes		getusage(argv[0]);
5371590Srgrimes		return;
5381590Srgrimes	}
5391590Srgrimes	if (!connected) {
5401590Srgrimes		for (n = 1; n < argc ; n++)
541229403Sed			if (strrchr(argv[n], ':') == 0) {
542207607Simp				printf("No remote host specified and "
543207607Simp				    "no host given for file '%s'\n", argv[n]);
5441590Srgrimes				getusage(argv[0]);
5451590Srgrimes				return;
5461590Srgrimes			}
5471590Srgrimes	}
5481590Srgrimes	for (n = 1; n < argc ; n++) {
549229403Sed		src = strrchr(argv[n], ':');
5501590Srgrimes		if (src == NULL)
5511590Srgrimes			src = argv[n];
5521590Srgrimes		else {
55394443Sume			char *lcp;
5541590Srgrimes
5551590Srgrimes			*src++ = 0;
55694443Sume			lcp = argv[n];
55794443Sume			if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
55894443Sume				lcp[strlen(lcp) - 1] = '\0';
55994443Sume				lcp++;
56094443Sume			}
56194443Sume			setpeer0(lcp, NULL);
56294443Sume			if (!connected)
5631590Srgrimes				continue;
5641590Srgrimes		}
5651590Srgrimes		if (argc < 4) {
5661590Srgrimes			cp = argc == 3 ? argv[2] : tail(src);
5671590Srgrimes			fd = creat(cp, 0644);
5681590Srgrimes			if (fd < 0) {
56928202Scharnier				warn("%s", cp);
5701590Srgrimes				return;
5711590Srgrimes			}
5721590Srgrimes			if (verbose)
5731590Srgrimes				printf("getting from %s:%s to %s [%s]\n",
574207607Simp				    hostname, src, cp, mode);
575207607Simp			recvfile(peer, port, fd, src, mode);
5761590Srgrimes			break;
5771590Srgrimes		}
5781590Srgrimes		cp = tail(src);         /* new .. jdg */
5791590Srgrimes		fd = creat(cp, 0644);
5801590Srgrimes		if (fd < 0) {
58128202Scharnier			warn("%s", cp);
5821590Srgrimes			continue;
5831590Srgrimes		}
5841590Srgrimes		if (verbose)
5851590Srgrimes			printf("getting from %s:%s to %s [%s]\n",
586207607Simp			    hostname, src, cp, mode);
587207607Simp		recvfile(peer, port, fd, src, mode);
5881590Srgrimes	}
5891590Srgrimes}
5901590Srgrimes
5911590Srgrimesstatic void
592207607Simpgetusage(char *s)
5931590Srgrimes{
594207607Simp
595207607Simp	printf("usage: %s file [localname]\n", s);
596207607Simp	printf("       %s [host:]file [localname]\n", s);
597120647Ssimon	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
5981590Srgrimes}
5991590Srgrimes
600207607Simpstatic void
601207607Simpsettimeoutpacket(int argc, char *argv[])
6021590Srgrimes{
6031590Srgrimes	int t;
604207607Simp	char	line[MAXLINE];
6051590Srgrimes
6061590Srgrimes	if (argc < 2) {
607207607Simp		strcpy(line, "Packet timeout ");
6081590Srgrimes		printf("(value) ");
60913068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
610207607Simp		makeargv(line);
6111590Srgrimes		argc = margc;
6121590Srgrimes		argv = margv;
6131590Srgrimes	}
6141590Srgrimes	if (argc != 2) {
6151590Srgrimes		printf("usage: %s value\n", argv[0]);
6161590Srgrimes		return;
6171590Srgrimes	}
6181590Srgrimes	t = atoi(argv[1]);
619207607Simp	if (t < 0) {
6201590Srgrimes		printf("%s: bad value\n", argv[1]);
621207607Simp		return;
622207607Simp	}
623207607Simp
624207607Simp	settimeouts(t, timeoutnetwork, maxtimeouts);
6251590Srgrimes}
6261590Srgrimes
627207607Simpstatic void
628207607Simpsettimeoutnetwork(int argc, char *argv[])
6291590Srgrimes{
6301590Srgrimes	int t;
631207607Simp	char	line[MAXLINE];
6321590Srgrimes
6331590Srgrimes	if (argc < 2) {
634207607Simp		strcpy(line, "Network timeout ");
6351590Srgrimes		printf("(value) ");
63613068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
637207607Simp		makeargv(line);
6381590Srgrimes		argc = margc;
6391590Srgrimes		argv = margv;
6401590Srgrimes	}
6411590Srgrimes	if (argc != 2) {
6421590Srgrimes		printf("usage: %s value\n", argv[0]);
6431590Srgrimes		return;
6441590Srgrimes	}
6451590Srgrimes	t = atoi(argv[1]);
646207607Simp	if (t < 0) {
6471590Srgrimes		printf("%s: bad value\n", argv[1]);
648207607Simp		return;
649207607Simp	}
650207607Simp
651207607Simp	settimeouts(timeoutpacket, t, maxtimeouts);
6521590Srgrimes}
6531590Srgrimes
654207607Simpstatic void
655207607Simpshowstatus(int argc __unused, char *argv[] __unused)
6561590Srgrimes{
657207607Simp
658207607Simp	printf("Remote host: %s\n",
659207607Simp	    connected ? hostname : "none specified yet");
660207607Simp	printf("RFC2347 Options support: %s\n",
661207607Simp	    options_rfc_enabled ? "enabled" : "disabled");
662207607Simp	printf("Non-RFC defined options support: %s\n",
663207607Simp	    options_extra_enabled ? "enabled" : "disabled");
664207607Simp	printf("Mode: %s\n", mode);
665207607Simp	printf("Verbose: %s\n", verbose ? "on" : "off");
666207607Simp	printf("Debug: %s\n", debug_show(debug));
667207607Simp	printf("Artificial packetloss: %d in 100 packets\n",
668207607Simp	    packetdroppercentage);
669207607Simp	printf("Segment size: %d bytes\n", segsize);
670207607Simp	printf("Network timeout: %d seconds\n", timeoutpacket);
671207607Simp	printf("Maximum network timeout: %d seconds\n", timeoutnetwork);
672207607Simp	printf("Maximum timeouts: %d \n", maxtimeouts);
6731590Srgrimes}
6741590Srgrimes
675207607Simpstatic void
676183858Sdelphijintr(int dummy __unused)
6771590Srgrimes{
6781590Srgrimes
6791590Srgrimes	signal(SIGALRM, SIG_IGN);
6801590Srgrimes	alarm(0);
6811590Srgrimes	longjmp(toplevel, -1);
6821590Srgrimes}
6831590Srgrimes
684207607Simpstatic char *
685183858Sdelphijtail(char *filename)
6861590Srgrimes{
68787708Smarkm	char *s;
6888874Srgrimes
6891590Srgrimes	while (*filename) {
690229403Sed		s = strrchr(filename, '/');
6911590Srgrimes		if (s == NULL)
6921590Srgrimes			break;
6931590Srgrimes		if (s[1])
6941590Srgrimes			return (s + 1);
6951590Srgrimes		*s = '\0';
6961590Srgrimes	}
6971590Srgrimes	return (filename);
6981590Srgrimes}
6991590Srgrimes
70085120Smdoddstatic const char *
701213099Smariuscommand_prompt(void)
70285155Sbde{
70385155Sbde
70485120Smdodd	return ("tftp> ");
70585120Smdodd}
70685120Smdodd
7071590Srgrimes/*
7081590Srgrimes * Command parser.
7091590Srgrimes */
71018286Sbdestatic void
711183858Sdelphijcommand(void)
7121590Srgrimes{
71385155Sbde	HistEvent he;
71487708Smarkm	struct cmd *c;
71585155Sbde	static EditLine *el;
71685155Sbde	static History *hist;
71785155Sbde	const char *bp;
71813068Sjoerg	char *cp;
71987708Smarkm	int len, num, vrbose;
720207607Simp	char	line[MAXLINE];
7211590Srgrimes
72287708Smarkm	vrbose = isatty(0);
72387708Smarkm	if (vrbose) {
72485120Smdodd		el = el_init("tftp", stdin, stdout, stderr);
72585120Smdodd		hist = history_init();
726151471Sstefanf		history(hist, &he, H_SETSIZE, 100);
72785120Smdodd		el_set(el, EL_HIST, history, hist);
72885120Smdodd		el_set(el, EL_EDITOR, "emacs");
72985120Smdodd		el_set(el, EL_PROMPT, command_prompt);
73085120Smdodd		el_set(el, EL_SIGNAL, 1);
73185120Smdodd		el_source(el, NULL);
73285120Smdodd	}
7331590Srgrimes	for (;;) {
73487708Smarkm		if (vrbose) {
73585120Smdodd                        if ((bp = el_gets(el, &num)) == NULL || num == 0)
73685120Smdodd                                exit(0);
73785120Smdodd                        len = (num > MAXLINE) ? MAXLINE : num;
73885120Smdodd                        memcpy(line, bp, len);
73985120Smdodd                        line[len] = '\0';
74085120Smdodd                        history(hist, &he, H_ENTER, bp);
74185120Smdodd		} else {
742207607Simp			line[0] = 0;
743230044Skevlo			if (fgets(line, sizeof line , stdin) == NULL) {
74485120Smdodd				if (feof(stdin)) {
74596433Sbsd					exit(txrx_error);
74685120Smdodd				} else {
74785120Smdodd					continue;
74885120Smdodd				}
7491590Srgrimes			}
7501590Srgrimes		}
75113068Sjoerg		if ((cp = strchr(line, '\n')))
75213068Sjoerg			*cp = '\0';
7531590Srgrimes		if (line[0] == 0)
7541590Srgrimes			continue;
755207607Simp		makeargv(line);
7561590Srgrimes		if (margc == 0)
7571590Srgrimes			continue;
7581590Srgrimes		c = getcmd(margv[0]);
7591590Srgrimes		if (c == (struct cmd *)-1) {
7601590Srgrimes			printf("?Ambiguous command\n");
7611590Srgrimes			continue;
7621590Srgrimes		}
7631590Srgrimes		if (c == 0) {
7641590Srgrimes			printf("?Invalid command\n");
7651590Srgrimes			continue;
7661590Srgrimes		}
7671590Srgrimes		(*c->handler)(margc, margv);
7681590Srgrimes	}
7691590Srgrimes}
7701590Srgrimes
771207607Simpstatic struct cmd *
772183858Sdelphijgetcmd(char *name)
7731590Srgrimes{
77487708Smarkm	const char *p, *q;
77587708Smarkm	struct cmd *c, *found;
77687708Smarkm	int nmatches, longest;
7771590Srgrimes
7781590Srgrimes	longest = 0;
7791590Srgrimes	nmatches = 0;
7801590Srgrimes	found = 0;
7811590Srgrimes	for (c = cmdtab; (p = c->name) != NULL; c++) {
7821590Srgrimes		for (q = name; *q == *p++; q++)
7831590Srgrimes			if (*q == 0)		/* exact match? */
7841590Srgrimes				return (c);
7851590Srgrimes		if (!*q) {			/* the name was a prefix */
7861590Srgrimes			if (q - name > longest) {
7871590Srgrimes				longest = q - name;
7881590Srgrimes				nmatches = 1;
7891590Srgrimes				found = c;
7901590Srgrimes			} else if (q - name == longest)
7911590Srgrimes				nmatches++;
7921590Srgrimes		}
7931590Srgrimes	}
7941590Srgrimes	if (nmatches > 1)
7951590Srgrimes		return ((struct cmd *)-1);
7961590Srgrimes	return (found);
7971590Srgrimes}
7981590Srgrimes
7991590Srgrimes/*
8001590Srgrimes * Slice a string up into argc/argv.
8011590Srgrimes */
8021590Srgrimesstatic void
803207607Simpmakeargv(char *line)
8041590Srgrimes{
80587708Smarkm	char *cp;
80687708Smarkm	char **argp = margv;
8071590Srgrimes
8081590Srgrimes	margc = 0;
809207607Simp	if ((cp = strchr(line, '\n')) != NULL)
81013068Sjoerg		*cp = '\0';
811207607Simp	for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) {
8121590Srgrimes		while (isspace(*cp))
8131590Srgrimes			cp++;
8141590Srgrimes		if (*cp == '\0')
8151590Srgrimes			break;
8161590Srgrimes		*argp++ = cp;
8171590Srgrimes		margc += 1;
8181590Srgrimes		while (*cp != '\0' && !isspace(*cp))
8191590Srgrimes			cp++;
8201590Srgrimes		if (*cp == '\0')
8211590Srgrimes			break;
8221590Srgrimes		*cp++ = '\0';
8231590Srgrimes	}
8241590Srgrimes	*argp++ = 0;
8251590Srgrimes}
8261590Srgrimes
827207607Simpstatic void
828183858Sdelphijquit(int argc __unused, char *argv[] __unused)
8291590Srgrimes{
830207607Simp
83196433Sbsd	exit(txrx_error);
8321590Srgrimes}
8331590Srgrimes
8341590Srgrimes/*
8351590Srgrimes * Help command.
8361590Srgrimes */
837207607Simpstatic void
838183858Sdelphijhelp(int argc, char *argv[])
8391590Srgrimes{
84087708Smarkm	struct cmd *c;
8411590Srgrimes
8421590Srgrimes	if (argc == 1) {
8431590Srgrimes		printf("Commands may be abbreviated.  Commands are:\n\n");
8441590Srgrimes		for (c = cmdtab; c->name; c++)
8451590Srgrimes			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
846207607Simp
847207607Simp		printf("\n[-] : You shouldn't use these ones anymore.\n");
848223135Srodrigc		printf("[*] : RFC2347 options support required.\n");
849223135Srodrigc		printf("[**] : Non-standard RFC2347 option.\n");
8501590Srgrimes		return;
8511590Srgrimes	}
8521590Srgrimes	while (--argc > 0) {
85387708Smarkm		char *arg;
8541590Srgrimes		arg = *++argv;
8551590Srgrimes		c = getcmd(arg);
8561590Srgrimes		if (c == (struct cmd *)-1)
857207607Simp			printf("?Ambiguous help command: %s\n", arg);
8581590Srgrimes		else if (c == (struct cmd *)0)
859207607Simp			printf("?Invalid help command: %s\n", arg);
8601590Srgrimes		else
8611590Srgrimes			printf("%s\n", c->help);
8621590Srgrimes	}
8631590Srgrimes}
8641590Srgrimes
865207607Simpstatic void
866207607Simpsetverbose(int argc __unused, char *argv[] __unused)
8671590Srgrimes{
8681590Srgrimes
8691590Srgrimes	verbose = !verbose;
8701590Srgrimes	printf("Verbose mode %s.\n", verbose ? "on" : "off");
8711590Srgrimes}
872207607Simp
873207607Simpstatic void
874207607Simpsetoptions(int argc, char *argv[])
875207607Simp{
876207607Simp
877207607Simp	if (argc == 2) {
878207607Simp		if (strcasecmp(argv[1], "enable") == 0 ||
879207607Simp		    strcasecmp(argv[1], "on") == 0) {
880207607Simp			options_extra_enabled = 1;
881207607Simp			options_rfc_enabled = 1;
882207607Simp		}
883207607Simp		if (strcasecmp(argv[1], "disable") == 0 ||
884207607Simp		    strcasecmp(argv[1], "off") == 0) {
885207607Simp			options_extra_enabled = 0;
886207607Simp			options_rfc_enabled = 0;
887207607Simp		}
888207607Simp		if (strcasecmp(argv[1], "extra") == 0)
889207607Simp			options_extra_enabled = !options_extra_enabled;
890207607Simp	}
891207607Simp	printf("Support for RFC2347 style options are now %s.\n",
892207607Simp	    options_rfc_enabled ? "enabled" : "disabled");
893207607Simp	printf("Support for non-RFC defined options are now %s.\n",
894207607Simp	    options_extra_enabled ? "enabled" : "disabled");
895207607Simp
896207607Simp	printf("\nThe following options are available:\n"
897207607Simp	    "\toptions on	: enable support for RFC2347 style options\n"
898207607Simp	    "\toptions off	: disable support for RFC2347 style options\n"
899207607Simp	    "\toptions extra	: toggle support for non-RFC defined options\n"
900207607Simp	);
901207607Simp}
902207607Simp
903207607Simpstatic void
904207607Simpsetrollover(int argc, char *argv[])
905207607Simp{
906207607Simp
907207607Simp	if (argc == 2) {
908207607Simp		if (strcasecmp(argv[1], "never") == 0 ||
909207607Simp		    strcasecmp(argv[1], "none") == 0) {
910207607Simp			free(options[OPT_ROLLOVER].o_request);
911207607Simp			options[OPT_ROLLOVER].o_request = NULL;
912207607Simp		}
913207607Simp		if (strcasecmp(argv[1], "1") == 0) {
914207607Simp			free(options[OPT_ROLLOVER].o_request);
915207607Simp			options[OPT_ROLLOVER].o_request = strdup("1");
916207607Simp		}
917207607Simp		if (strcasecmp(argv[1], "0") == 0) {
918207607Simp			free(options[OPT_ROLLOVER].o_request);
919207607Simp			options[OPT_ROLLOVER].o_request = strdup("0");
920207607Simp		}
921207607Simp	}
922207607Simp	printf("Support for the rollover options is %s.\n",
923207607Simp	    options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled");
924207607Simp	if (options[OPT_ROLLOVER].o_request != NULL)
925207607Simp		printf("Block rollover will be to block %s.\n",
926207607Simp		    options[OPT_ROLLOVER].o_request);
927207607Simp
928207607Simp
929207607Simp	printf("\nThe following rollover options are available:\n"
930207607Simp	    "\trollover 0	: rollover to block zero (default)\n"
931207607Simp	    "\trollover 1	: rollover to block one\n"
932207607Simp	    "\trollover never	: do not support the rollover option\n"
933207607Simp	    "\trollover none	: do not support the rollover option\n"
934207607Simp	);
935207607Simp}
936207607Simp
937207607Simpstatic void
938207607Simpsetdebug(int argc, char *argv[])
939207607Simp{
940207607Simp	int i;
941207607Simp
942207607Simp	if (argc != 1) {
943207607Simp		i = 1;
944207607Simp		while (i < argc)
945207607Simp			debug ^= debug_find(argv[i++]);
946207607Simp	}
947207607Simp	printf("The following debugging is enabled: %s\n", debug_show(debug));
948207607Simp
949207607Simp	printf("\nThe following debugs are available:\n");
950207607Simp	i = 0;
951207607Simp	while (debugs[i].name != NULL) {
952207607Simp		printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc);
953207607Simp		i++;
954207607Simp	}
955207607Simp}
956207607Simp
957207607Simpstatic void
958207607Simpsetblocksize(int argc, char *argv[])
959207607Simp{
960207607Simp
961207607Simp	if (!options_rfc_enabled)
962207607Simp		printf("RFC2347 style options are not enabled "
963222534Simp		    "(but proceeding anyway)\n");
964207607Simp
965207607Simp	if (argc != 1) {
966207607Simp		int size = atoi(argv[1]);
967207607Simp		size_t max;
968213099Smarius		u_long maxdgram;
969207607Simp
970213099Smarius		max = sizeof(maxdgram);
971207607Simp		if (sysctlbyname("net.inet.udp.maxdgram",
972213099Smarius			&maxdgram, &max, NULL, 0) < 0) {
973207607Simp			perror("sysctl: net.inet.udp.maxdgram");
974207607Simp			return;
975207607Simp		}
976207607Simp
977207607Simp		if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
978207607Simp			printf("Blocksize should be between %d and %d bytes.\n",
979207607Simp				BLKSIZE_MIN, BLKSIZE_MAX);
980207607Simp			return;
981213099Smarius		} else if (size > (int)maxdgram - 4) {
982213099Smarius			printf("Blocksize can't be bigger than %ld bytes due "
983207607Simp			    "to the net.inet.udp.maxdgram sysctl limitation.\n",
984213099Smarius			    maxdgram - 4);
985207607Simp			asprintf(&options[OPT_BLKSIZE].o_request,
986213099Smarius			    "%ld", maxdgram - 4);
987207607Simp		} else {
988207607Simp			asprintf(&options[OPT_BLKSIZE].o_request, "%d", size);
989207607Simp		}
990207607Simp	}
991207607Simp	printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request);
992207607Simp}
993207607Simp
994207607Simpstatic void
995207607Simpsetblocksize2(int argc, char *argv[])
996207607Simp{
997207607Simp
998207607Simp	if (!options_rfc_enabled || !options_extra_enabled)
999207607Simp		printf(
1000207607Simp		    "RFC2347 style or non-RFC defined options are not enabled "
1001222534Simp		    "(but proceeding anyway)\n");
1002207607Simp
1003207607Simp	if (argc != 1) {
1004207607Simp		int size = atoi(argv[1]);
1005207607Simp		int i;
1006207607Simp		size_t max;
1007213099Smarius		u_long maxdgram;
1008207607Simp
1009207607Simp		int sizes[] = {
1010207607Simp			8, 16, 32, 64, 128, 256, 512, 1024,
1011207607Simp			2048, 4096, 8192, 16384, 32768, 0
1012207607Simp		};
1013207607Simp
1014213099Smarius		max = sizeof(maxdgram);
1015207607Simp		if (sysctlbyname("net.inet.udp.maxdgram",
1016213099Smarius			&maxdgram, &max, NULL, 0) < 0) {
1017207607Simp			perror("sysctl: net.inet.udp.maxdgram");
1018207607Simp			return;
1019207607Simp		}
1020207607Simp
1021207607Simp		for (i = 0; sizes[i] != 0; i++) {
1022207607Simp			if (sizes[i] == size) break;
1023207607Simp		}
1024207607Simp		if (sizes[i] == 0) {
1025207607Simp			printf("Blocksize2 should be a power of two between "
1026207607Simp			    "8 and 32768.\n");
1027207607Simp			return;
1028207607Simp		}
1029207607Simp
1030207607Simp		if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
1031207607Simp			printf("Blocksize2 should be between "
1032207607Simp			    "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX);
1033207607Simp			return;
1034213099Smarius		} else if (size > (int)maxdgram - 4) {
1035213099Smarius			printf("Blocksize2 can't be bigger than %ld bytes due "
1036207607Simp			    "to the net.inet.udp.maxdgram sysctl limitation.\n",
1037213099Smarius			    maxdgram - 4);
1038207607Simp			for (i = 0; sizes[i+1] != 0; i++) {
1039213099Smarius				if ((int)maxdgram < sizes[i+1]) break;
1040207607Simp			}
1041207607Simp			asprintf(&options[OPT_BLKSIZE2].o_request,
1042207607Simp			    "%d", sizes[i]);
1043207607Simp		} else {
1044207607Simp			asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size);
1045207607Simp		}
1046207607Simp	}
1047207607Simp	printf("Blocksize2 is now %s bytes.\n",
1048207607Simp	    options[OPT_BLKSIZE2].o_request);
1049207607Simp}
1050207607Simp
1051207607Simpstatic void
1052207607Simpsetpacketdrop(int argc, char *argv[])
1053207607Simp{
1054207607Simp
1055207607Simp	if (argc != 1)
1056207607Simp		packetdroppercentage = atoi(argv[1]);
1057207607Simp
1058207607Simp	printf("Randomly %d in 100 packets will be dropped\n",
1059207607Simp	    packetdroppercentage);
1060207607Simp}
1061