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