Home | History | Annotate | Line # | Download | only in tftp
main.c revision 1.12.8.2
      1 /*	$NetBSD: main.c,v 1.12.8.2 2001/02/03 21:12:49 he Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1983, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
     39 	The Regents of the University of California.  All rights reserved.\n");
     40 #if 0
     41 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
     42 #else
     43 __RCSID("$NetBSD: main.c,v 1.12.8.2 2001/02/03 21:12:49 he Exp $");
     44 #endif
     45 #endif /* not lint */
     46 
     47 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
     48 
     49 /*
     50  * TFTP User Program -- Command Interface.
     51  */
     52 #include <sys/types.h>
     53 #include <sys/socket.h>
     54 
     55 #include <netinet/in.h>
     56 
     57 #include <arpa/inet.h>
     58 
     59 #include <ctype.h>
     60 #include <fcntl.h>
     61 #include <err.h>
     62 #include <errno.h>
     63 #include <netdb.h>
     64 #include <setjmp.h>
     65 #include <signal.h>
     66 #include <stdio.h>
     67 #include <stdlib.h>
     68 #include <string.h>
     69 #include <unistd.h>
     70 
     71 #include "extern.h"
     72 
     73 #define	TIMEOUT		5		/* secs between rexmt's */
     74 #define	LBUFLEN		200		/* size of input buffer */
     75 
     76 struct	sockaddr_storage peeraddr;
     77 int	f;
     78 int	trace;
     79 int	verbose;
     80 int	connected;
     81 char	mode[32];
     82 char	line[LBUFLEN];
     83 int	margc;
     84 char	*margv[20];
     85 char	*prompt = "tftp";
     86 jmp_buf	toplevel;
     87 
     88 void	get __P((int, char **));
     89 void	help __P((int, char **));
     90 void	modecmd __P((int, char **));
     91 void	put __P((int, char **));
     92 void	quit __P((int, char **));
     93 void	setascii __P((int, char **));
     94 void	setbinary __P((int, char **));
     95 void	setpeer0 __P((char *, char *));
     96 void	setpeer __P((int, char **));
     97 void	setrexmt __P((int, char **));
     98 void	settimeout __P((int, char **));
     99 void	settrace __P((int, char **));
    100 void	setverbose __P((int, char **));
    101 void	status __P((int, char **));
    102 char	*tail __P((char *));
    103 int	main __P((int, char *[]));
    104 void	intr __P((int));
    105 struct cmd *getcmd __P((char *));
    106 
    107 static __dead void command __P((void));
    108 
    109 static void getusage __P((char *));
    110 static void makeargv __P((void));
    111 static void putusage __P((char *));
    112 static void settftpmode __P((char *));
    113 
    114 #define HELPINDENT (sizeof("connect"))
    115 
    116 struct cmd {
    117 	char	*name;
    118 	char	*help;
    119 	void	(*handler) __P((int, char **));
    120 };
    121 
    122 char	vhelp[] = "toggle verbose mode";
    123 char	thelp[] = "toggle packet tracing";
    124 char	chelp[] = "connect to remote tftp";
    125 char	qhelp[] = "exit tftp";
    126 char	hhelp[] = "print help information";
    127 char	shelp[] = "send file";
    128 char	rhelp[] = "receive file";
    129 char	mhelp[] = "set file transfer mode";
    130 char	sthelp[] = "show current status";
    131 char	xhelp[] = "set per-packet retransmission timeout";
    132 char	ihelp[] = "set total retransmission timeout";
    133 char    ashelp[] = "set mode to netascii";
    134 char    bnhelp[] = "set mode to octet";
    135 
    136 struct cmd cmdtab[] = {
    137 	{ "connect",	chelp,		setpeer },
    138 	{ "mode",       mhelp,          modecmd },
    139 	{ "put",	shelp,		put },
    140 	{ "get",	rhelp,		get },
    141 	{ "quit",	qhelp,		quit },
    142 	{ "verbose",	vhelp,		setverbose },
    143 	{ "trace",	thelp,		settrace },
    144 	{ "status",	sthelp,		status },
    145 	{ "binary",     bnhelp,         setbinary },
    146 	{ "ascii",      ashelp,         setascii },
    147 	{ "rexmt",	xhelp,		setrexmt },
    148 	{ "timeout",	ihelp,		settimeout },
    149 	{ "?",		hhelp,		help },
    150 	{ 0 }
    151 };
    152 
    153 int
    154 main(argc, argv)
    155 	int argc;
    156 	char *argv[];
    157 {
    158 	f = -1;
    159 	strcpy(mode, "netascii");
    160 	signal(SIGINT, intr);
    161 	if (argc > 1) {
    162 		if (setjmp(toplevel) != 0)
    163 			exit(0);
    164 		setpeer(argc, argv);
    165 	}
    166 	if (setjmp(toplevel) != 0)
    167 		(void)putchar('\n');
    168 	command();
    169 	return (0);
    170 }
    171 
    172 char    hostname[100];
    173 
    174 void
    175 setpeer0(host, port)
    176 	char *host;
    177 	char *port;
    178 {
    179 	struct addrinfo hints, *res0, *res;
    180 	int error;
    181 	struct sockaddr_storage ss;
    182 	char *cause = "unknown";
    183 
    184 	if (connected) {
    185 		close(f);
    186 		f = -1;
    187 	}
    188 	connected = 0;
    189 
    190 	memset(&hints, 0, sizeof(hints));
    191 	hints.ai_family = PF_UNSPEC;
    192 	hints.ai_socktype = SOCK_DGRAM;
    193 	hints.ai_protocol = IPPROTO_UDP;
    194 	hints.ai_flags = AI_CANONNAME;
    195 	if (!port)
    196 		port = "tftp";
    197 	error = getaddrinfo(host, port, &hints, &res0);
    198 	if (error) {
    199 		warnx("%s", gai_strerror(error));
    200 		return;
    201 	}
    202 
    203 	for (res = res0; res; res = res->ai_next) {
    204 		if (res->ai_addrlen > sizeof(peeraddr))
    205 			continue;
    206 		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    207 		if (f < 0) {
    208 			cause = "socket";
    209 			continue;
    210 		}
    211 
    212 		memset(&ss, 0, sizeof(ss));
    213 		ss.ss_family = res->ai_family;
    214 		ss.ss_len = res->ai_addrlen;
    215 		if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
    216 			cause = "bind";
    217 			close(f);
    218 			f = -1;
    219 			continue;
    220 		}
    221 
    222 		break;
    223 	}
    224 
    225 	if (f < 0)
    226 		warn("%s", cause);
    227 	else {
    228 		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
    229 		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
    230 		if (res->ai_canonname) {
    231 			(void) strncpy(hostname, res->ai_canonname,
    232 				sizeof(hostname));
    233 		} else
    234 			(void) strncpy(hostname, host, sizeof(hostname));
    235 		hostname[sizeof(hostname)-1] = 0;
    236 		connected = 1;
    237 	}
    238 
    239 	freeaddrinfo(res0);
    240 }
    241 
    242 void
    243 setpeer(argc, argv)
    244 	int argc;
    245 	char *argv[];
    246 {
    247 
    248 	if (argc < 2) {
    249 		strcpy(line, "Connect ");
    250 		printf("(to) ");
    251 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    252 		makeargv();
    253 		argc = margc;
    254 		argv = margv;
    255 	}
    256 	if ((argc < 2) || (argc > 3)) {
    257 		printf("usage: %s host-name [port]\n", argv[0]);
    258 		return;
    259 	}
    260 	if (argc == 2)
    261 		setpeer0(argv[1], NULL);
    262 	else
    263 		setpeer0(argv[1], argv[2]);
    264 }
    265 
    266 struct	modes {
    267 	char *m_name;
    268 	char *m_mode;
    269 } modes[] = {
    270 	{ "ascii",	"netascii" },
    271 	{ "netascii",   "netascii" },
    272 	{ "binary",     "octet" },
    273 	{ "image",      "octet" },
    274 	{ "octet",     "octet" },
    275 /*      { "mail",       "mail" },       */
    276 	{ 0,		0 }
    277 };
    278 
    279 void
    280 modecmd(argc, argv)
    281 	int argc;
    282 	char *argv[];
    283 {
    284 	struct modes *p;
    285 	char *sep;
    286 
    287 	if (argc < 2) {
    288 		printf("Using %s mode to transfer files.\n", mode);
    289 		return;
    290 	}
    291 	if (argc == 2) {
    292 		for (p = modes; p->m_name; p++)
    293 			if (strcmp(argv[1], p->m_name) == 0)
    294 				break;
    295 		if (p->m_name) {
    296 			settftpmode(p->m_mode);
    297 			return;
    298 		}
    299 		printf("%s: unknown mode\n", argv[1]);
    300 		/* drop through and print usage message */
    301 	}
    302 
    303 	printf("usage: %s [", argv[0]);
    304 	sep = " ";
    305 	for (p = modes; p->m_name; p++) {
    306 		printf("%s%s", sep, p->m_name);
    307 		if (*sep == ' ')
    308 			sep = " | ";
    309 	}
    310 	printf(" ]\n");
    311 	return;
    312 }
    313 
    314 void
    315 setbinary(argc, argv)
    316 	int argc;
    317 	char *argv[];
    318 {
    319 
    320 	settftpmode("octet");
    321 }
    322 
    323 void
    324 setascii(argc, argv)
    325 	int argc;
    326 	char *argv[];
    327 {
    328 
    329 	settftpmode("netascii");
    330 }
    331 
    332 static void
    333 settftpmode(newmode)
    334 	char *newmode;
    335 {
    336 	strcpy(mode, newmode);
    337 	if (verbose)
    338 		printf("mode set to %s\n", mode);
    339 }
    340 
    341 
    342 /*
    343  * Send file(s).
    344  */
    345 void
    346 put(argc, argv)
    347 	int argc;
    348 	char *argv[];
    349 {
    350 	int fd;
    351 	int n;
    352 	char *cp, *targ;
    353 
    354 	if (argc < 2) {
    355 		strcpy(line, "send ");
    356 		printf("(file) ");
    357 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    358 		makeargv();
    359 		argc = margc;
    360 		argv = margv;
    361 	}
    362 	if (argc < 2) {
    363 		putusage(argv[0]);
    364 		return;
    365 	}
    366 	targ = argv[argc - 1];
    367 	if (strrchr(argv[argc - 1], ':')) {
    368 		char *cp;
    369 
    370 		for (n = 1; n < argc - 1; n++)
    371 			if (strchr(argv[n], ':')) {
    372 				putusage(argv[0]);
    373 				return;
    374 			}
    375 		cp = argv[argc - 1];
    376 		targ = strrchr(cp, ':');
    377 		*targ++ = 0;
    378 		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
    379 			cp[strlen(cp) - 1] = '\0';
    380 			cp++;
    381 		}
    382 		setpeer0(cp, NULL);
    383 	}
    384 	if (!connected) {
    385 		printf("No target machine specified.\n");
    386 		return;
    387 	}
    388 	if (argc < 4) {
    389 		cp = argc == 2 ? tail(targ) : argv[1];
    390 		fd = open(cp, O_RDONLY);
    391 		if (fd < 0) {
    392 			warn("%s", cp);
    393 			return;
    394 		}
    395 		if (verbose)
    396 			printf("putting %s to %s:%s [%s]\n",
    397 				cp, hostname, targ, mode);
    398 		sendfile(fd, targ, mode);
    399 		return;
    400 	}
    401 				/* this assumes the target is a directory */
    402 				/* on a remote unix system.  hmmmm.  */
    403 	cp = strchr(targ, '\0');
    404 	*cp++ = '/';
    405 	for (n = 1; n < argc - 1; n++) {
    406 		strcpy(cp, tail(argv[n]));
    407 		fd = open(argv[n], O_RDONLY);
    408 		if (fd < 0) {
    409 			warn("%s", argv[n]);
    410 			continue;
    411 		}
    412 		if (verbose)
    413 			printf("putting %s to %s:%s [%s]\n",
    414 				argv[n], hostname, targ, mode);
    415 		sendfile(fd, targ, mode);
    416 	}
    417 }
    418 
    419 static void
    420 putusage(s)
    421 	char *s;
    422 {
    423 	printf("usage: %s file ... host:target, or\n", s);
    424 	printf("       %s file ... target (when already connected)\n", s);
    425 }
    426 
    427 /*
    428  * Receive file(s).
    429  */
    430 void
    431 get(argc, argv)
    432 	int argc;
    433 	char *argv[];
    434 {
    435 	int fd;
    436 	int n;
    437 	char *cp;
    438 	char *src;
    439 
    440 	if (argc < 2) {
    441 		strcpy(line, "get ");
    442 		printf("(files) ");
    443 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    444 		makeargv();
    445 		argc = margc;
    446 		argv = margv;
    447 	}
    448 	if (argc < 2) {
    449 		getusage(argv[0]);
    450 		return;
    451 	}
    452 	if (!connected) {
    453 		for (n = 1; n < argc ; n++)
    454 			if (strrchr(argv[n], ':') == 0) {
    455 				getusage(argv[0]);
    456 				return;
    457 			}
    458 	}
    459 	for (n = 1; n < argc ; n++) {
    460 		src = strrchr(argv[n], ':');
    461 		if (src == NULL)
    462 			src = argv[n];
    463 		else {
    464 			char *cp;
    465 			*src++ = 0;
    466 			cp = argv[n];
    467 			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
    468 				cp[strlen(cp) - 1] = '\0';
    469 				cp++;
    470 			}
    471 			setpeer0(cp, NULL);
    472 			if (!connected)
    473 				continue;
    474 		}
    475 		if (argc < 4) {
    476 			cp = argc == 3 ? argv[2] : tail(src);
    477 			fd = creat(cp, 0644);
    478 			if (fd < 0) {
    479 				warn("%s", cp);
    480 				return;
    481 			}
    482 			if (verbose)
    483 				printf("getting from %s:%s to %s [%s]\n",
    484 					hostname, src, cp, mode);
    485 			recvfile(fd, src, mode);
    486 			break;
    487 		}
    488 		cp = tail(src);         /* new .. jdg */
    489 		fd = creat(cp, 0644);
    490 		if (fd < 0) {
    491 			warn("%s", cp);
    492 			continue;
    493 		}
    494 		if (verbose)
    495 			printf("getting from %s:%s to %s [%s]\n",
    496 				hostname, src, cp, mode);
    497 		recvfile(fd, src, mode);
    498 	}
    499 }
    500 
    501 static void
    502 getusage(s)
    503 	char *s;
    504 {
    505 	printf("usage: %s host:file host:file ... file, or\n", s);
    506 	printf("       %s file file ... file if connected\n", s);
    507 }
    508 
    509 int	rexmtval = TIMEOUT;
    510 
    511 void
    512 setrexmt(argc, argv)
    513 	int argc;
    514 	char *argv[];
    515 {
    516 	int t;
    517 
    518 	if (argc < 2) {
    519 		strcpy(line, "Rexmt-timeout ");
    520 		printf("(value) ");
    521 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    522 		makeargv();
    523 		argc = margc;
    524 		argv = margv;
    525 	}
    526 	if (argc != 2) {
    527 		printf("usage: %s value\n", argv[0]);
    528 		return;
    529 	}
    530 	t = atoi(argv[1]);
    531 	if (t < 0)
    532 		printf("%s: bad value\n", argv[1]);
    533 	else
    534 		rexmtval = t;
    535 }
    536 
    537 int	maxtimeout = 5 * TIMEOUT;
    538 
    539 void
    540 settimeout(argc, argv)
    541 	int argc;
    542 	char *argv[];
    543 {
    544 	int t;
    545 
    546 	if (argc < 2) {
    547 		strcpy(line, "Maximum-timeout ");
    548 		printf("(value) ");
    549 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    550 		makeargv();
    551 		argc = margc;
    552 		argv = margv;
    553 	}
    554 	if (argc != 2) {
    555 		printf("usage: %s value\n", argv[0]);
    556 		return;
    557 	}
    558 	t = atoi(argv[1]);
    559 	if (t < 0)
    560 		printf("%s: bad value\n", argv[1]);
    561 	else
    562 		maxtimeout = t;
    563 }
    564 
    565 void
    566 status(argc, argv)
    567 	int argc;
    568 	char *argv[];
    569 {
    570 	if (connected)
    571 		printf("Connected to %s.\n", hostname);
    572 	else
    573 		printf("Not connected.\n");
    574 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
    575 		verbose ? "on" : "off", trace ? "on" : "off");
    576 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
    577 		rexmtval, maxtimeout);
    578 }
    579 
    580 void
    581 intr(dummy)
    582 	int dummy;
    583 {
    584 
    585 	signal(SIGALRM, SIG_IGN);
    586 	alarm(0);
    587 	longjmp(toplevel, -1);
    588 }
    589 
    590 char *
    591 tail(filename)
    592 	char *filename;
    593 {
    594 	char *s;
    595 
    596 	while (*filename) {
    597 		s = strrchr(filename, '/');
    598 		if (s == NULL)
    599 			break;
    600 		if (s[1])
    601 			return (s + 1);
    602 		*s = '\0';
    603 	}
    604 	return (filename);
    605 }
    606 
    607 /*
    608  * Command parser.
    609  */
    610 static __dead void
    611 command()
    612 {
    613 	struct cmd *c;
    614 
    615 	for (;;) {
    616 		printf("%s> ", prompt);
    617 		if (fgets(line, LBUFLEN, stdin) == 0) {
    618 			if (feof(stdin)) {
    619 				exit(0);
    620 			} else {
    621 				continue;
    622 			}
    623 		}
    624 		if ((line[0] == 0) || (line[0] == '\n'))
    625 			continue;
    626 		makeargv();
    627 		if (margc == 0)
    628 			continue;
    629 		c = getcmd(margv[0]);
    630 		if (c == (struct cmd *)-1) {
    631 			printf("?Ambiguous command\n");
    632 			continue;
    633 		}
    634 		if (c == 0) {
    635 			printf("?Invalid command\n");
    636 			continue;
    637 		}
    638 		(*c->handler)(margc, margv);
    639 	}
    640 }
    641 
    642 struct cmd *
    643 getcmd(name)
    644 	char *name;
    645 {
    646 	char *p, *q;
    647 	struct cmd *c, *found;
    648 	int nmatches, longest;
    649 
    650 	longest = 0;
    651 	nmatches = 0;
    652 	found = 0;
    653 	for (c = cmdtab; (p = c->name) != NULL; c++) {
    654 		for (q = name; *q == *p++; q++)
    655 			if (*q == 0)		/* exact match? */
    656 				return (c);
    657 		if (!*q) {			/* the name was a prefix */
    658 			if (q - name > longest) {
    659 				longest = q - name;
    660 				nmatches = 1;
    661 				found = c;
    662 			} else if (q - name == longest)
    663 				nmatches++;
    664 		}
    665 	}
    666 	if (nmatches > 1)
    667 		return ((struct cmd *)-1);
    668 	return (found);
    669 }
    670 
    671 /*
    672  * Slice a string up into argc/argv.
    673  */
    674 static void
    675 makeargv()
    676 {
    677 	char *cp;
    678 	char **argp = margv;
    679 
    680 	margc = 0;
    681 	for (cp = line; *cp;) {
    682 		while (isspace((unsigned char)*cp))
    683 			cp++;
    684 		if (*cp == '\0')
    685 			break;
    686 		*argp++ = cp;
    687 		margc += 1;
    688 		while (*cp != '\0' && !isspace((unsigned char)*cp))
    689 			cp++;
    690 		if (*cp == '\0')
    691 			break;
    692 		*cp++ = '\0';
    693 	}
    694 	*argp++ = 0;
    695 }
    696 
    697 void
    698 quit(argc, argv)
    699 	int argc;
    700 	char *argv[];
    701 {
    702 
    703 	exit(0);
    704 }
    705 
    706 /*
    707  * Help command.
    708  */
    709 void
    710 help(argc, argv)
    711 	int argc;
    712 	char *argv[];
    713 {
    714 	struct cmd *c;
    715 
    716 	if (argc == 1) {
    717 		printf("Commands may be abbreviated.  Commands are:\n\n");
    718 		for (c = cmdtab; c->name; c++)
    719 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
    720 		return;
    721 	}
    722 	while (--argc > 0) {
    723 		char *arg;
    724 		arg = *++argv;
    725 		c = getcmd(arg);
    726 		if (c == (struct cmd *)-1)
    727 			printf("?Ambiguous help command %s\n", arg);
    728 		else if (c == (struct cmd *)0)
    729 			printf("?Invalid help command %s\n", arg);
    730 		else
    731 			printf("%s\n", c->help);
    732 	}
    733 }
    734 
    735 void
    736 settrace(argc, argv)
    737 	int argc;
    738 	char **argv;
    739 {
    740 	trace = !trace;
    741 	printf("Packet tracing %s.\n", trace ? "on" : "off");
    742 }
    743 
    744 void
    745 setverbose(argc, argv)
    746 	int argc;
    747 	char **argv;
    748 {
    749 	verbose = !verbose;
    750 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
    751 }
    752