Home | History | Annotate | Line # | Download | only in tftp
main.c revision 1.11
      1 /*	$NetBSD: main.c,v 1.11 1999/07/12 20:19:20 itojun 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.11 1999/07/12 20:19:20 itojun 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 		connected = 0;
    188 	}
    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 		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    205 		if (f < 0) {
    206 			cause = "socket";
    207 			continue;
    208 		}
    209 
    210 		memset(&ss, 0, sizeof(ss));
    211 		ss.ss_family = res->ai_family;
    212 		ss.ss_len = res->ai_addrlen;
    213 		if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
    214 			cause = "bind";
    215 			close(f);
    216 			f = -1;
    217 			continue;
    218 		}
    219 
    220 		break;
    221 	}
    222 
    223 	if (f < 0)
    224 		warn("%s", cause);
    225 	else {
    226 		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
    227 		if (res->ai_canonname) {
    228 			(void) strncpy(hostname, res->ai_canonname,
    229 				sizeof(hostname));
    230 		} else
    231 			(void) strncpy(hostname, host, sizeof(hostname));
    232 		hostname[sizeof(hostname)-1] = 0;
    233 		connected = 1;
    234 	}
    235 }
    236 
    237 void
    238 setpeer(argc, argv)
    239 	int argc;
    240 	char *argv[];
    241 {
    242 
    243 	if (argc < 2) {
    244 		strcpy(line, "Connect ");
    245 		printf("(to) ");
    246 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    247 		makeargv();
    248 		argc = margc;
    249 		argv = margv;
    250 	}
    251 	if ((argc < 2) || (argc > 3)) {
    252 		printf("usage: %s host-name [port]\n", argv[0]);
    253 		return;
    254 	}
    255 	if (argc == 3)
    256 		setpeer0(argv[1], NULL);
    257 	else
    258 		setpeer0(argv[1], argv[2]);
    259 }
    260 
    261 struct	modes {
    262 	char *m_name;
    263 	char *m_mode;
    264 } modes[] = {
    265 	{ "ascii",	"netascii" },
    266 	{ "netascii",   "netascii" },
    267 	{ "binary",     "octet" },
    268 	{ "image",      "octet" },
    269 	{ "octet",     "octet" },
    270 /*      { "mail",       "mail" },       */
    271 	{ 0,		0 }
    272 };
    273 
    274 void
    275 modecmd(argc, argv)
    276 	int argc;
    277 	char *argv[];
    278 {
    279 	struct modes *p;
    280 	char *sep;
    281 
    282 	if (argc < 2) {
    283 		printf("Using %s mode to transfer files.\n", mode);
    284 		return;
    285 	}
    286 	if (argc == 2) {
    287 		for (p = modes; p->m_name; p++)
    288 			if (strcmp(argv[1], p->m_name) == 0)
    289 				break;
    290 		if (p->m_name) {
    291 			settftpmode(p->m_mode);
    292 			return;
    293 		}
    294 		printf("%s: unknown mode\n", argv[1]);
    295 		/* drop through and print usage message */
    296 	}
    297 
    298 	printf("usage: %s [", argv[0]);
    299 	sep = " ";
    300 	for (p = modes; p->m_name; p++) {
    301 		printf("%s%s", sep, p->m_name);
    302 		if (*sep == ' ')
    303 			sep = " | ";
    304 	}
    305 	printf(" ]\n");
    306 	return;
    307 }
    308 
    309 void
    310 setbinary(argc, argv)
    311 	int argc;
    312 	char *argv[];
    313 {
    314 
    315 	settftpmode("octet");
    316 }
    317 
    318 void
    319 setascii(argc, argv)
    320 	int argc;
    321 	char *argv[];
    322 {
    323 
    324 	settftpmode("netascii");
    325 }
    326 
    327 static void
    328 settftpmode(newmode)
    329 	char *newmode;
    330 {
    331 	strcpy(mode, newmode);
    332 	if (verbose)
    333 		printf("mode set to %s\n", mode);
    334 }
    335 
    336 
    337 /*
    338  * Send file(s).
    339  */
    340 void
    341 put(argc, argv)
    342 	int argc;
    343 	char *argv[];
    344 {
    345 	int fd;
    346 	int n;
    347 	char *cp, *targ;
    348 
    349 	if (argc < 2) {
    350 		strcpy(line, "send ");
    351 		printf("(file) ");
    352 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    353 		makeargv();
    354 		argc = margc;
    355 		argv = margv;
    356 	}
    357 	if (argc < 2) {
    358 		putusage(argv[0]);
    359 		return;
    360 	}
    361 	targ = argv[argc - 1];
    362 	if (strrchr(argv[argc - 1], ':')) {
    363 		char *cp;
    364 
    365 		for (n = 1; n < argc - 1; n++)
    366 			if (strchr(argv[n], ':')) {
    367 				putusage(argv[0]);
    368 				return;
    369 			}
    370 		cp = argv[argc - 1];
    371 		targ = strrchr(cp, ':');
    372 		*targ++ = 0;
    373 		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
    374 			cp[strlen(cp) - 1] = '\0';
    375 			cp++;
    376 		}
    377 		setpeer0(cp, NULL);
    378 	}
    379 	if (!connected) {
    380 		printf("No target machine specified.\n");
    381 		return;
    382 	}
    383 	if (argc < 4) {
    384 		cp = argc == 2 ? tail(targ) : argv[1];
    385 		fd = open(cp, O_RDONLY);
    386 		if (fd < 0) {
    387 			warn("%s", cp);
    388 			return;
    389 		}
    390 		if (verbose)
    391 			printf("putting %s to %s:%s [%s]\n",
    392 				cp, hostname, targ, mode);
    393 		sendfile(fd, targ, mode);
    394 		return;
    395 	}
    396 				/* this assumes the target is a directory */
    397 				/* on a remote unix system.  hmmmm.  */
    398 	cp = strchr(targ, '\0');
    399 	*cp++ = '/';
    400 	for (n = 1; n < argc - 1; n++) {
    401 		strcpy(cp, tail(argv[n]));
    402 		fd = open(argv[n], O_RDONLY);
    403 		if (fd < 0) {
    404 			warn("%s", argv[n]);
    405 			continue;
    406 		}
    407 		if (verbose)
    408 			printf("putting %s to %s:%s [%s]\n",
    409 				argv[n], hostname, targ, mode);
    410 		sendfile(fd, targ, mode);
    411 	}
    412 }
    413 
    414 static void
    415 putusage(s)
    416 	char *s;
    417 {
    418 	printf("usage: %s file ... host:target, or\n", s);
    419 	printf("       %s file ... target (when already connected)\n", s);
    420 }
    421 
    422 /*
    423  * Receive file(s).
    424  */
    425 void
    426 get(argc, argv)
    427 	int argc;
    428 	char *argv[];
    429 {
    430 	int fd;
    431 	int n;
    432 	char *cp;
    433 	char *src;
    434 
    435 	if (argc < 2) {
    436 		strcpy(line, "get ");
    437 		printf("(files) ");
    438 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    439 		makeargv();
    440 		argc = margc;
    441 		argv = margv;
    442 	}
    443 	if (argc < 2) {
    444 		getusage(argv[0]);
    445 		return;
    446 	}
    447 	if (!connected) {
    448 		for (n = 1; n < argc ; n++)
    449 			if (strrchr(argv[n], ':') == 0) {
    450 				getusage(argv[0]);
    451 				return;
    452 			}
    453 	}
    454 	for (n = 1; n < argc ; n++) {
    455 		src = strrchr(argv[n], ':');
    456 		if (src == NULL)
    457 			src = argv[n];
    458 		else {
    459 			char *cp;
    460 			*src++ = 0;
    461 			cp = argv[n];
    462 			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
    463 				cp[strlen(cp) - 1] = '\0';
    464 				cp++;
    465 			}
    466 			setpeer0(cp, NULL);
    467 			if (!connected)
    468 				continue;
    469 		}
    470 		if (argc < 4) {
    471 			cp = argc == 3 ? argv[2] : tail(src);
    472 			fd = creat(cp, 0644);
    473 			if (fd < 0) {
    474 				warn("%s", cp);
    475 				return;
    476 			}
    477 			if (verbose)
    478 				printf("getting from %s:%s to %s [%s]\n",
    479 					hostname, src, cp, mode);
    480 			recvfile(fd, src, mode);
    481 			break;
    482 		}
    483 		cp = tail(src);         /* new .. jdg */
    484 		fd = creat(cp, 0644);
    485 		if (fd < 0) {
    486 			warn("%s", cp);
    487 			continue;
    488 		}
    489 		if (verbose)
    490 			printf("getting from %s:%s to %s [%s]\n",
    491 				hostname, src, cp, mode);
    492 		recvfile(fd, src, mode);
    493 	}
    494 }
    495 
    496 static void
    497 getusage(s)
    498 	char *s;
    499 {
    500 	printf("usage: %s host:file host:file ... file, or\n", s);
    501 	printf("       %s file file ... file if connected\n", s);
    502 }
    503 
    504 int	rexmtval = TIMEOUT;
    505 
    506 void
    507 setrexmt(argc, argv)
    508 	int argc;
    509 	char *argv[];
    510 {
    511 	int t;
    512 
    513 	if (argc < 2) {
    514 		strcpy(line, "Rexmt-timeout ");
    515 		printf("(value) ");
    516 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    517 		makeargv();
    518 		argc = margc;
    519 		argv = margv;
    520 	}
    521 	if (argc != 2) {
    522 		printf("usage: %s value\n", argv[0]);
    523 		return;
    524 	}
    525 	t = atoi(argv[1]);
    526 	if (t < 0)
    527 		printf("%s: bad value\n", argv[1]);
    528 	else
    529 		rexmtval = t;
    530 }
    531 
    532 int	maxtimeout = 5 * TIMEOUT;
    533 
    534 void
    535 settimeout(argc, argv)
    536 	int argc;
    537 	char *argv[];
    538 {
    539 	int t;
    540 
    541 	if (argc < 2) {
    542 		strcpy(line, "Maximum-timeout ");
    543 		printf("(value) ");
    544 		fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
    545 		makeargv();
    546 		argc = margc;
    547 		argv = margv;
    548 	}
    549 	if (argc != 2) {
    550 		printf("usage: %s value\n", argv[0]);
    551 		return;
    552 	}
    553 	t = atoi(argv[1]);
    554 	if (t < 0)
    555 		printf("%s: bad value\n", argv[1]);
    556 	else
    557 		maxtimeout = t;
    558 }
    559 
    560 void
    561 status(argc, argv)
    562 	int argc;
    563 	char *argv[];
    564 {
    565 	if (connected)
    566 		printf("Connected to %s.\n", hostname);
    567 	else
    568 		printf("Not connected.\n");
    569 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
    570 		verbose ? "on" : "off", trace ? "on" : "off");
    571 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
    572 		rexmtval, maxtimeout);
    573 }
    574 
    575 void
    576 intr(dummy)
    577 	int dummy;
    578 {
    579 
    580 	signal(SIGALRM, SIG_IGN);
    581 	alarm(0);
    582 	longjmp(toplevel, -1);
    583 }
    584 
    585 char *
    586 tail(filename)
    587 	char *filename;
    588 {
    589 	char *s;
    590 
    591 	while (*filename) {
    592 		s = strrchr(filename, '/');
    593 		if (s == NULL)
    594 			break;
    595 		if (s[1])
    596 			return (s + 1);
    597 		*s = '\0';
    598 	}
    599 	return (filename);
    600 }
    601 
    602 /*
    603  * Command parser.
    604  */
    605 static __dead void
    606 command()
    607 {
    608 	struct cmd *c;
    609 
    610 	for (;;) {
    611 		printf("%s> ", prompt);
    612 		if (fgets(line, LBUFLEN, stdin) == 0) {
    613 			if (feof(stdin)) {
    614 				exit(0);
    615 			} else {
    616 				continue;
    617 			}
    618 		}
    619 		if ((line[0] == 0) || (line[0] == '\n'))
    620 			continue;
    621 		makeargv();
    622 		if (margc == 0)
    623 			continue;
    624 		c = getcmd(margv[0]);
    625 		if (c == (struct cmd *)-1) {
    626 			printf("?Ambiguous command\n");
    627 			continue;
    628 		}
    629 		if (c == 0) {
    630 			printf("?Invalid command\n");
    631 			continue;
    632 		}
    633 		(*c->handler)(margc, margv);
    634 	}
    635 }
    636 
    637 struct cmd *
    638 getcmd(name)
    639 	char *name;
    640 {
    641 	char *p, *q;
    642 	struct cmd *c, *found;
    643 	int nmatches, longest;
    644 
    645 	longest = 0;
    646 	nmatches = 0;
    647 	found = 0;
    648 	for (c = cmdtab; (p = c->name) != NULL; c++) {
    649 		for (q = name; *q == *p++; q++)
    650 			if (*q == 0)		/* exact match? */
    651 				return (c);
    652 		if (!*q) {			/* the name was a prefix */
    653 			if (q - name > longest) {
    654 				longest = q - name;
    655 				nmatches = 1;
    656 				found = c;
    657 			} else if (q - name == longest)
    658 				nmatches++;
    659 		}
    660 	}
    661 	if (nmatches > 1)
    662 		return ((struct cmd *)-1);
    663 	return (found);
    664 }
    665 
    666 /*
    667  * Slice a string up into argc/argv.
    668  */
    669 static void
    670 makeargv()
    671 {
    672 	char *cp;
    673 	char **argp = margv;
    674 
    675 	margc = 0;
    676 	for (cp = line; *cp;) {
    677 		while (isspace((unsigned char)*cp))
    678 			cp++;
    679 		if (*cp == '\0')
    680 			break;
    681 		*argp++ = cp;
    682 		margc += 1;
    683 		while (*cp != '\0' && !isspace((unsigned char)*cp))
    684 			cp++;
    685 		if (*cp == '\0')
    686 			break;
    687 		*cp++ = '\0';
    688 	}
    689 	*argp++ = 0;
    690 }
    691 
    692 void
    693 quit(argc, argv)
    694 	int argc;
    695 	char *argv[];
    696 {
    697 
    698 	exit(0);
    699 }
    700 
    701 /*
    702  * Help command.
    703  */
    704 void
    705 help(argc, argv)
    706 	int argc;
    707 	char *argv[];
    708 {
    709 	struct cmd *c;
    710 
    711 	if (argc == 1) {
    712 		printf("Commands may be abbreviated.  Commands are:\n\n");
    713 		for (c = cmdtab; c->name; c++)
    714 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
    715 		return;
    716 	}
    717 	while (--argc > 0) {
    718 		char *arg;
    719 		arg = *++argv;
    720 		c = getcmd(arg);
    721 		if (c == (struct cmd *)-1)
    722 			printf("?Ambiguous help command %s\n", arg);
    723 		else if (c == (struct cmd *)0)
    724 			printf("?Invalid help command %s\n", arg);
    725 		else
    726 			printf("%s\n", c->help);
    727 	}
    728 }
    729 
    730 void
    731 settrace(argc, argv)
    732 	int argc;
    733 	char **argv;
    734 {
    735 	trace = !trace;
    736 	printf("Packet tracing %s.\n", trace ? "on" : "off");
    737 }
    738 
    739 void
    740 setverbose(argc, argv)
    741 	int argc;
    742 	char **argv;
    743 {
    744 	verbose = !verbose;
    745 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
    746 }
    747