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