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