main.c revision 13068
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 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
41static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
45
46/*
47 * TFTP User Program -- Command Interface.
48 */
49#include <sys/types.h>
50#include <sys/socket.h>
51#include <sys/file.h>
52
53#include <netinet/in.h>
54
55#include <arpa/inet.h>
56
57#include <ctype.h>
58#include <errno.h>
59#include <netdb.h>
60#include <setjmp.h>
61#include <signal.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include <unistd.h>
66
67#include "extern.h"
68
69#define	TIMEOUT		5		/* secs between rexmt's */
70
71struct	sockaddr_in peeraddr;
72int	f;
73short   port;
74int	trace;
75int	verbose;
76int	connected;
77char	mode[32];
78char	line[200];
79int	margc;
80char	*margv[20];
81char	*prompt = "tftp";
82jmp_buf	toplevel;
83void	intr();
84struct	servent *sp;
85
86void	get __P((int, char **));
87void	help __P((int, char **));
88void	modecmd __P((int, char **));
89void	put __P((int, char **));
90void	quit __P((int, char **));
91void	setascii __P((int, char **));
92void	setbinary __P((int, char **));
93void	setpeer __P((int, char **));
94void	setrexmt __P((int, char **));
95void	settimeout __P((int, char **));
96void	settrace __P((int, char **));
97void	setverbose __P((int, char **));
98void	status __P((int, char **));
99
100static __dead void command __P((void));
101
102static void getusage __P((char *));
103static void makeargv __P((void));
104static void putusage __P((char *));
105static void settftpmode __P((char *));
106
107#define HELPINDENT (sizeof("connect"))
108
109struct cmd {
110	char	*name;
111	char	*help;
112	void	(*handler) __P((int, char **));
113};
114
115char	vhelp[] = "toggle verbose mode";
116char	thelp[] = "toggle packet tracing";
117char	chelp[] = "connect to remote tftp";
118char	qhelp[] = "exit tftp";
119char	hhelp[] = "print help information";
120char	shelp[] = "send file";
121char	rhelp[] = "receive file";
122char	mhelp[] = "set file transfer mode";
123char	sthelp[] = "show current status";
124char	xhelp[] = "set per-packet retransmission timeout";
125char	ihelp[] = "set total retransmission timeout";
126char    ashelp[] = "set mode to netascii";
127char    bnhelp[] = "set mode to octet";
128
129struct cmd cmdtab[] = {
130	{ "connect",	chelp,		setpeer },
131	{ "mode",       mhelp,          modecmd },
132	{ "put",	shelp,		put },
133	{ "get",	rhelp,		get },
134	{ "quit",	qhelp,		quit },
135	{ "verbose",	vhelp,		setverbose },
136	{ "trace",	thelp,		settrace },
137	{ "status",	sthelp,		status },
138	{ "binary",     bnhelp,         setbinary },
139	{ "ascii",      ashelp,         setascii },
140	{ "rexmt",	xhelp,		setrexmt },
141	{ "timeout",	ihelp,		settimeout },
142	{ "?",		hhelp,		help },
143	{ 0 }
144};
145
146struct	cmd *getcmd();
147char	*tail();
148char	*index();
149char	*rindex();
150
151int
152main(argc, argv)
153	int argc;
154	char *argv[];
155{
156	struct sockaddr_in sin;
157
158	sp = getservbyname("tftp", "udp");
159	if (sp == 0) {
160		fprintf(stderr, "tftp: udp/tftp: unknown service\n");
161		exit(1);
162	}
163	f = socket(AF_INET, SOCK_DGRAM, 0);
164	if (f < 0) {
165		perror("tftp: socket");
166		exit(3);
167	}
168	bzero((char *)&sin, sizeof(sin));
169	sin.sin_family = AF_INET;
170	if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
171		perror("tftp: bind");
172		exit(1);
173	}
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[100];
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 > 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, host->h_length);
211		strcpy(hostname, host->h_name);
212	} else {
213		peeraddr.sin_family = AF_INET;
214		peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
215		if (peeraddr.sin_addr.s_addr == -1) {
216			connected = 0;
217			printf("%s: unknown host\n", argv[1]);
218			return;
219		}
220		strcpy(hostname, argv[1]);
221	}
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, hp->h_length);
355		peeraddr.sin_family = hp->h_addrtype;
356		connected = 1;
357		strcpy(hostname, hp->h_name);
358	}
359	if (!connected) {
360		printf("No target machine specified.\n");
361		return;
362	}
363	if (argc < 4) {
364		cp = argc == 2 ? tail(targ) : argv[1];
365		fd = open(cp, O_RDONLY);
366		if (fd < 0) {
367			fprintf(stderr, "tftp: "); perror(cp);
368			return;
369		}
370		if (verbose)
371			printf("putting %s to %s:%s [%s]\n",
372				cp, hostname, targ, mode);
373		peeraddr.sin_port = port;
374		sendfile(fd, targ, mode);
375		return;
376	}
377				/* this assumes the target is a directory */
378				/* on a remote unix system.  hmmmm.  */
379	cp = index(targ, '\0');
380	*cp++ = '/';
381	for (n = 1; n < argc - 1; n++) {
382		strcpy(cp, tail(argv[n]));
383		fd = open(argv[n], O_RDONLY);
384		if (fd < 0) {
385			fprintf(stderr, "tftp: "); perror(argv[n]);
386			continue;
387		}
388		if (verbose)
389			printf("putting %s to %s:%s [%s]\n",
390				argv[n], hostname, targ, mode);
391		peeraddr.sin_port = port;
392		sendfile(fd, targ, mode);
393	}
394}
395
396static void
397putusage(s)
398	char *s;
399{
400	printf("usage: %s file ... host:target, or\n", s);
401	printf("       %s file ... target (when already connected)\n", s);
402}
403
404/*
405 * Receive file(s).
406 */
407void
408get(argc, argv)
409	int argc;
410	char *argv[];
411{
412	int fd;
413	register int n;
414	register char *cp;
415	char *src;
416
417	if (argc < 2) {
418		strcpy(line, "get ");
419		printf("(files) ");
420		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
421		makeargv();
422		argc = margc;
423		argv = margv;
424	}
425	if (argc < 2) {
426		getusage(argv[0]);
427		return;
428	}
429	if (!connected) {
430		for (n = 1; n < argc ; n++)
431			if (index(argv[n], ':') == 0) {
432				getusage(argv[0]);
433				return;
434			}
435	}
436	for (n = 1; n < argc ; n++) {
437		src = index(argv[n], ':');
438		if (src == NULL)
439			src = argv[n];
440		else {
441			struct hostent *hp;
442
443			*src++ = 0;
444			hp = gethostbyname(argv[n]);
445			if (hp == NULL) {
446				fprintf(stderr, "tftp: %s: ", argv[n]);
447				herror((char *)NULL);
448				continue;
449			}
450			bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
451			    hp->h_length);
452			peeraddr.sin_family = hp->h_addrtype;
453			connected = 1;
454			strcpy(hostname, hp->h_name);
455		}
456		if (argc < 4) {
457			cp = argc == 3 ? argv[2] : tail(src);
458			fd = creat(cp, 0644);
459			if (fd < 0) {
460				fprintf(stderr, "tftp: "); perror(cp);
461				return;
462			}
463			if (verbose)
464				printf("getting from %s:%s to %s [%s]\n",
465					hostname, src, cp, mode);
466			peeraddr.sin_port = port;
467			recvfile(fd, src, mode);
468			break;
469		}
470		cp = tail(src);         /* new .. jdg */
471		fd = creat(cp, 0644);
472		if (fd < 0) {
473			fprintf(stderr, "tftp: "); perror(cp);
474			continue;
475		}
476		if (verbose)
477			printf("getting from %s:%s to %s [%s]\n",
478				hostname, src, cp, mode);
479		peeraddr.sin_port = port;
480		recvfile(fd, src, mode);
481	}
482}
483
484static void
485getusage(s)
486	char *s;
487{
488	printf("usage: %s host:file host:file ... file, or\n", s);
489	printf("       %s file file ... file if connected\n", s);
490}
491
492int	rexmtval = TIMEOUT;
493
494void
495setrexmt(argc, argv)
496	int argc;
497	char *argv[];
498{
499	int t;
500
501	if (argc < 2) {
502		strcpy(line, "Rexmt-timeout ");
503		printf("(value) ");
504		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
505		makeargv();
506		argc = margc;
507		argv = margv;
508	}
509	if (argc != 2) {
510		printf("usage: %s value\n", argv[0]);
511		return;
512	}
513	t = atoi(argv[1]);
514	if (t < 0)
515		printf("%s: bad value\n", argv[1]);
516	else
517		rexmtval = t;
518}
519
520int	maxtimeout = 5 * TIMEOUT;
521
522void
523settimeout(argc, argv)
524	int argc;
525	char *argv[];
526{
527	int t;
528
529	if (argc < 2) {
530		strcpy(line, "Maximum-timeout ");
531		printf("(value) ");
532		fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
533		makeargv();
534		argc = margc;
535		argv = margv;
536	}
537	if (argc != 2) {
538		printf("usage: %s value\n", argv[0]);
539		return;
540	}
541	t = atoi(argv[1]);
542	if (t < 0)
543		printf("%s: bad value\n", argv[1]);
544	else
545		maxtimeout = t;
546}
547
548void
549status(argc, argv)
550	int argc;
551	char *argv[];
552{
553	if (connected)
554		printf("Connected to %s.\n", hostname);
555	else
556		printf("Not connected.\n");
557	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
558		verbose ? "on" : "off", trace ? "on" : "off");
559	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
560		rexmtval, maxtimeout);
561}
562
563void
564intr()
565{
566
567	signal(SIGALRM, SIG_IGN);
568	alarm(0);
569	longjmp(toplevel, -1);
570}
571
572char *
573tail(filename)
574	char *filename;
575{
576	register char *s;
577
578	while (*filename) {
579		s = rindex(filename, '/');
580		if (s == NULL)
581			break;
582		if (s[1])
583			return (s + 1);
584		*s = '\0';
585	}
586	return (filename);
587}
588
589/*
590 * Command parser.
591 */
592static __dead void
593command()
594{
595	register struct cmd *c;
596	char *cp;
597
598	for (;;) {
599		printf("%s> ", prompt);
600		if (fgets(line, sizeof line , stdin) == 0) {
601			if (feof(stdin)) {
602				exit(0);
603			} else {
604				continue;
605			}
606		}
607		if ((cp = strchr(line, '\n')))
608			*cp = '\0';
609		if (line[0] == 0)
610			continue;
611		makeargv();
612		if (margc == 0)
613			continue;
614		c = getcmd(margv[0]);
615		if (c == (struct cmd *)-1) {
616			printf("?Ambiguous command\n");
617			continue;
618		}
619		if (c == 0) {
620			printf("?Invalid command\n");
621			continue;
622		}
623		(*c->handler)(margc, margv);
624	}
625}
626
627struct cmd *
628getcmd(name)
629	register char *name;
630{
631	register char *p, *q;
632	register struct cmd *c, *found;
633	register int nmatches, longest;
634
635	longest = 0;
636	nmatches = 0;
637	found = 0;
638	for (c = cmdtab; (p = c->name) != NULL; c++) {
639		for (q = name; *q == *p++; q++)
640			if (*q == 0)		/* exact match? */
641				return (c);
642		if (!*q) {			/* the name was a prefix */
643			if (q - name > longest) {
644				longest = q - name;
645				nmatches = 1;
646				found = c;
647			} else if (q - name == longest)
648				nmatches++;
649		}
650	}
651	if (nmatches > 1)
652		return ((struct cmd *)-1);
653	return (found);
654}
655
656/*
657 * Slice a string up into argc/argv.
658 */
659static void
660makeargv()
661{
662	register char *cp;
663	register char **argp = margv;
664
665	margc = 0;
666	if ((cp = strchr(line, '\n')))
667		*cp = '\0';
668	for (cp = line; *cp;) {
669		while (isspace(*cp))
670			cp++;
671		if (*cp == '\0')
672			break;
673		*argp++ = cp;
674		margc += 1;
675		while (*cp != '\0' && !isspace(*cp))
676			cp++;
677		if (*cp == '\0')
678			break;
679		*cp++ = '\0';
680	}
681	*argp++ = 0;
682}
683
684void
685quit(argc, argv)
686	int argc;
687	char *argv[];
688{
689
690	exit(0);
691}
692
693/*
694 * Help command.
695 */
696void
697help(argc, argv)
698	int argc;
699	char *argv[];
700{
701	register struct cmd *c;
702
703	if (argc == 1) {
704		printf("Commands may be abbreviated.  Commands are:\n\n");
705		for (c = cmdtab; c->name; c++)
706			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
707		return;
708	}
709	while (--argc > 0) {
710		register char *arg;
711		arg = *++argv;
712		c = getcmd(arg);
713		if (c == (struct cmd *)-1)
714			printf("?Ambiguous help command %s\n", arg);
715		else if (c == (struct cmd *)0)
716			printf("?Invalid help command %s\n", arg);
717		else
718			printf("%s\n", c->help);
719	}
720}
721
722void
723settrace(argc, argv)
724	int argc;
725	char **argv;
726{
727	trace = !trace;
728	printf("Packet tracing %s.\n", trace ? "on" : "off");
729}
730
731void
732setverbose(argc, argv)
733	int argc;
734	char **argv;
735{
736	verbose = !verbose;
737	printf("Verbose mode %s.\n", verbose ? "on" : "off");
738}
739