xref: /original-bsd/usr.bin/tftp/main.c (revision 81f6297c)
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.1 (Berkeley) 06/06/85";
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 			continue;
468 		if (line[0] == 0)
469 			continue;
470 		makeargv();
471 		c = getcmd(margv[0]);
472 		if (c == (struct cmd *)-1) {
473 			printf("?Ambiguous command\n");
474 			continue;
475 		}
476 		if (c == 0) {
477 			printf("?Invalid command\n");
478 			continue;
479 		}
480 		(*c->handler)(margc, margv);
481 	}
482 }
483 
484 struct cmd *
485 getcmd(name)
486 	register char *name;
487 {
488 	register char *p, *q;
489 	register struct cmd *c, *found;
490 	register int nmatches, longest;
491 
492 	longest = 0;
493 	nmatches = 0;
494 	found = 0;
495 	for (c = cmdtab; p = c->name; c++) {
496 		for (q = name; *q == *p++; q++)
497 			if (*q == 0)		/* exact match? */
498 				return (c);
499 		if (!*q) {			/* the name was a prefix */
500 			if (q - name > longest) {
501 				longest = q - name;
502 				nmatches = 1;
503 				found = c;
504 			} else if (q - name == longest)
505 				nmatches++;
506 		}
507 	}
508 	if (nmatches > 1)
509 		return ((struct cmd *)-1);
510 	return (found);
511 }
512 
513 /*
514  * Slice a string up into argc/argv.
515  */
516 makeargv()
517 {
518 	register char *cp;
519 	register char **argp = margv;
520 
521 	margc = 0;
522 	for (cp = line; *cp;) {
523 		while (isspace(*cp))
524 			cp++;
525 		if (*cp == '\0')
526 			break;
527 		*argp++ = cp;
528 		margc += 1;
529 		while (*cp != '\0' && !isspace(*cp))
530 			cp++;
531 		if (*cp == '\0')
532 			break;
533 		*cp++ = '\0';
534 	}
535 	*argp++ = 0;
536 }
537 
538 /*VARARGS*/
539 quit()
540 {
541 	exit(0);
542 }
543 
544 /*
545  * Help command.
546  */
547 help(argc, argv)
548 	int argc;
549 	char *argv[];
550 {
551 	register struct cmd *c;
552 
553 	if (argc == 1) {
554 		printf("Commands may be abbreviated.  Commands are:\n\n");
555 		for (c = cmdtab; c->name; c++)
556 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
557 		return;
558 	}
559 	while (--argc > 0) {
560 		register char *arg;
561 		arg = *++argv;
562 		c = getcmd(arg);
563 		if (c == (struct cmd *)-1)
564 			printf("?Ambiguous help command %s\n", arg);
565 		else if (c == (struct cmd *)0)
566 			printf("?Invalid help command %s\n", arg);
567 		else
568 			printf("%s\n", c->help);
569 	}
570 }
571 
572 /*VARARGS*/
573 settrace()
574 {
575 	trace = !trace;
576 	printf("Packet tracing %s.\n", trace ? "on" : "off");
577 }
578