xref: /csrg-svn/usr.bin/tftp/main.c (revision 8384)
1*8384Ssam /*	main.c	4.2	82/10/08	*/
27769Ssam 
37769Ssam /*
47769Ssam  * TFTP User Program -- Command Interface.
57769Ssam  */
67769Ssam #include <sys/types.h>
77769Ssam #include <net/in.h>
87769Ssam #include <sys/socket.h>
97769Ssam #include <signal.h>
107769Ssam #include <stdio.h>
117769Ssam #include <errno.h>
127769Ssam #include <setjmp.h>
137769Ssam #include <ctype.h>
14*8384Ssam #include <netdb.h>
157769Ssam 
16*8384Ssam struct	sockaddr_in sin = { AF_INET };
177769Ssam int	f;
187769Ssam int	options;
197769Ssam int	trace;
207769Ssam int	verbose;
217769Ssam int	connected;
227769Ssam char	mode[32];
237769Ssam char	line[200];
247769Ssam int	margc;
257769Ssam char	*margv[20];
267769Ssam char	*prompt = "tftp";
277769Ssam jmp_buf	toplevel;
287769Ssam int	intr();
29*8384Ssam struct	servent *sp;
307769Ssam 
317769Ssam int	quit(), help(), setverbose(), settrace(), status();
327769Ssam int	get(), put(), setpeer(), setmode();
337769Ssam 
347769Ssam #define HELPINDENT (sizeof("connect"))
357769Ssam 
367769Ssam struct cmd {
377769Ssam 	char	*name;
387769Ssam 	char	*help;
397769Ssam 	int	(*handler)();
407769Ssam };
417769Ssam 
427769Ssam char	vhelp[] = "toggle verbose mode";
437769Ssam char	thelp[] = "toggle packet tracing";
447769Ssam char	chelp[] = "connect to remote tftp";
457769Ssam char	qhelp[] = "exit tftp";
467769Ssam char	hhelp[] = "print help information";
477769Ssam char	shelp[] = "send file";
487769Ssam char	rhelp[] = "receive file";
497769Ssam char	mhelp[] = "set file transfer mode";
507769Ssam char	sthelp[] = "show current status";
517769Ssam 
527769Ssam struct cmd cmdtab[] = {
537769Ssam 	{ "connect",	chelp,		setpeer },
547769Ssam 	{ "mode",	mhelp,		setmode },
557769Ssam 	{ "put",	shelp,		put },
567769Ssam 	{ "get",	rhelp,		get },
577769Ssam 	{ "quit",	qhelp,		quit },
587769Ssam 	{ "verbose",	vhelp,		setverbose },
597769Ssam 	{ "trace",	thelp,		settrace },
607769Ssam 	{ "status",	sthelp,		status },
617769Ssam 	{ "?",		hhelp,		help },
627769Ssam 	0
637769Ssam };
647769Ssam 
657769Ssam struct	cmd *getcmd();
667769Ssam char	*tail();
677769Ssam char	*index();
687769Ssam char	*rindex();
697769Ssam 
707769Ssam main(argc, argv)
717769Ssam 	char *argv[];
727769Ssam {
73*8384Ssam 	sp = getservbyname("tftp", "udp");
74*8384Ssam 	if (sp == 0) {
75*8384Ssam 		fprintf(stderr, "tftp: udp/tftp: unknown service\n");
76*8384Ssam 		exit(1);
77*8384Ssam 	}
78*8384Ssam 	sin.sin_port = htons(sp->s_port);
797769Ssam 	if (argc > 1 && !strcmp(argv[1], "-d")) {
807769Ssam 		options |= SO_DEBUG;
817769Ssam 		argc--, argv++;
827769Ssam 	}
837769Ssam 	f = socket(SOCK_DGRAM, 0, 0, options);
847769Ssam 	if (f < 0) {
857769Ssam 		perror("socket");
867769Ssam 		exit(3);
877769Ssam 	}
887769Ssam 	strcpy(mode, "netascii");
897769Ssam 	if (argc > 1) {
907769Ssam 		if (setjmp(toplevel) != 0)
917769Ssam 			exit(0);
927769Ssam 		setpeer(argc, argv);
937769Ssam 	}
947769Ssam 	setjmp(toplevel);
957769Ssam 	for (;;)
967769Ssam 		command(1);
977769Ssam }
987769Ssam 
99*8384Ssam char	*hostname;
100*8384Ssam char	hnamebuf[32];
1017769Ssam 
1027769Ssam setpeer(argc, argv)
1037769Ssam 	int argc;
1047769Ssam 	char *argv[];
1057769Ssam {
1067769Ssam 	register int c;
107*8384Ssam 	struct hostent *host;
1087769Ssam 
1097769Ssam 	if (argc < 2) {
1107769Ssam 		strcpy(line, "Connect ");
1117769Ssam 		printf("(to) ");
1127769Ssam 		gets(&line[strlen(line)]);
1137769Ssam 		makeargv();
1147769Ssam 		argc = margc;
1157769Ssam 		argv = margv;
1167769Ssam 	}
1177769Ssam 	if (argc > 3) {
1187769Ssam 		printf("usage: %s host-name [port]\n", argv[0]);
1197769Ssam 		return;
1207769Ssam 	}
121*8384Ssam 	host = gethostbyname(argv[1]);
122*8384Ssam 	if (host) {
123*8384Ssam 		bcopy(host->h_addr, &sin.sin_addr, host->h_length);
124*8384Ssam 		hostname = host->h_name;
125*8384Ssam 	} else {
126*8384Ssam 		sin.sin_addr.s_addr = inet_addr(argv[1]);
127*8384Ssam 		if (sin.sin_addr.s_addr == -1) {
128*8384Ssam 			connected = 0;
129*8384Ssam 			printf("%s: unknown host\n", argv[1]);
130*8384Ssam 			return;
131*8384Ssam 		}
132*8384Ssam 		strcpy(hnamebuf, argv[1]);
133*8384Ssam 		hostname = hnamebuf;
1347769Ssam 	}
135*8384Ssam 	sin.sin_port = sp->s_port;
1367769Ssam 	if (argc == 3) {
1377769Ssam 		sin.sin_port = atoi(argv[2]);
1387769Ssam 		if (sin.sin_port < 0) {
1397769Ssam 			printf("%s: bad port number\n", argv[2]);
1407769Ssam 			connected = 0;
1417769Ssam 			return;
1427769Ssam 		}
143*8384Ssam 	}
1447769Ssam #if vax || pdp11
145*8384Ssam 	sin.sin_port = htons(sin.sin_port);
1467769Ssam #endif
1477769Ssam 	connected = 1;
1487769Ssam }
1497769Ssam 
1507769Ssam struct	modes {
1517769Ssam 	char *m_name;
1527769Ssam 	char *m_mode;
1537769Ssam } modes[] = {
1547769Ssam 	{ "ascii",	"netascii" },
1557769Ssam 	{ "binary",	"octect" },
1567769Ssam 	{ "mail",	"mail" },
1577769Ssam 	{ 0,		0 }
1587769Ssam };
1597769Ssam 
1607769Ssam setmode(argc, argv)
1617769Ssam 	char *argv[];
1627769Ssam {
1637769Ssam 	register struct modes *p;
1647769Ssam 
1657769Ssam 	if (argc > 2) {
1667769Ssam 		char *sep;
1677769Ssam 
1687769Ssam 		printf("usage: %s [", argv[0]);
1697769Ssam 		sep = " ";
1707769Ssam 		for (p = modes; p->m_name; p++) {
1717769Ssam 			printf("%s%s", sep, p->m_name);
1727769Ssam 			if (*sep == ' ')
1737769Ssam 				sep = " | ";
1747769Ssam 		}
1757769Ssam 		printf(" ]\n");
1767769Ssam 		return;
1777769Ssam 	}
1787769Ssam 	if (argc < 2) {
1797769Ssam 		printf("Using %s mode to transfer files.\n", mode);
1807769Ssam 		return;
1817769Ssam 	}
1827769Ssam 	for (p = modes; p->m_name; p++)
1837769Ssam 		if (strcmp(argv[1], p->m_name) == 0)
1847769Ssam 			break;
1857769Ssam 	if (p->m_name)
1867769Ssam 		strcpy(mode, p->m_mode);
1877769Ssam 	else
1887769Ssam 		printf("%s: unknown mode\n", argv[1]);
1897769Ssam }
1907769Ssam 
1917769Ssam /*
1927769Ssam  * Send file(s).
1937769Ssam  */
1947769Ssam put(argc, argv)
1957769Ssam 	char *argv[];
1967769Ssam {
1977769Ssam 	int fd;
1987769Ssam 	register int n, addr;
1997769Ssam 	register char *cp, *targ;
2007769Ssam 
2017769Ssam 	if (argc < 2) {
2027769Ssam 		strcpy(line, "send ");
2037769Ssam 		printf("(file) ");
2047769Ssam 		gets(&line[strlen(line)]);
2057769Ssam 		makeargv();
2067769Ssam 		argc = margc;
2077769Ssam 		argv = margv;
2087769Ssam 	}
2097769Ssam 	if (argc < 2) {
2107769Ssam 		putusage(argv[0]);
2117769Ssam 		return;
2127769Ssam 	}
2137769Ssam 	targ = argv[argc - 1];
2147769Ssam 	if (index(argv[argc - 1], ':')) {
215*8384Ssam 		char *cp;
216*8384Ssam 		struct hostent *hp;
2177769Ssam 
2187769Ssam 		for (n = 1; n < argc - 1; n++)
2197769Ssam 			if (index(argv[n], ':')) {
2207769Ssam 				putusage(argv[0]);
2217769Ssam 				return;
2227769Ssam 			}
223*8384Ssam 		cp = argv[argc - 1];
224*8384Ssam 		targ = index(cp, ':');
2257769Ssam 		*targ++ = 0;
226*8384Ssam 		hp = gethostbyname(cp);
227*8384Ssam 		if (hp == 0) {
228*8384Ssam 			printf("%s: Unknown host.\n", cp);
2297769Ssam 			return;
2307769Ssam 		}
231*8384Ssam 		bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
232*8384Ssam 		sin.sin_family = hp->h_addrtype;
2337769Ssam 		connected = 1;
234*8384Ssam 		hostname = hp->h_name;
2357769Ssam 	}
2367769Ssam 	if (!connected) {
2377769Ssam 		printf("No target machine specified.\n");
2387769Ssam 		return;
2397769Ssam 	}
2407769Ssam 	sigset(SIGINT, intr);
2417769Ssam 	if (argc < 4) {
2427769Ssam 		cp = argc == 2 ? tail(targ) : argv[1];
2437769Ssam 		fd = open(cp);
2447769Ssam 		if (fd < 0) {
2457769Ssam 			perror(cp);
2467769Ssam 			return;
2477769Ssam 		}
2487769Ssam 		sendfile(fd, targ);
2497769Ssam 		return;
2507769Ssam 	}
2517769Ssam 	cp = index(targ, '\0');
2527769Ssam 	*cp++ = '/';
2537769Ssam 	for (n = 1; n < argc - 1; n++) {
2547769Ssam 		strcpy(cp, tail(argv[n]));
2557769Ssam 		fd = open(argv[n], 0);
2567769Ssam 		if (fd < 0) {
2577769Ssam 			perror(argv[n]);
2587769Ssam 			continue;
2597769Ssam 		}
2607769Ssam 		sendfile(fd, targ);
2617769Ssam 	}
2627769Ssam }
2637769Ssam 
2647769Ssam putusage(s)
2657769Ssam 	char *s;
2667769Ssam {
2677769Ssam 	printf("usage: %s file ... host:target, or\n", s);
2687769Ssam 	printf("       %s file ... target (when already connected)\n", s);
2697769Ssam }
2707769Ssam 
2717769Ssam /*
2727769Ssam  * Receive file(s).
2737769Ssam  */
2747769Ssam get(argc, argv)
2757769Ssam 	char *argv[];
2767769Ssam {
2777769Ssam 	int fd;
2787769Ssam 	register int n, addr;
2797769Ssam 	register char *cp;
2807769Ssam 	char *src;
2817769Ssam 
2827769Ssam 	if (argc < 2) {
2837769Ssam 		strcpy(line, "get ");
2847769Ssam 		printf("(files) ");
2857769Ssam 		gets(&line[strlen(line)]);
2867769Ssam 		makeargv();
2877769Ssam 		argc = margc;
2887769Ssam 		argv = margv;
2897769Ssam 	}
2907769Ssam 	if (argc < 2) {
2917769Ssam 		getusage(argv[0]);
2927769Ssam 		return;
2937769Ssam 	}
2947769Ssam 	if (!connected)
2957769Ssam 		for (n = 1; n < argc - 1; n++)
2967769Ssam 			if (index(argv[n], ':') == 0) {
2977769Ssam 				getusage(argv[0]);
2987769Ssam 				return;
2997769Ssam 			}
3007769Ssam 	sigset(SIGINT, intr);
3017769Ssam 	for (n = 1; argc == 2 || n < argc - 1; n++) {
3027769Ssam 		src = index(argv[n], ':');
3037769Ssam 		if (src == NULL)
3047769Ssam 			src = argv[n];
3057769Ssam 		else {
306*8384Ssam 			struct hostent *hp;
307*8384Ssam 
3087769Ssam 			*src++ = 0;
309*8384Ssam 			hp = gethostbyname(argv[n]);
310*8384Ssam 			if (hp == 0) {
3117769Ssam 				printf("%s: Unknown host.\n", argv[n]);
3127769Ssam 				continue;
3137769Ssam 			}
314*8384Ssam 			bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
315*8384Ssam 			sin.sin_family = hp->h_addrtype;
3167769Ssam 			connected = 1;
317*8384Ssam 			hostname = hp->h_name;
3187769Ssam 		}
3197769Ssam 		if (argc < 4) {
3207769Ssam 			cp = argc == 3 ? argv[2] : tail(src);
3217769Ssam 			fd = creat(cp, 0644);
3227769Ssam 			if (fd < 0) {
3237769Ssam 				perror(cp);
3247769Ssam 				return;
3257769Ssam 			}
3267769Ssam 			recvfile(fd, src);
3277769Ssam 			break;
3287769Ssam 		}
3297769Ssam 		cp = index(argv[argc - 1], '\0');
3307769Ssam 		*cp++ = '/';
3317769Ssam 		strcpy(cp, tail(src));
3327769Ssam 		fd = creat(src, 0644);
3337769Ssam 		if (fd < 0) {
3347769Ssam 			perror(src);
3357769Ssam 			continue;
3367769Ssam 		}
3377769Ssam 		recvfile(fd, src);
3387769Ssam 	}
3397769Ssam }
3407769Ssam 
3417769Ssam getusage(s)
3427769Ssam {
3437769Ssam 	printf("usage: %s host:file host:file ... file, or\n", s);
3447769Ssam 	printf("       %s file file ... file if connected\n", s);
3457769Ssam }
3467769Ssam 
3477769Ssam status(argc, argv)
3487769Ssam 	char *argv[];
3497769Ssam {
3507769Ssam 	if (connected)
351*8384Ssam 		printf("Connected to %s.\n", hostname);
3527769Ssam 	else
3537769Ssam 		printf("Not connected.\n");
3547769Ssam 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
3557769Ssam 		verbose ? "on" : "off", trace ? "on" : "off");
3567769Ssam }
3577769Ssam 
3587769Ssam intr()
3597769Ssam {
3607769Ssam 	longjmp(toplevel, -1);
3617769Ssam }
3627769Ssam 
3637769Ssam char *
3647769Ssam tail(filename)
3657769Ssam 	char *filename;
3667769Ssam {
3677769Ssam 	register char *s;
3687769Ssam 
3697769Ssam 	while (*filename) {
3707769Ssam 		s = rindex(filename, '/');
3717769Ssam 		if (s == NULL)
3727769Ssam 			break;
3737769Ssam 		if (s[1])
3747769Ssam 			return (s + 1);
3757769Ssam 		*s = '\0';
3767769Ssam 	}
3777769Ssam 	return (filename);
3787769Ssam }
3797769Ssam 
3807769Ssam /*
3817769Ssam  * Command parser.
3827769Ssam  */
3837769Ssam command(top)
3847769Ssam 	int top;
3857769Ssam {
3867769Ssam 	register struct cmd *c;
3877769Ssam 
3887769Ssam 	if (!top)
3897769Ssam 		putchar('\n');
3907769Ssam 	else
3917769Ssam 		sigset(SIGINT, SIG_DFL);
3927769Ssam 	for (;;) {
3937769Ssam 		printf("%s> ", prompt);
3947769Ssam 		if (gets(line) == 0)
3957769Ssam 			break;
3967769Ssam 		if (line[0] == 0)
3977769Ssam 			break;
3987769Ssam 		makeargv();
3997769Ssam 		c = getcmd(margv[0]);
4007769Ssam 		if (c == (struct cmd *)-1) {
4017769Ssam 			printf("?Ambiguous command\n");
4027769Ssam 			continue;
4037769Ssam 		}
4047769Ssam 		if (c == 0) {
4057769Ssam 			printf("?Invalid command\n");
4067769Ssam 			continue;
4077769Ssam 		}
4087769Ssam 		(*c->handler)(margc, margv);
4097769Ssam 		if (c->handler != help)
4107769Ssam 			break;
4117769Ssam 	}
4127769Ssam 	longjmp(toplevel, 1);
4137769Ssam }
4147769Ssam 
4157769Ssam struct cmd *
4167769Ssam getcmd(name)
4177769Ssam 	register char *name;
4187769Ssam {
4197769Ssam 	register char *p, *q;
4207769Ssam 	register struct cmd *c, *found;
4217769Ssam 	register int nmatches, longest;
4227769Ssam 
4237769Ssam 	longest = 0;
4247769Ssam 	nmatches = 0;
4257769Ssam 	found = 0;
4267769Ssam 	for (c = cmdtab; p = c->name; c++) {
4277769Ssam 		for (q = name; *q == *p++; q++)
4287769Ssam 			if (*q == 0)		/* exact match? */
4297769Ssam 				return (c);
4307769Ssam 		if (!*q) {			/* the name was a prefix */
4317769Ssam 			if (q - name > longest) {
4327769Ssam 				longest = q - name;
4337769Ssam 				nmatches = 1;
4347769Ssam 				found = c;
4357769Ssam 			} else if (q - name == longest)
4367769Ssam 				nmatches++;
4377769Ssam 		}
4387769Ssam 	}
4397769Ssam 	if (nmatches > 1)
4407769Ssam 		return ((struct cmd *)-1);
4417769Ssam 	return (found);
4427769Ssam }
4437769Ssam 
4447769Ssam /*
4457769Ssam  * Slice a string up into argc/argv.
4467769Ssam  */
4477769Ssam makeargv()
4487769Ssam {
4497769Ssam 	register char *cp;
4507769Ssam 	register char **argp = margv;
4517769Ssam 
4527769Ssam 	margc = 0;
4537769Ssam 	for (cp = line; *cp;) {
4547769Ssam 		while (isspace(*cp))
4557769Ssam 			cp++;
4567769Ssam 		if (*cp == '\0')
4577769Ssam 			break;
4587769Ssam 		*argp++ = cp;
4597769Ssam 		margc += 1;
4607769Ssam 		while (*cp != '\0' && !isspace(*cp))
4617769Ssam 			cp++;
4627769Ssam 		if (*cp == '\0')
4637769Ssam 			break;
4647769Ssam 		*cp++ = '\0';
4657769Ssam 	}
4667769Ssam 	*argp++ = 0;
4677769Ssam }
4687769Ssam 
4697769Ssam /*VARARGS*/
4707769Ssam quit()
4717769Ssam {
4727769Ssam 	exit(0);
4737769Ssam }
4747769Ssam 
4757769Ssam /*
4767769Ssam  * Help command.
4777769Ssam  * Call each command handler with argc == 0 and argv[0] == name.
4787769Ssam  */
4797769Ssam help(argc, argv)
4807769Ssam 	int argc;
4817769Ssam 	char *argv[];
4827769Ssam {
4837769Ssam 	register struct cmd *c;
4847769Ssam 
4857769Ssam 	if (argc == 1) {
4867769Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
4877769Ssam 		for (c = cmdtab; c->name; c++)
4887769Ssam 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
4897769Ssam 		return;
4907769Ssam 	}
4917769Ssam 	while (--argc > 0) {
4927769Ssam 		register char *arg;
4937769Ssam 		arg = *++argv;
4947769Ssam 		c = getcmd(arg);
4957769Ssam 		if (c == (struct cmd *)-1)
4967769Ssam 			printf("?Ambiguous help command %s\n", arg);
4977769Ssam 		else if (c == (struct cmd *)0)
4987769Ssam 			printf("?Invalid help command %s\n", arg);
4997769Ssam 		else
5007769Ssam 			printf("%s\n", c->help);
5017769Ssam 	}
5027769Ssam }
5037769Ssam 
5047769Ssam /*
5057769Ssam  * Call routine with argc, argv set from args (terminated by 0).
5067769Ssam  */
5077769Ssam /* VARARGS2 */
5087769Ssam call(routine, args)
5097769Ssam 	int (*routine)();
5107769Ssam 	int args;
5117769Ssam {
5127769Ssam 	register int *argp;
5137769Ssam 	register int argc;
5147769Ssam 
5157769Ssam 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
5167769Ssam 		;
5177769Ssam 	(*routine)(argc, &args);
5187769Ssam }
5197769Ssam 
5207769Ssam /*VARARGS*/
5217769Ssam settrace()
5227769Ssam {
5237769Ssam 	trace = !trace;
5247769Ssam 	printf("Packet tracing %s.\n", trace ? "on" : "off");
5257769Ssam }
5267769Ssam 
5277769Ssam /*VARARGS*/
5287769Ssam setverbose()
5297769Ssam {
5307769Ssam 	verbose = !verbose;
5317769Ssam 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
5327769Ssam }
533