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