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