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