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