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