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