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