1/*	$NetBSD: main.c,v 1.31 2012/03/20 20:34:59 matt Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35 The Regents of the University of California.  All rights reserved.");
36#if 0
37static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
38#else
39__RCSID("$NetBSD: main.c,v 1.31 2012/03/20 20:34:59 matt Exp $");
40#endif
41#endif /* not lint */
42
43/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
44
45/*
46 * TFTP User Program -- Command Interface.
47 */
48#include <sys/types.h>
49#include <sys/param.h>
50#include <sys/socket.h>
51
52#include <netinet/in.h>
53
54#include <arpa/inet.h>
55#include <arpa/tftp.h>
56
57#include <ctype.h>
58#include <fcntl.h>
59#include <err.h>
60#include <errno.h>
61#include <netdb.h>
62#include <setjmp.h>
63#include <signal.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <unistd.h>
68
69#include "extern.h"
70
71#define	TIMEOUT		5		/* secs between rexmt's */
72#define	LBUFLEN		200		/* size of input buffer */
73
74struct	sockaddr_storage peeraddr;
75int	f;
76int	mf;
77int	trace;
78int	verbose;
79int	tsize=0;
80int	tout=0;
81size_t	def_blksize = SEGSIZE;
82size_t	blksize = SEGSIZE;
83in_addr_t	mcaddr = INADDR_NONE;
84uint16_t	mcport;
85u_int	def_rexmtval = TIMEOUT;
86u_int	rexmtval = TIMEOUT;
87ushort	mcmasterslave;
88int	maxtimeout = 5 * TIMEOUT;
89
90jmp_buf	toplevel;
91
92static int	connected;
93static char	mode[32];
94static char	line[LBUFLEN];
95static int	margc;
96static char	*margv[20];
97static const	char *prompt = "tftp";
98static char    hostname[MAXHOSTNAMELEN];
99
100static void	get(int, char **);
101static void	help(int, char **);
102static void	modecmd(int, char **);
103static void	put(int, char **);
104static __dead void quit(int, char **);
105static void	setascii(int, char **);
106static void	setbinary(int, char **);
107static void	setpeer0(const char *, const char *);
108static void	setpeer(int, char **);
109static void	setrexmt(int, char **);
110static void	settimeout(int, char **);
111static void	settrace(int, char **);
112static void	setverbose(int, char **);
113static void	setblksize(int, char **);
114static void	settsize(int, char **);
115static void	settimeoutopt(int, char **);
116static void	status(int, char **);
117static char	*tail(char *);
118static __dead void intr(int);
119static const	struct cmd *getcmd(const char *);
120
121static __dead void command(void);
122
123static void getUsage(char *);
124static void makeargv(void);
125static void putUsage(const char *);
126static void settftpmode(const char *);
127
128#define HELPINDENT sizeof("connect")
129
130struct cmd {
131	const char *name;
132	const char *help;
133	void	(*handler)(int, char **);
134};
135
136static const char vhelp[] = "toggle verbose mode";
137static const char thelp[] = "toggle packet tracing";
138static const char tshelp[] = "toggle extended tsize option";
139static const char tohelp[] = "toggle extended timeout option";
140static const char blhelp[] = "set an alternative blocksize (def. 512)";
141static const char chelp[] = "connect to remote tftp";
142static const char qhelp[] = "exit tftp";
143static const char hhelp[] = "print help information";
144static const char shelp[] = "send file";
145static const char rhelp[] = "receive file";
146static const char mhelp[] = "set file transfer mode";
147static const char sthelp[] = "show current status";
148static const char xhelp[] = "set per-packet retransmission timeout";
149static const char ihelp[] = "set total retransmission timeout";
150static const char ashelp[] = "set mode to netascii";
151static const char bnhelp[] = "set mode to octet";
152
153static const struct cmd cmdtab[] = {
154	{ "connect",	chelp,		setpeer },
155	{ "mode",       mhelp,          modecmd },
156	{ "put",	shelp,		put },
157	{ "get",	rhelp,		get },
158	{ "quit",	qhelp,		quit },
159	{ "verbose",	vhelp,		setverbose },
160	{ "blksize",	blhelp,		setblksize },
161	{ "tsize",	tshelp,		settsize },
162	{ "trace",	thelp,		settrace },
163	{ "status",	sthelp,		status },
164	{ "binary",     bnhelp,         setbinary },
165	{ "ascii",      ashelp,         setascii },
166	{ "rexmt",	xhelp,		setrexmt },
167	{ "timeout",	ihelp,		settimeout },
168	{ "tout",	tohelp,		settimeoutopt },
169	{ "?",		hhelp,		help },
170	{ .name = NULL }
171};
172
173static struct	modes {
174	const char *m_name;
175	const char *m_mode;
176} modes[] = {
177	{ "ascii",	"netascii" },
178	{ "netascii",   "netascii" },
179	{ "binary",     "octet" },
180	{ "image",      "octet" },
181	{ "octet",     "octet" },
182/*      { "mail",       "mail" },       */
183	{ 0,		0 }
184};
185
186int
187main(int argc, char *argv[])
188{
189	int	c;
190
191	f = mf = -1;
192	(void)strlcpy(mode, "netascii", sizeof(mode));
193	(void)signal(SIGINT, intr);
194
195	setprogname(argv[0]);
196	while ((c = getopt(argc, argv, "e")) != -1) {
197		switch (c) {
198		case 'e':
199			blksize = MAXSEGSIZE;
200			(void)strlcpy(mode, "octet", sizeof(mode));
201			tsize = 1;
202			tout = 1;
203			break;
204		default:
205			(void)fprintf(stderr,
206			    "Usage: %s [-e] host-name [port]\n", getprogname());
207			exit(1);
208		}
209	}
210	argc -= optind;
211	argv += optind;
212
213	if (argc >= 1) {
214		if (setjmp(toplevel) != 0)
215			exit(0);
216		argc++;
217		argv--;
218		setpeer(argc, argv);
219	}
220	if (setjmp(toplevel) != 0)
221		(void)putchar('\n');
222	command();
223	return 0;
224}
225
226static void
227getmore(const char *cmd, const char *prm)
228{
229	(void)strlcpy(line, cmd, sizeof(line));
230	(void)printf("%s", prm);
231	(void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
232	makeargv();
233}
234
235static void
236setpeer0(const char *host, const char *port)
237{
238	struct addrinfo hints, *res0, *res;
239	int error, soopt;
240	struct sockaddr_storage ss;
241	const char *cause = "unknown";
242
243	if (connected) {
244		(void)close(f);
245		f = -1;
246	}
247	connected = 0;
248
249	(void)memset(&hints, 0, sizeof(hints));
250	hints.ai_family = PF_UNSPEC;
251	hints.ai_socktype = SOCK_DGRAM;
252	hints.ai_protocol = IPPROTO_UDP;
253	hints.ai_flags = AI_CANONNAME;
254	if (!port)
255		port = "tftp";
256	error = getaddrinfo(host, port, &hints, &res0);
257	if (error) {
258		warnx("%s", gai_strerror(error));
259		return;
260	}
261
262	for (res = res0; res; res = res->ai_next) {
263		if (res->ai_addrlen > sizeof(peeraddr))
264			continue;
265		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
266		if (f == -1) {
267			cause = "socket";
268			continue;
269		}
270
271		(void)memset(&ss, 0, sizeof(ss));
272		ss.ss_family = res->ai_family;
273		ss.ss_len = res->ai_addrlen;
274		if (bind(f, (struct sockaddr *)(void *)&ss,
275		    (socklen_t)ss.ss_len) == -1) {
276			cause = "bind";
277			(void)close(f);
278			f = -1;
279			continue;
280		}
281
282		break;
283	}
284
285	if (f >= 0) {
286		soopt = 65536;
287		if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt))
288		    == -1) {
289			(void)close(f);
290			f = -1;
291			cause = "setsockopt SNDBUF";
292		}
293		else if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt,
294		    sizeof(soopt)) == -1) {
295			(void)close(f);
296			f = -1;
297			cause = "setsockopt RCVBUF";
298		}
299	}
300
301	if (f == -1 || res == NULL)
302		warn("%s", cause);
303	else {
304		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
305		(void)memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
306		if (res->ai_canonname) {
307			(void)strlcpy(hostname, res->ai_canonname,
308			    sizeof(hostname));
309		} else
310			(void)strlcpy(hostname, host, sizeof(hostname));
311		connected = 1;
312	}
313
314	freeaddrinfo(res0);
315}
316
317static void
318setpeer(int argc, char *argv[])
319{
320
321	if (argc < 2) {
322		getmore("Connect ", "(to) ");
323		argc = margc;
324		argv = margv;
325	}
326	if (argc < 2 || argc > 3) {
327		(void)printf("Usage: %s [-e] host-name [port]\n",
328		    getprogname());
329		return;
330	}
331	if (argc == 2)
332		setpeer0(argv[1], NULL);
333	else
334		setpeer0(argv[1], argv[2]);
335}
336
337static void
338modecmd(int argc, char *argv[])
339{
340	struct modes *p;
341	const char *sep;
342
343	if (argc < 2) {
344		(void)printf("Using %s mode to transfer files.\n", mode);
345		return;
346	}
347	if (argc == 2) {
348		for (p = modes; p->m_name; p++)
349			if (strcmp(argv[1], p->m_name) == 0)
350				break;
351		if (p->m_name) {
352			settftpmode(p->m_mode);
353			return;
354		}
355		(void)printf("%s: unknown mode\n", argv[1]);
356		/* drop through and print Usage message */
357	}
358
359	(void)printf("Usage: %s [", argv[0]);
360	sep = " ";
361	for (p = modes; p->m_name; p++) {
362		(void)printf("%s%s", sep, p->m_name);
363		if (*sep == ' ')
364			sep = " | ";
365	}
366	(void)printf(" ]\n");
367	return;
368}
369
370static void
371/*ARGSUSED*/
372setbinary(int argc, char *argv[])
373{
374
375	settftpmode("octet");
376}
377
378static void
379/*ARGSUSED*/
380setascii(int argc, char *argv[])
381{
382
383	settftpmode("netascii");
384}
385
386static void
387settftpmode(const char *newmode)
388{
389	(void)strlcpy(mode, newmode, sizeof(mode));
390	if (verbose)
391		(void)printf("mode set to %s\n", mode);
392}
393
394
395/*
396 * Send file(s).
397 */
398static void
399put(int argc, char *argv[])
400{
401	int fd;
402	int n;
403	char *targ, *p;
404
405	if (argc < 2) {
406		getmore("send ", "(file) ");
407		argc = margc;
408		argv = margv;
409	}
410	if (argc < 2) {
411		putUsage(argv[0]);
412		return;
413	}
414	targ = argv[argc - 1];
415	if (strrchr(argv[argc - 1], ':')) {
416		char *cp;
417
418		for (n = 1; n < argc - 1; n++)
419			if (strchr(argv[n], ':')) {
420				putUsage(argv[0]);
421				return;
422			}
423		cp = argv[argc - 1];
424		targ = strrchr(cp, ':');
425		*targ++ = 0;
426		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
427			cp[strlen(cp) - 1] = '\0';
428			cp++;
429		}
430		setpeer0(cp, NULL);
431	}
432	if (!connected) {
433		(void)printf("No target machine specified.\n");
434		return;
435	}
436	if (argc < 4) {
437		char *cp = argc == 2 ? tail(targ) : argv[1];
438		fd = open(cp, O_RDONLY);
439		if (fd == -1) {
440			warn("%s", cp);
441			return;
442		}
443		if (verbose)
444			(void)printf("putting %s to %s:%s [%s]\n",
445				cp, hostname, targ, mode);
446		sendfile(fd, targ, mode);
447		return;
448	}
449				/* this assumes the target is a directory */
450				/* on a remote unix system.  hmmmm.  */
451	p = strchr(targ, '\0');
452	*p++ = '/';
453	for (n = 1; n < argc - 1; n++) {
454		(void)strcpy(p, tail(argv[n]));
455		fd = open(argv[n], O_RDONLY);
456		if (fd == -1) {
457			warn("%s", argv[n]);
458			continue;
459		}
460		if (verbose)
461			(void)printf("putting %s to %s:%s [%s]\n",
462				argv[n], hostname, targ, mode);
463		sendfile(fd, targ, mode);
464	}
465}
466
467static void
468putUsage(const char *s)
469{
470	(void)printf("Usage: %s file ... host:target, or\n", s);
471	(void)printf("       %s file ... target (when already connected)\n", s);
472}
473
474/*
475 * Receive file(s).
476 */
477static void
478get(int argc, char *argv[])
479{
480	int fd;
481	int n;
482	char *p;
483	char *src;
484
485	if (argc < 2) {
486		getmore("get ", "(files) ");
487		argc = margc;
488		argv = margv;
489	}
490	if (argc < 2) {
491		getUsage(argv[0]);
492		return;
493	}
494	if (!connected) {
495		for (n = 1; n < argc ; n++)
496			if (strrchr(argv[n], ':') == 0) {
497				getUsage(argv[0]);
498				return;
499			}
500	}
501	for (n = 1; n < argc ; n++) {
502		src = strrchr(argv[n], ':');
503		if (src == NULL)
504			src = argv[n];
505		else {
506			char *cp;
507			*src++ = 0;
508			cp = argv[n];
509			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
510				cp[strlen(cp) - 1] = '\0';
511				cp++;
512			}
513			setpeer0(cp, NULL);
514			if (!connected)
515				continue;
516		}
517		if (argc < 4) {
518			char *cp = argc == 3 ? argv[2] : tail(src);
519			fd = creat(cp, 0644);
520			if (fd == -1) {
521				warn("%s", cp);
522				return;
523			}
524			if (verbose)
525				(void)printf("getting from %s:%s to %s [%s]\n",
526					hostname, src, cp, mode);
527			recvfile(fd, src, mode);
528			break;
529		}
530		p = tail(src);         /* new .. jdg */
531		fd = creat(p, 0644);
532		if (fd == -1) {
533			warn("%s", p);
534			continue;
535		}
536		if (verbose)
537			(void)printf("getting from %s:%s to %s [%s]\n",
538				hostname, src, p, mode);
539		recvfile(fd, src, mode);
540	}
541}
542
543static void
544getUsage(char *s)
545{
546	(void)printf("Usage: %s host:file host:file ... file, or\n", s);
547	(void)printf("       %s file file ... file if connected\n", s);
548}
549
550void
551setblksize(int argc, char *argv[])
552{
553	int t;
554
555	if (argc < 2) {
556		getmore("blksize ", "(blksize) ");
557		argc = margc;
558		argv = margv;
559	}
560	if (argc != 2) {
561		(void)printf("Usage: %s value\n", argv[0]);
562		return;
563	}
564	t = atoi(argv[1]);
565	if (t < 8 || t > 65464)
566		(void)printf("%s: bad value\n", argv[1]);
567	else
568		blksize = t;
569}
570
571static void
572setrexmt(int argc, char *argv[])
573{
574	int t;
575
576	if (argc < 2) {
577		getmore("Rexmt-timeout ", "(value) ");
578		argc = margc;
579		argv = margv;
580	}
581	if (argc != 2) {
582		(void)printf("Usage: %s value\n", argv[0]);
583		return;
584	}
585	t = atoi(argv[1]);
586	if (t < 0)
587		(void)printf("%s: bad value\n", argv[1]);
588	else
589		rexmtval = t;
590}
591
592static void
593settimeout(int argc, char *argv[])
594{
595	int t;
596
597	if (argc < 2) {
598		getmore("Maximum-timeout ", "(value) ");
599		argc = margc;
600		argv = margv;
601	}
602	if (argc != 2) {
603		(void)printf("Usage: %s value\n", argv[0]);
604		return;
605	}
606	t = atoi(argv[1]);
607	if (t < 0)
608		(void)printf("%s: bad value\n", argv[1]);
609	else
610		maxtimeout = t;
611}
612
613static void
614/*ARGSUSED*/
615status(int argc, char *argv[])
616{
617	if (connected)
618		(void)printf("Connected to %s.\n", hostname);
619	else
620		(void)printf("Not connected.\n");
621	(void)printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
622		verbose ? "on" : "off", trace ? "on" : "off");
623	(void)printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
624		rexmtval, maxtimeout);
625}
626
627static void
628/*ARGSUSED*/
629intr(int dummy)
630{
631
632	(void)signal(SIGALRM, SIG_IGN);
633	(void)alarm(0);
634	longjmp(toplevel, -1);
635}
636
637static char *
638tail(char *filename)
639{
640	char *s;
641
642	while (*filename) {
643		s = strrchr(filename, '/');
644		if (s == NULL)
645			break;
646		if (s[1])
647			return s + 1;
648		*s = '\0';
649	}
650	return filename;
651}
652
653/*
654 * Command parser.
655 */
656static __dead void
657command(void)
658{
659	const struct cmd *c;
660
661	for (;;) {
662		(void)printf("%s> ", prompt);
663		if (fgets(line, LBUFLEN, stdin) == NULL) {
664			if (feof(stdin)) {
665				exit(0);
666			} else {
667				continue;
668			}
669		}
670		if (line[0] == '\0' || line[0] == '\n')
671			continue;
672		makeargv();
673		if (margc == 0)
674			continue;
675		c = getcmd(margv[0]);
676		if (c == (struct cmd *)-1) {
677			(void)printf("?Ambiguous command\n");
678			continue;
679		}
680		if (c == 0) {
681			(void)printf("?Invalid command\n");
682			continue;
683		}
684		(*c->handler)(margc, margv);
685	}
686}
687
688static const struct cmd *
689getcmd(const char *name)
690{
691	const char *p, *q;
692	const struct cmd *c, *found;
693	int nmatches, longest;
694
695	longest = 0;
696	nmatches = 0;
697	found = 0;
698	for (c = cmdtab; (p = c->name) != NULL; c++) {
699		for (q = name; *q == *p++; q++)
700			if (*q == 0)		/* exact match? */
701				return c;
702		if (!*q) {			/* the name was a prefix */
703			if (q - name > longest) {
704				longest = q - name;
705				nmatches = 1;
706				found = c;
707			} else if (q - name == longest)
708				nmatches++;
709		}
710	}
711	if (nmatches > 1)
712		return (struct cmd *)-1;
713	return found;
714}
715
716/*
717 * Slice a string up into argc/argv.
718 */
719static void
720makeargv(void)
721{
722	char *cp;
723	char **argp = margv;
724
725	margc = 0;
726	for (cp = line; *cp;) {
727		while (isspace((unsigned char)*cp))
728			cp++;
729		if (*cp == '\0')
730			break;
731		*argp++ = cp;
732		margc += 1;
733		while (*cp != '\0' && !isspace((unsigned char)*cp))
734			cp++;
735		if (*cp == '\0')
736			break;
737		*cp++ = '\0';
738	}
739	*argp++ = NULL;
740}
741
742static void
743/*ARGSUSED*/
744quit(int argc, char *argv[])
745{
746
747	exit(0);
748}
749
750/*
751 * Help command.
752 */
753static void
754help(int argc, char *argv[])
755{
756	const struct cmd *c;
757
758	if (argc == 1) {
759		(void)printf("Commands may be abbreviated.  Commands are:\n\n");
760		for (c = cmdtab; c->name; c++)
761			(void)printf("%-*s\t%s\n", (int)HELPINDENT, c->name,
762			    c->help);
763		return;
764	}
765	while (--argc > 0) {
766		char *arg;
767		arg = *++argv;
768		c = getcmd(arg);
769		if (c == (struct cmd *)-1)
770			(void)printf("?Ambiguous help command %s\n", arg);
771		else if (c == NULL)
772			(void)printf("?Invalid help command %s\n", arg);
773		else
774			(void)printf("%s\n", c->help);
775	}
776}
777
778static void
779/*ARGSUSED*/
780settrace(int argc, char **argv)
781{
782	trace = !trace;
783	(void)printf("Packet tracing %s.\n", trace ? "on" : "off");
784}
785
786static void
787/*ARGSUSED*/
788setverbose(int argc, char **argv)
789{
790	verbose = !verbose;
791	(void)printf("Verbose mode %s.\n", verbose ? "on" : "off");
792}
793
794static void
795/*ARGSUSED*/
796settsize(int argc, char **argv)
797{
798	tsize = !tsize;
799	(void)printf("Tsize mode %s.\n", tsize ? "on" : "off");
800}
801
802static void
803/*ARGSUSED*/
804settimeoutopt(int argc, char **argv)
805{
806	tout = !tout;
807	(void)printf("Timeout option %s.\n", tout ? "on" : "off");
808}
809