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