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