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$");
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
226207607Simp	strncpy(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) {
323207607Simp			(void) strncpy(hostname, res->ai_canonname,
32494443Sume				sizeof(hostname));
32594443Sume		} else
326207607Simp			(void) strncpy(hostname, host, sizeof(hostname));
327207607Simp		hostname[sizeof(hostname)-1] = 0;
32894443Sume		connected = 1;
32994443Sume	}
33094443Sume
33194443Sume	freeaddrinfo(res0);
33294443Sume}
33394443Sume
334207607Simpstatic void
335183858Sdelphijsetpeer(int argc, char *argv[])
3361590Srgrimes{
337207607Simp	char	line[MAXLINE];
3381590Srgrimes
3391590Srgrimes	if (argc < 2) {
3401590Srgrimes		strcpy(line, "Connect ");
3411590Srgrimes		printf("(to) ");
34213068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
343207607Simp		makeargv(line);
3441590Srgrimes		argc = margc;
3451590Srgrimes		argv = margv;
3461590Srgrimes	}
34788883Sguido	if ((argc < 2) || (argc > 3)) {
348120647Ssimon		printf("usage: %s [host [port]]\n", argv[0]);
3491590Srgrimes		return;
3501590Srgrimes	}
351207607Simp	if (argc == 3) {
352207607Simp		port = argv[2];
353112582Sjon		setpeer0(argv[1], argv[2]);
354207607Simp	} else
35594443Sume		setpeer0(argv[1], NULL);
3561590Srgrimes}
3571590Srgrimes
358207607Simpstatic void
359183858Sdelphijmodecmd(int argc, char *argv[])
3601590Srgrimes{
36187708Smarkm	struct modes *p;
36287708Smarkm	const char *sep;
3631590Srgrimes
3641590Srgrimes	if (argc < 2) {
3651590Srgrimes		printf("Using %s mode to transfer files.\n", mode);
3661590Srgrimes		return;
3671590Srgrimes	}
3681590Srgrimes	if (argc == 2) {
3691590Srgrimes		for (p = modes; p->m_name; p++)
3701590Srgrimes			if (strcmp(argv[1], p->m_name) == 0)
3711590Srgrimes				break;
3721590Srgrimes		if (p->m_name) {
3731590Srgrimes			settftpmode(p->m_mode);
3741590Srgrimes			return;
3751590Srgrimes		}
3761590Srgrimes		printf("%s: unknown mode\n", argv[1]);
3771590Srgrimes		/* drop through and print usage message */
3781590Srgrimes	}
3791590Srgrimes
3801590Srgrimes	printf("usage: %s [", argv[0]);
3811590Srgrimes	sep = " ";
382207607Simp	for (p = modes; p->m_name != NULL; p++) {
3831590Srgrimes		printf("%s%s", sep, p->m_name);
3841590Srgrimes		if (*sep == ' ')
3851590Srgrimes			sep = " | ";
3861590Srgrimes	}
3871590Srgrimes	printf(" ]\n");
3881590Srgrimes	return;
3891590Srgrimes}
3901590Srgrimes
391207607Simpstatic void
392183858Sdelphijsetbinary(int argc __unused, char *argv[] __unused)
3938874Srgrimes{
3941590Srgrimes
3951590Srgrimes	settftpmode("octet");
3961590Srgrimes}
3971590Srgrimes
398207607Simpstatic void
399183858Sdelphijsetascii(int argc __unused, char *argv[] __unused)
4001590Srgrimes{
4011590Srgrimes
4021590Srgrimes	settftpmode("netascii");
4031590Srgrimes}
4041590Srgrimes
4051590Srgrimesstatic void
406183858Sdelphijsettftpmode(const char *newmode)
4071590Srgrimes{
408207607Simp
4091590Srgrimes	strcpy(mode, newmode);
4101590Srgrimes	if (verbose)
4111590Srgrimes		printf("mode set to %s\n", mode);
4121590Srgrimes}
4131590Srgrimes
4141590Srgrimes
4151590Srgrimes/*
4161590Srgrimes * Send file(s).
4171590Srgrimes */
418207607Simpstatic void
419183858Sdelphijput(int argc, char *argv[])
4201590Srgrimes{
421207607Simp	int	fd;
422207607Simp	int	n;
423207607Simp	char	*cp, *targ;
424207607Simp	char	line[MAXLINE];
425207607Simp	struct stat sb;
4261590Srgrimes
4271590Srgrimes	if (argc < 2) {
4281590Srgrimes		strcpy(line, "send ");
4291590Srgrimes		printf("(file) ");
43013068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
431207607Simp		makeargv(line);
4321590Srgrimes		argc = margc;
4331590Srgrimes		argv = margv;
4341590Srgrimes	}
4351590Srgrimes	if (argc < 2) {
4361590Srgrimes		putusage(argv[0]);
4371590Srgrimes		return;
4381590Srgrimes	}
4391590Srgrimes	targ = argv[argc - 1];
440229403Sed	if (strrchr(argv[argc - 1], ':')) {
44187708Smarkm		char *lcp;
4421590Srgrimes
4431590Srgrimes		for (n = 1; n < argc - 1; n++)
444229403Sed			if (strchr(argv[n], ':')) {
4451590Srgrimes				putusage(argv[0]);
4461590Srgrimes				return;
4471590Srgrimes			}
44887708Smarkm		lcp = argv[argc - 1];
449229403Sed		targ = strrchr(lcp, ':');
4501590Srgrimes		*targ++ = 0;
45194443Sume		if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
45294443Sume			lcp[strlen(lcp) - 1] = '\0';
45394443Sume			lcp++;
4541590Srgrimes		}
45594443Sume		setpeer0(lcp, NULL);
4561590Srgrimes	}
4571590Srgrimes	if (!connected) {
4581590Srgrimes		printf("No target machine specified.\n");
4591590Srgrimes		return;
4601590Srgrimes	}
4611590Srgrimes	if (argc < 4) {
4621590Srgrimes		cp = argc == 2 ? tail(targ) : argv[1];
4631590Srgrimes		fd = open(cp, O_RDONLY);
4641590Srgrimes		if (fd < 0) {
46528202Scharnier			warn("%s", cp);
4661590Srgrimes			return;
4671590Srgrimes		}
468207607Simp
469207607Simp		stat(cp, &sb);
470207607Simp		asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size);
471207607Simp
4721590Srgrimes		if (verbose)
4731590Srgrimes			printf("putting %s to %s:%s [%s]\n",
474207607Simp			    cp, hostname, targ, mode);
475207607Simp		xmitfile(peer, port, fd, targ, mode);
4761590Srgrimes		return;
4771590Srgrimes	}
4781590Srgrimes				/* this assumes the target is a directory */
4791590Srgrimes				/* on a remote unix system.  hmmmm.  */
480229403Sed	cp = strchr(targ, '\0');
4811590Srgrimes	*cp++ = '/';
4821590Srgrimes	for (n = 1; n < argc - 1; n++) {
4831590Srgrimes		strcpy(cp, tail(argv[n]));
4841590Srgrimes		fd = open(argv[n], O_RDONLY);
4851590Srgrimes		if (fd < 0) {
48628202Scharnier			warn("%s", argv[n]);
4871590Srgrimes			continue;
4881590Srgrimes		}
489207607Simp
490207607Simp		stat(cp, &sb);
491207607Simp		asprintf(&options[OPT_TSIZE].o_request, "%ju", sb.st_size);
492207607Simp
4931590Srgrimes		if (verbose)
4941590Srgrimes			printf("putting %s to %s:%s [%s]\n",
495207607Simp			    argv[n], hostname, targ, mode);
496207607Simp		xmitfile(peer, port, fd, targ, mode);
4971590Srgrimes	}
4981590Srgrimes}
4991590Srgrimes
5001590Srgrimesstatic void
501207607Simpputusage(char *s)
5021590Srgrimes{
503207607Simp
504207607Simp	printf("usage: %s file [remotename]\n", s);
505207607Simp	printf("       %s file host:remotename\n", s);
506120647Ssimon	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
5071590Srgrimes}
5081590Srgrimes
5091590Srgrimes/*
5101590Srgrimes * Receive file(s).
5111590Srgrimes */
512207607Simpstatic void
513183858Sdelphijget(int argc, char *argv[])
5141590Srgrimes{
5151590Srgrimes	int fd;
51687708Smarkm	int n;
51787708Smarkm	char *cp;
5181590Srgrimes	char *src;
519207607Simp	char	line[MAXLINE];
5201590Srgrimes
5211590Srgrimes	if (argc < 2) {
5221590Srgrimes		strcpy(line, "get ");
5231590Srgrimes		printf("(files) ");
52413068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
525207607Simp		makeargv(line);
5261590Srgrimes		argc = margc;
5271590Srgrimes		argv = margv;
5281590Srgrimes	}
5291590Srgrimes	if (argc < 2) {
5301590Srgrimes		getusage(argv[0]);
5311590Srgrimes		return;
5321590Srgrimes	}
5331590Srgrimes	if (!connected) {
5341590Srgrimes		for (n = 1; n < argc ; n++)
535229403Sed			if (strrchr(argv[n], ':') == 0) {
536207607Simp				printf("No remote host specified and "
537207607Simp				    "no host given for file '%s'\n", argv[n]);
5381590Srgrimes				getusage(argv[0]);
5391590Srgrimes				return;
5401590Srgrimes			}
5411590Srgrimes	}
5421590Srgrimes	for (n = 1; n < argc ; n++) {
543229403Sed		src = strrchr(argv[n], ':');
5441590Srgrimes		if (src == NULL)
5451590Srgrimes			src = argv[n];
5461590Srgrimes		else {
54794443Sume			char *lcp;
5481590Srgrimes
5491590Srgrimes			*src++ = 0;
55094443Sume			lcp = argv[n];
55194443Sume			if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
55294443Sume				lcp[strlen(lcp) - 1] = '\0';
55394443Sume				lcp++;
55494443Sume			}
55594443Sume			setpeer0(lcp, NULL);
55694443Sume			if (!connected)
5571590Srgrimes				continue;
5581590Srgrimes		}
5591590Srgrimes		if (argc < 4) {
5601590Srgrimes			cp = argc == 3 ? argv[2] : tail(src);
5611590Srgrimes			fd = creat(cp, 0644);
5621590Srgrimes			if (fd < 0) {
56328202Scharnier				warn("%s", cp);
5641590Srgrimes				return;
5651590Srgrimes			}
5661590Srgrimes			if (verbose)
5671590Srgrimes				printf("getting from %s:%s to %s [%s]\n",
568207607Simp				    hostname, src, cp, mode);
569207607Simp			recvfile(peer, port, fd, src, mode);
5701590Srgrimes			break;
5711590Srgrimes		}
5721590Srgrimes		cp = tail(src);         /* new .. jdg */
5731590Srgrimes		fd = creat(cp, 0644);
5741590Srgrimes		if (fd < 0) {
57528202Scharnier			warn("%s", cp);
5761590Srgrimes			continue;
5771590Srgrimes		}
5781590Srgrimes		if (verbose)
5791590Srgrimes			printf("getting from %s:%s to %s [%s]\n",
580207607Simp			    hostname, src, cp, mode);
581207607Simp		recvfile(peer, port, fd, src, mode);
5821590Srgrimes	}
5831590Srgrimes}
5841590Srgrimes
5851590Srgrimesstatic void
586207607Simpgetusage(char *s)
5871590Srgrimes{
588207607Simp
589207607Simp	printf("usage: %s file [localname]\n", s);
590207607Simp	printf("       %s [host:]file [localname]\n", s);
591120647Ssimon	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
5921590Srgrimes}
5931590Srgrimes
594207607Simpstatic void
595207607Simpsettimeoutpacket(int argc, char *argv[])
5961590Srgrimes{
5971590Srgrimes	int t;
598207607Simp	char	line[MAXLINE];
5991590Srgrimes
6001590Srgrimes	if (argc < 2) {
601207607Simp		strcpy(line, "Packet timeout ");
6021590Srgrimes		printf("(value) ");
60313068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
604207607Simp		makeargv(line);
6051590Srgrimes		argc = margc;
6061590Srgrimes		argv = margv;
6071590Srgrimes	}
6081590Srgrimes	if (argc != 2) {
6091590Srgrimes		printf("usage: %s value\n", argv[0]);
6101590Srgrimes		return;
6111590Srgrimes	}
6121590Srgrimes	t = atoi(argv[1]);
613207607Simp	if (t < 0) {
6141590Srgrimes		printf("%s: bad value\n", argv[1]);
615207607Simp		return;
616207607Simp	}
617207607Simp
618207607Simp	settimeouts(t, timeoutnetwork, maxtimeouts);
6191590Srgrimes}
6201590Srgrimes
621207607Simpstatic void
622207607Simpsettimeoutnetwork(int argc, char *argv[])
6231590Srgrimes{
6241590Srgrimes	int t;
625207607Simp	char	line[MAXLINE];
6261590Srgrimes
6271590Srgrimes	if (argc < 2) {
628207607Simp		strcpy(line, "Network timeout ");
6291590Srgrimes		printf("(value) ");
63013068Sjoerg		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
631207607Simp		makeargv(line);
6321590Srgrimes		argc = margc;
6331590Srgrimes		argv = margv;
6341590Srgrimes	}
6351590Srgrimes	if (argc != 2) {
6361590Srgrimes		printf("usage: %s value\n", argv[0]);
6371590Srgrimes		return;
6381590Srgrimes	}
6391590Srgrimes	t = atoi(argv[1]);
640207607Simp	if (t < 0) {
6411590Srgrimes		printf("%s: bad value\n", argv[1]);
642207607Simp		return;
643207607Simp	}
644207607Simp
645207607Simp	settimeouts(timeoutpacket, t, maxtimeouts);
6461590Srgrimes}
6471590Srgrimes
648207607Simpstatic void
649207607Simpshowstatus(int argc __unused, char *argv[] __unused)
6501590Srgrimes{
651207607Simp
652207607Simp	printf("Remote host: %s\n",
653207607Simp	    connected ? hostname : "none specified yet");
654207607Simp	printf("RFC2347 Options support: %s\n",
655207607Simp	    options_rfc_enabled ? "enabled" : "disabled");
656207607Simp	printf("Non-RFC defined options support: %s\n",
657207607Simp	    options_extra_enabled ? "enabled" : "disabled");
658207607Simp	printf("Mode: %s\n", mode);
659207607Simp	printf("Verbose: %s\n", verbose ? "on" : "off");
660207607Simp	printf("Debug: %s\n", debug_show(debug));
661207607Simp	printf("Artificial packetloss: %d in 100 packets\n",
662207607Simp	    packetdroppercentage);
663207607Simp	printf("Segment size: %d bytes\n", segsize);
664207607Simp	printf("Network timeout: %d seconds\n", timeoutpacket);
665207607Simp	printf("Maximum network timeout: %d seconds\n", timeoutnetwork);
666207607Simp	printf("Maximum timeouts: %d \n", maxtimeouts);
6671590Srgrimes}
6681590Srgrimes
669207607Simpstatic void
670183858Sdelphijintr(int dummy __unused)
6711590Srgrimes{
6721590Srgrimes
6731590Srgrimes	signal(SIGALRM, SIG_IGN);
6741590Srgrimes	alarm(0);
6751590Srgrimes	longjmp(toplevel, -1);
6761590Srgrimes}
6771590Srgrimes
678207607Simpstatic char *
679183858Sdelphijtail(char *filename)
6801590Srgrimes{
68187708Smarkm	char *s;
6828874Srgrimes
6831590Srgrimes	while (*filename) {
684229403Sed		s = strrchr(filename, '/');
6851590Srgrimes		if (s == NULL)
6861590Srgrimes			break;
6871590Srgrimes		if (s[1])
6881590Srgrimes			return (s + 1);
6891590Srgrimes		*s = '\0';
6901590Srgrimes	}
6911590Srgrimes	return (filename);
6921590Srgrimes}
6931590Srgrimes
69485120Smdoddstatic const char *
695213099Smariuscommand_prompt(void)
69685155Sbde{
69785155Sbde
69885120Smdodd	return ("tftp> ");
69985120Smdodd}
70085120Smdodd
7011590Srgrimes/*
7021590Srgrimes * Command parser.
7031590Srgrimes */
70418286Sbdestatic void
705183858Sdelphijcommand(void)
7061590Srgrimes{
70785155Sbde	HistEvent he;
70887708Smarkm	struct cmd *c;
70985155Sbde	static EditLine *el;
71085155Sbde	static History *hist;
71185155Sbde	const char *bp;
71213068Sjoerg	char *cp;
71387708Smarkm	int len, num, vrbose;
714207607Simp	char	line[MAXLINE];
7151590Srgrimes
71687708Smarkm	vrbose = isatty(0);
71787708Smarkm	if (vrbose) {
71885120Smdodd		el = el_init("tftp", stdin, stdout, stderr);
71985120Smdodd		hist = history_init();
720151471Sstefanf		history(hist, &he, H_SETSIZE, 100);
72185120Smdodd		el_set(el, EL_HIST, history, hist);
72285120Smdodd		el_set(el, EL_EDITOR, "emacs");
72385120Smdodd		el_set(el, EL_PROMPT, command_prompt);
72485120Smdodd		el_set(el, EL_SIGNAL, 1);
72585120Smdodd		el_source(el, NULL);
72685120Smdodd	}
7271590Srgrimes	for (;;) {
72887708Smarkm		if (vrbose) {
72985120Smdodd                        if ((bp = el_gets(el, &num)) == NULL || num == 0)
73085120Smdodd                                exit(0);
73185120Smdodd                        len = (num > MAXLINE) ? MAXLINE : num;
73285120Smdodd                        memcpy(line, bp, len);
73385120Smdodd                        line[len] = '\0';
73485120Smdodd                        history(hist, &he, H_ENTER, bp);
73585120Smdodd		} else {
736207607Simp			line[0] = 0;
737230044Skevlo			if (fgets(line, sizeof line , stdin) == NULL) {
73885120Smdodd				if (feof(stdin)) {
73996433Sbsd					exit(txrx_error);
74085120Smdodd				} else {
74185120Smdodd					continue;
74285120Smdodd				}
7431590Srgrimes			}
7441590Srgrimes		}
74513068Sjoerg		if ((cp = strchr(line, '\n')))
74613068Sjoerg			*cp = '\0';
7471590Srgrimes		if (line[0] == 0)
7481590Srgrimes			continue;
749207607Simp		makeargv(line);
7501590Srgrimes		if (margc == 0)
7511590Srgrimes			continue;
7521590Srgrimes		c = getcmd(margv[0]);
7531590Srgrimes		if (c == (struct cmd *)-1) {
7541590Srgrimes			printf("?Ambiguous command\n");
7551590Srgrimes			continue;
7561590Srgrimes		}
7571590Srgrimes		if (c == 0) {
7581590Srgrimes			printf("?Invalid command\n");
7591590Srgrimes			continue;
7601590Srgrimes		}
7611590Srgrimes		(*c->handler)(margc, margv);
7621590Srgrimes	}
7631590Srgrimes}
7641590Srgrimes
765207607Simpstatic struct cmd *
766183858Sdelphijgetcmd(char *name)
7671590Srgrimes{
76887708Smarkm	const char *p, *q;
76987708Smarkm	struct cmd *c, *found;
77087708Smarkm	int nmatches, longest;
7711590Srgrimes
7721590Srgrimes	longest = 0;
7731590Srgrimes	nmatches = 0;
7741590Srgrimes	found = 0;
7751590Srgrimes	for (c = cmdtab; (p = c->name) != NULL; c++) {
7761590Srgrimes		for (q = name; *q == *p++; q++)
7771590Srgrimes			if (*q == 0)		/* exact match? */
7781590Srgrimes				return (c);
7791590Srgrimes		if (!*q) {			/* the name was a prefix */
7801590Srgrimes			if (q - name > longest) {
7811590Srgrimes				longest = q - name;
7821590Srgrimes				nmatches = 1;
7831590Srgrimes				found = c;
7841590Srgrimes			} else if (q - name == longest)
7851590Srgrimes				nmatches++;
7861590Srgrimes		}
7871590Srgrimes	}
7881590Srgrimes	if (nmatches > 1)
7891590Srgrimes		return ((struct cmd *)-1);
7901590Srgrimes	return (found);
7911590Srgrimes}
7921590Srgrimes
7931590Srgrimes/*
7941590Srgrimes * Slice a string up into argc/argv.
7951590Srgrimes */
7961590Srgrimesstatic void
797207607Simpmakeargv(char *line)
7981590Srgrimes{
79987708Smarkm	char *cp;
80087708Smarkm	char **argp = margv;
8011590Srgrimes
8021590Srgrimes	margc = 0;
803207607Simp	if ((cp = strchr(line, '\n')) != NULL)
80413068Sjoerg		*cp = '\0';
805207607Simp	for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) {
8061590Srgrimes		while (isspace(*cp))
8071590Srgrimes			cp++;
8081590Srgrimes		if (*cp == '\0')
8091590Srgrimes			break;
8101590Srgrimes		*argp++ = cp;
8111590Srgrimes		margc += 1;
8121590Srgrimes		while (*cp != '\0' && !isspace(*cp))
8131590Srgrimes			cp++;
8141590Srgrimes		if (*cp == '\0')
8151590Srgrimes			break;
8161590Srgrimes		*cp++ = '\0';
8171590Srgrimes	}
8181590Srgrimes	*argp++ = 0;
8191590Srgrimes}
8201590Srgrimes
821207607Simpstatic void
822183858Sdelphijquit(int argc __unused, char *argv[] __unused)
8231590Srgrimes{
824207607Simp
82596433Sbsd	exit(txrx_error);
8261590Srgrimes}
8271590Srgrimes
8281590Srgrimes/*
8291590Srgrimes * Help command.
8301590Srgrimes */
831207607Simpstatic void
832183858Sdelphijhelp(int argc, char *argv[])
8331590Srgrimes{
83487708Smarkm	struct cmd *c;
8351590Srgrimes
8361590Srgrimes	if (argc == 1) {
8371590Srgrimes		printf("Commands may be abbreviated.  Commands are:\n\n");
8381590Srgrimes		for (c = cmdtab; c->name; c++)
8391590Srgrimes			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
840207607Simp
841207607Simp		printf("\n[-] : You shouldn't use these ones anymore.\n");
842223135Srodrigc		printf("[*] : RFC2347 options support required.\n");
843223135Srodrigc		printf("[**] : Non-standard RFC2347 option.\n");
8441590Srgrimes		return;
8451590Srgrimes	}
8461590Srgrimes	while (--argc > 0) {
84787708Smarkm		char *arg;
8481590Srgrimes		arg = *++argv;
8491590Srgrimes		c = getcmd(arg);
8501590Srgrimes		if (c == (struct cmd *)-1)
851207607Simp			printf("?Ambiguous help command: %s\n", arg);
8521590Srgrimes		else if (c == (struct cmd *)0)
853207607Simp			printf("?Invalid help command: %s\n", arg);
8541590Srgrimes		else
8551590Srgrimes			printf("%s\n", c->help);
8561590Srgrimes	}
8571590Srgrimes}
8581590Srgrimes
859207607Simpstatic void
860207607Simpsetverbose(int argc __unused, char *argv[] __unused)
8611590Srgrimes{
8621590Srgrimes
8631590Srgrimes	verbose = !verbose;
8641590Srgrimes	printf("Verbose mode %s.\n", verbose ? "on" : "off");
8651590Srgrimes}
866207607Simp
867207607Simpstatic void
868207607Simpsetoptions(int argc, char *argv[])
869207607Simp{
870207607Simp
871207607Simp	if (argc == 2) {
872207607Simp		if (strcasecmp(argv[1], "enable") == 0 ||
873207607Simp		    strcasecmp(argv[1], "on") == 0) {
874207607Simp			options_extra_enabled = 1;
875207607Simp			options_rfc_enabled = 1;
876207607Simp		}
877207607Simp		if (strcasecmp(argv[1], "disable") == 0 ||
878207607Simp		    strcasecmp(argv[1], "off") == 0) {
879207607Simp			options_extra_enabled = 0;
880207607Simp			options_rfc_enabled = 0;
881207607Simp		}
882207607Simp		if (strcasecmp(argv[1], "extra") == 0)
883207607Simp			options_extra_enabled = !options_extra_enabled;
884207607Simp	}
885207607Simp	printf("Support for RFC2347 style options are now %s.\n",
886207607Simp	    options_rfc_enabled ? "enabled" : "disabled");
887207607Simp	printf("Support for non-RFC defined options are now %s.\n",
888207607Simp	    options_extra_enabled ? "enabled" : "disabled");
889207607Simp
890207607Simp	printf("\nThe following options are available:\n"
891207607Simp	    "\toptions on	: enable support for RFC2347 style options\n"
892207607Simp	    "\toptions off	: disable support for RFC2347 style options\n"
893207607Simp	    "\toptions extra	: toggle support for non-RFC defined options\n"
894207607Simp	);
895207607Simp}
896207607Simp
897207607Simpstatic void
898207607Simpsetrollover(int argc, char *argv[])
899207607Simp{
900207607Simp
901207607Simp	if (argc == 2) {
902207607Simp		if (strcasecmp(argv[1], "never") == 0 ||
903207607Simp		    strcasecmp(argv[1], "none") == 0) {
904207607Simp			free(options[OPT_ROLLOVER].o_request);
905207607Simp			options[OPT_ROLLOVER].o_request = NULL;
906207607Simp		}
907207607Simp		if (strcasecmp(argv[1], "1") == 0) {
908207607Simp			free(options[OPT_ROLLOVER].o_request);
909207607Simp			options[OPT_ROLLOVER].o_request = strdup("1");
910207607Simp		}
911207607Simp		if (strcasecmp(argv[1], "0") == 0) {
912207607Simp			free(options[OPT_ROLLOVER].o_request);
913207607Simp			options[OPT_ROLLOVER].o_request = strdup("0");
914207607Simp		}
915207607Simp	}
916207607Simp	printf("Support for the rollover options is %s.\n",
917207607Simp	    options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled");
918207607Simp	if (options[OPT_ROLLOVER].o_request != NULL)
919207607Simp		printf("Block rollover will be to block %s.\n",
920207607Simp		    options[OPT_ROLLOVER].o_request);
921207607Simp
922207607Simp
923207607Simp	printf("\nThe following rollover options are available:\n"
924207607Simp	    "\trollover 0	: rollover to block zero (default)\n"
925207607Simp	    "\trollover 1	: rollover to block one\n"
926207607Simp	    "\trollover never	: do not support the rollover option\n"
927207607Simp	    "\trollover none	: do not support the rollover option\n"
928207607Simp	);
929207607Simp}
930207607Simp
931207607Simpstatic void
932207607Simpsetdebug(int argc, char *argv[])
933207607Simp{
934207607Simp	int i;
935207607Simp
936207607Simp	if (argc != 1) {
937207607Simp		i = 1;
938207607Simp		while (i < argc)
939207607Simp			debug ^= debug_find(argv[i++]);
940207607Simp	}
941207607Simp	printf("The following debugging is enabled: %s\n", debug_show(debug));
942207607Simp
943207607Simp	printf("\nThe following debugs are available:\n");
944207607Simp	i = 0;
945207607Simp	while (debugs[i].name != NULL) {
946207607Simp		printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc);
947207607Simp		i++;
948207607Simp	}
949207607Simp}
950207607Simp
951207607Simpstatic void
952207607Simpsetblocksize(int argc, char *argv[])
953207607Simp{
954207607Simp
955207607Simp	if (!options_rfc_enabled)
956207607Simp		printf("RFC2347 style options are not enabled "
957222534Simp		    "(but proceeding anyway)\n");
958207607Simp
959207607Simp	if (argc != 1) {
960207607Simp		int size = atoi(argv[1]);
961207607Simp		size_t max;
962213099Smarius		u_long maxdgram;
963207607Simp
964213099Smarius		max = sizeof(maxdgram);
965207607Simp		if (sysctlbyname("net.inet.udp.maxdgram",
966213099Smarius			&maxdgram, &max, NULL, 0) < 0) {
967207607Simp			perror("sysctl: net.inet.udp.maxdgram");
968207607Simp			return;
969207607Simp		}
970207607Simp
971207607Simp		if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
972207607Simp			printf("Blocksize should be between %d and %d bytes.\n",
973207607Simp				BLKSIZE_MIN, BLKSIZE_MAX);
974207607Simp			return;
975213099Smarius		} else if (size > (int)maxdgram - 4) {
976213099Smarius			printf("Blocksize can't be bigger than %ld bytes due "
977207607Simp			    "to the net.inet.udp.maxdgram sysctl limitation.\n",
978213099Smarius			    maxdgram - 4);
979207607Simp			asprintf(&options[OPT_BLKSIZE].o_request,
980213099Smarius			    "%ld", maxdgram - 4);
981207607Simp		} else {
982207607Simp			asprintf(&options[OPT_BLKSIZE].o_request, "%d", size);
983207607Simp		}
984207607Simp	}
985207607Simp	printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request);
986207607Simp}
987207607Simp
988207607Simpstatic void
989207607Simpsetblocksize2(int argc, char *argv[])
990207607Simp{
991207607Simp
992207607Simp	if (!options_rfc_enabled || !options_extra_enabled)
993207607Simp		printf(
994207607Simp		    "RFC2347 style or non-RFC defined options are not enabled "
995222534Simp		    "(but proceeding anyway)\n");
996207607Simp
997207607Simp	if (argc != 1) {
998207607Simp		int size = atoi(argv[1]);
999207607Simp		int i;
1000207607Simp		size_t max;
1001213099Smarius		u_long maxdgram;
1002207607Simp
1003207607Simp		int sizes[] = {
1004207607Simp			8, 16, 32, 64, 128, 256, 512, 1024,
1005207607Simp			2048, 4096, 8192, 16384, 32768, 0
1006207607Simp		};
1007207607Simp
1008213099Smarius		max = sizeof(maxdgram);
1009207607Simp		if (sysctlbyname("net.inet.udp.maxdgram",
1010213099Smarius			&maxdgram, &max, NULL, 0) < 0) {
1011207607Simp			perror("sysctl: net.inet.udp.maxdgram");
1012207607Simp			return;
1013207607Simp		}
1014207607Simp
1015207607Simp		for (i = 0; sizes[i] != 0; i++) {
1016207607Simp			if (sizes[i] == size) break;
1017207607Simp		}
1018207607Simp		if (sizes[i] == 0) {
1019207607Simp			printf("Blocksize2 should be a power of two between "
1020207607Simp			    "8 and 32768.\n");
1021207607Simp			return;
1022207607Simp		}
1023207607Simp
1024207607Simp		if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
1025207607Simp			printf("Blocksize2 should be between "
1026207607Simp			    "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX);
1027207607Simp			return;
1028213099Smarius		} else if (size > (int)maxdgram - 4) {
1029213099Smarius			printf("Blocksize2 can't be bigger than %ld bytes due "
1030207607Simp			    "to the net.inet.udp.maxdgram sysctl limitation.\n",
1031213099Smarius			    maxdgram - 4);
1032207607Simp			for (i = 0; sizes[i+1] != 0; i++) {
1033213099Smarius				if ((int)maxdgram < sizes[i+1]) break;
1034207607Simp			}
1035207607Simp			asprintf(&options[OPT_BLKSIZE2].o_request,
1036207607Simp			    "%d", sizes[i]);
1037207607Simp		} else {
1038207607Simp			asprintf(&options[OPT_BLKSIZE2].o_request, "%d", size);
1039207607Simp		}
1040207607Simp	}
1041207607Simp	printf("Blocksize2 is now %s bytes.\n",
1042207607Simp	    options[OPT_BLKSIZE2].o_request);
1043207607Simp}
1044207607Simp
1045207607Simpstatic void
1046207607Simpsetpacketdrop(int argc, char *argv[])
1047207607Simp{
1048207607Simp
1049207607Simp	if (argc != 1)
1050207607Simp		packetdroppercentage = atoi(argv[1]);
1051207607Simp
1052207607Simp	printf("Randomly %d in 100 packets will be dropped\n",
1053207607Simp	    packetdroppercentage);
1054207607Simp}
1055