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