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