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