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