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