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