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