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