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