xref: /csrg-svn/usr.bin/tftp/main.c (revision 26103)
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.4 (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, 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 {
414 	printf("usage: %s host:file host:file ... file, or\n", s);
415 	printf("       %s file file ... file if connected\n", s);
416 }
417 
418 int	rexmtval = TIMEOUT;
419 
420 setrexmt(argc, argv)
421 	char *argv[];
422 {
423 	int t;
424 
425 	if (argc < 2) {
426 		strcpy(line, "Rexmt-timeout ");
427 		printf("(value) ");
428 		gets(&line[strlen(line)]);
429 		makeargv();
430 		argc = margc;
431 		argv = margv;
432 	}
433 	if (argc != 2) {
434 		printf("usage: %s value\n", argv[0]);
435 		return;
436 	}
437 	t = atoi(argv[1]);
438 	if (t < 0)
439 		printf("%s: bad value\n", t);
440 	else
441 		rexmtval = t;
442 }
443 
444 int	maxtimeout = 5 * TIMEOUT;
445 
446 settimeout(argc, argv)
447 	char *argv[];
448 {
449 	int t;
450 
451 	if (argc < 2) {
452 		strcpy(line, "Maximum-timeout ");
453 		printf("(value) ");
454 		gets(&line[strlen(line)]);
455 		makeargv();
456 		argc = margc;
457 		argv = margv;
458 	}
459 	if (argc != 2) {
460 		printf("usage: %s value\n", argv[0]);
461 		return;
462 	}
463 	t = atoi(argv[1]);
464 	if (t < 0)
465 		printf("%s: bad value\n", t);
466 	else
467 		maxtimeout = t;
468 }
469 
470 status(argc, argv)
471 	char *argv[];
472 {
473 	if (connected)
474 		printf("Connected to %s.\n", hostname);
475 	else
476 		printf("Not connected.\n");
477 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
478 		verbose ? "on" : "off", trace ? "on" : "off");
479 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
480 		rexmtval, maxtimeout);
481 }
482 
483 intr()
484 {
485 	signal(SIGALRM, SIG_IGN);
486 	alarm(0);
487 	longjmp(toplevel, -1);
488 }
489 
490 char *
491 tail(filename)
492 	char *filename;
493 {
494 	register char *s;
495 
496 	while (*filename) {
497 		s = rindex(filename, '/');
498 		if (s == NULL)
499 			break;
500 		if (s[1])
501 			return (s + 1);
502 		*s = '\0';
503 	}
504 	return (filename);
505 }
506 
507 /*
508  * Command parser.
509  */
510 command(top)
511 	int top;
512 {
513 	register struct cmd *c;
514 
515 	if (!top)
516 		putchar('\n');
517 	for (;;) {
518 		printf("%s> ", prompt);
519 		if (gets(line) == 0) {
520 			if (feof(stdin)) {
521 				quit();
522 			} else {
523 				continue;
524 			}
525 		}
526 		if (line[0] == 0)
527 			continue;
528 		makeargv();
529 		c = getcmd(margv[0]);
530 		if (c == (struct cmd *)-1) {
531 			printf("?Ambiguous command\n");
532 			continue;
533 		}
534 		if (c == 0) {
535 			printf("?Invalid command\n");
536 			continue;
537 		}
538 		(*c->handler)(margc, margv);
539 	}
540 }
541 
542 struct cmd *
543 getcmd(name)
544 	register char *name;
545 {
546 	register char *p, *q;
547 	register struct cmd *c, *found;
548 	register int nmatches, longest;
549 
550 	longest = 0;
551 	nmatches = 0;
552 	found = 0;
553 	for (c = cmdtab; p = c->name; c++) {
554 		for (q = name; *q == *p++; q++)
555 			if (*q == 0)		/* exact match? */
556 				return (c);
557 		if (!*q) {			/* the name was a prefix */
558 			if (q - name > longest) {
559 				longest = q - name;
560 				nmatches = 1;
561 				found = c;
562 			} else if (q - name == longest)
563 				nmatches++;
564 		}
565 	}
566 	if (nmatches > 1)
567 		return ((struct cmd *)-1);
568 	return (found);
569 }
570 
571 /*
572  * Slice a string up into argc/argv.
573  */
574 makeargv()
575 {
576 	register char *cp;
577 	register char **argp = margv;
578 
579 	margc = 0;
580 	for (cp = line; *cp;) {
581 		while (isspace(*cp))
582 			cp++;
583 		if (*cp == '\0')
584 			break;
585 		*argp++ = cp;
586 		margc += 1;
587 		while (*cp != '\0' && !isspace(*cp))
588 			cp++;
589 		if (*cp == '\0')
590 			break;
591 		*cp++ = '\0';
592 	}
593 	*argp++ = 0;
594 }
595 
596 /*VARARGS*/
597 quit()
598 {
599 	exit(0);
600 }
601 
602 /*
603  * Help command.
604  */
605 help(argc, argv)
606 	int argc;
607 	char *argv[];
608 {
609 	register struct cmd *c;
610 
611 	if (argc == 1) {
612 		printf("Commands may be abbreviated.  Commands are:\n\n");
613 		for (c = cmdtab; c->name; c++)
614 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
615 		return;
616 	}
617 	while (--argc > 0) {
618 		register char *arg;
619 		arg = *++argv;
620 		c = getcmd(arg);
621 		if (c == (struct cmd *)-1)
622 			printf("?Ambiguous help command %s\n", arg);
623 		else if (c == (struct cmd *)0)
624 			printf("?Invalid help command %s\n", arg);
625 		else
626 			printf("%s\n", c->help);
627 	}
628 }
629 
630 /*VARARGS*/
631 settrace()
632 {
633 	trace = !trace;
634 	printf("Packet tracing %s.\n", trace ? "on" : "off");
635 }
636 
637 /*VARARGS*/
638 setverbose()
639 {
640 	verbose = !verbose;
641 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
642 }
643