xref: /original-bsd/usr.bin/tftp/main.c (revision 2301fdfb)
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.8 (Berkeley) 10/11/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 == NULL) {
295 			fprintf(stderr, "tftp: %s: ", cp);
296 			herror((char *)NULL);
297 			return;
298 		}
299 		bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
300 		sin.sin_family = hp->h_addrtype;
301 		connected = 1;
302 		strcpy(hostname, hp->h_name);
303 	}
304 	if (!connected) {
305 		printf("No target machine specified.\n");
306 		return;
307 	}
308 	if (argc < 4) {
309 		cp = argc == 2 ? tail(targ) : argv[1];
310 		fd = open(cp, O_RDONLY);
311 		if (fd < 0) {
312 			fprintf(stderr, "tftp: "); perror(cp);
313 			return;
314 		}
315 		if (verbose)
316 			printf("putting %s to %s:%s [%s]\n",
317 				cp, hostname, targ, mode);
318 		sin.sin_port = port;
319 		sendfile(fd, targ, mode);
320 		return;
321 	}
322 				/* this assumes the target is a directory */
323 				/* on a remote unix system.  hmmmm.  */
324 	cp = index(targ, '\0');
325 	*cp++ = '/';
326 	for (n = 1; n < argc - 1; n++) {
327 		strcpy(cp, tail(argv[n]));
328 		fd = open(argv[n], O_RDONLY);
329 		if (fd < 0) {
330 			fprintf(stderr, "tftp: "); perror(argv[n]);
331 			continue;
332 		}
333 		if (verbose)
334 			printf("putting %s to %s:%s [%s]\n",
335 				argv[n], hostname, targ, mode);
336 		sin.sin_port = port;
337 		sendfile(fd, targ, mode);
338 	}
339 }
340 
341 putusage(s)
342 	char *s;
343 {
344 	printf("usage: %s file ... host:target, or\n", s);
345 	printf("       %s file ... target (when already connected)\n", s);
346 }
347 
348 /*
349  * Receive file(s).
350  */
351 get(argc, argv)
352 	char *argv[];
353 {
354 	int fd;
355 	register int n;
356 	register char *cp;
357 	char *src;
358 
359 	if (argc < 2) {
360 		strcpy(line, "get ");
361 		printf("(files) ");
362 		gets(&line[strlen(line)]);
363 		makeargv();
364 		argc = margc;
365 		argv = margv;
366 	}
367 	if (argc < 2) {
368 		getusage(argv[0]);
369 		return;
370 	}
371 	if (!connected) {
372 		for (n = 1; n < argc ; n++)
373 			if (index(argv[n], ':') == 0) {
374 				getusage(argv[0]);
375 				return;
376 			}
377 	}
378 	for (n = 1; n < argc ; n++) {
379 		src = index(argv[n], ':');
380 		if (src == NULL)
381 			src = argv[n];
382 		else {
383 			struct hostent *hp;
384 
385 			*src++ = 0;
386 			hp = gethostbyname(argv[n]);
387 			if (hp == NULL) {
388 				fprintf(stderr, "tftp: %s: ", argv[n]);
389 				herror((char *)NULL);
390 				continue;
391 			}
392 			bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
393 			sin.sin_family = hp->h_addrtype;
394 			connected = 1;
395 			strcpy(hostname, hp->h_name);
396 		}
397 		if (argc < 4) {
398 			cp = argc == 3 ? argv[2] : tail(src);
399 			fd = creat(cp, 0644);
400 			if (fd < 0) {
401 				fprintf(stderr, "tftp: "); perror(cp);
402 				return;
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 			break;
410 		}
411 		cp = tail(src);         /* new .. jdg */
412 		fd = creat(cp, 0644);
413 		if (fd < 0) {
414 			fprintf(stderr, "tftp: "); perror(cp);
415 			continue;
416 		}
417 		if (verbose)
418 			printf("getting from %s:%s to %s [%s]\n",
419 				hostname, src, cp, mode);
420 		sin.sin_port = port;
421 		recvfile(fd, src, mode);
422 	}
423 }
424 
425 getusage(s)
426 char * s;
427 {
428 	printf("usage: %s host:file host:file ... file, or\n", s);
429 	printf("       %s file file ... file if connected\n", s);
430 }
431 
432 int	rexmtval = TIMEOUT;
433 
434 setrexmt(argc, argv)
435 	char *argv[];
436 {
437 	int t;
438 
439 	if (argc < 2) {
440 		strcpy(line, "Rexmt-timeout ");
441 		printf("(value) ");
442 		gets(&line[strlen(line)]);
443 		makeargv();
444 		argc = margc;
445 		argv = margv;
446 	}
447 	if (argc != 2) {
448 		printf("usage: %s value\n", argv[0]);
449 		return;
450 	}
451 	t = atoi(argv[1]);
452 	if (t < 0)
453 		printf("%s: bad value\n", t);
454 	else
455 		rexmtval = t;
456 }
457 
458 int	maxtimeout = 5 * TIMEOUT;
459 
460 settimeout(argc, argv)
461 	char *argv[];
462 {
463 	int t;
464 
465 	if (argc < 2) {
466 		strcpy(line, "Maximum-timeout ");
467 		printf("(value) ");
468 		gets(&line[strlen(line)]);
469 		makeargv();
470 		argc = margc;
471 		argv = margv;
472 	}
473 	if (argc != 2) {
474 		printf("usage: %s value\n", argv[0]);
475 		return;
476 	}
477 	t = atoi(argv[1]);
478 	if (t < 0)
479 		printf("%s: bad value\n", t);
480 	else
481 		maxtimeout = t;
482 }
483 
484 status(argc, argv)
485 	char *argv[];
486 {
487 	if (connected)
488 		printf("Connected to %s.\n", hostname);
489 	else
490 		printf("Not connected.\n");
491 	printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
492 		verbose ? "on" : "off", trace ? "on" : "off");
493 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
494 		rexmtval, maxtimeout);
495 }
496 
497 intr()
498 {
499 	signal(SIGALRM, SIG_IGN);
500 	alarm(0);
501 	longjmp(toplevel, -1);
502 }
503 
504 char *
505 tail(filename)
506 	char *filename;
507 {
508 	register char *s;
509 
510 	while (*filename) {
511 		s = rindex(filename, '/');
512 		if (s == NULL)
513 			break;
514 		if (s[1])
515 			return (s + 1);
516 		*s = '\0';
517 	}
518 	return (filename);
519 }
520 
521 /*
522  * Command parser.
523  */
524 command(top)
525 	int top;
526 {
527 	register struct cmd *c;
528 
529 	if (!top)
530 		putchar('\n');
531 	for (;;) {
532 		printf("%s> ", prompt);
533 		if (gets(line) == 0) {
534 			if (feof(stdin)) {
535 				quit();
536 			} else {
537 				continue;
538 			}
539 		}
540 		if (line[0] == 0)
541 			continue;
542 		makeargv();
543 		c = getcmd(margv[0]);
544 		if (c == (struct cmd *)-1) {
545 			printf("?Ambiguous command\n");
546 			continue;
547 		}
548 		if (c == 0) {
549 			printf("?Invalid command\n");
550 			continue;
551 		}
552 		(*c->handler)(margc, margv);
553 	}
554 }
555 
556 struct cmd *
557 getcmd(name)
558 	register char *name;
559 {
560 	register char *p, *q;
561 	register struct cmd *c, *found;
562 	register int nmatches, longest;
563 
564 	longest = 0;
565 	nmatches = 0;
566 	found = 0;
567 	for (c = cmdtab; p = c->name; c++) {
568 		for (q = name; *q == *p++; q++)
569 			if (*q == 0)		/* exact match? */
570 				return (c);
571 		if (!*q) {			/* the name was a prefix */
572 			if (q - name > longest) {
573 				longest = q - name;
574 				nmatches = 1;
575 				found = c;
576 			} else if (q - name == longest)
577 				nmatches++;
578 		}
579 	}
580 	if (nmatches > 1)
581 		return ((struct cmd *)-1);
582 	return (found);
583 }
584 
585 /*
586  * Slice a string up into argc/argv.
587  */
588 makeargv()
589 {
590 	register char *cp;
591 	register char **argp = margv;
592 
593 	margc = 0;
594 	for (cp = line; *cp;) {
595 		while (isspace(*cp))
596 			cp++;
597 		if (*cp == '\0')
598 			break;
599 		*argp++ = cp;
600 		margc += 1;
601 		while (*cp != '\0' && !isspace(*cp))
602 			cp++;
603 		if (*cp == '\0')
604 			break;
605 		*cp++ = '\0';
606 	}
607 	*argp++ = 0;
608 }
609 
610 /*VARARGS*/
611 quit()
612 {
613 	exit(0);
614 }
615 
616 /*
617  * Help command.
618  */
619 help(argc, argv)
620 	int argc;
621 	char *argv[];
622 {
623 	register struct cmd *c;
624 
625 	if (argc == 1) {
626 		printf("Commands may be abbreviated.  Commands are:\n\n");
627 		for (c = cmdtab; c->name; c++)
628 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
629 		return;
630 	}
631 	while (--argc > 0) {
632 		register char *arg;
633 		arg = *++argv;
634 		c = getcmd(arg);
635 		if (c == (struct cmd *)-1)
636 			printf("?Ambiguous help command %s\n", arg);
637 		else if (c == (struct cmd *)0)
638 			printf("?Invalid help command %s\n", arg);
639 		else
640 			printf("%s\n", c->help);
641 	}
642 }
643 
644 /*VARARGS*/
645 settrace()
646 {
647 	trace = !trace;
648 	printf("Packet tracing %s.\n", trace ? "on" : "off");
649 }
650 
651 /*VARARGS*/
652 setverbose()
653 {
654 	verbose = !verbose;
655 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
656 }
657