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