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