xref: /original-bsd/usr.bin/tftp/main.c (revision 8251a00e)
1 #ifndef lint
2 static char sccsid[] = "@(#)main.c	4.7 (Berkeley) 08/11/83";
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 	longjmp(toplevel, -1);
429 }
430 
431 char *
432 tail(filename)
433 	char *filename;
434 {
435 	register char *s;
436 
437 	while (*filename) {
438 		s = rindex(filename, '/');
439 		if (s == NULL)
440 			break;
441 		if (s[1])
442 			return (s + 1);
443 		*s = '\0';
444 	}
445 	return (filename);
446 }
447 
448 /*
449  * Command parser.
450  */
451 command(top)
452 	int top;
453 {
454 	register struct cmd *c;
455 
456 	if (!top)
457 		putchar('\n');
458 	for (;;) {
459 		printf("%s> ", prompt);
460 		if (gets(line) == 0)
461 			continue;
462 		if (line[0] == 0)
463 			continue;
464 		makeargv();
465 		c = getcmd(margv[0]);
466 		if (c == (struct cmd *)-1) {
467 			printf("?Ambiguous command\n");
468 			continue;
469 		}
470 		if (c == 0) {
471 			printf("?Invalid command\n");
472 			continue;
473 		}
474 		(*c->handler)(margc, margv);
475 	}
476 }
477 
478 struct cmd *
479 getcmd(name)
480 	register char *name;
481 {
482 	register char *p, *q;
483 	register struct cmd *c, *found;
484 	register int nmatches, longest;
485 
486 	longest = 0;
487 	nmatches = 0;
488 	found = 0;
489 	for (c = cmdtab; p = c->name; c++) {
490 		for (q = name; *q == *p++; q++)
491 			if (*q == 0)		/* exact match? */
492 				return (c);
493 		if (!*q) {			/* the name was a prefix */
494 			if (q - name > longest) {
495 				longest = q - name;
496 				nmatches = 1;
497 				found = c;
498 			} else if (q - name == longest)
499 				nmatches++;
500 		}
501 	}
502 	if (nmatches > 1)
503 		return ((struct cmd *)-1);
504 	return (found);
505 }
506 
507 /*
508  * Slice a string up into argc/argv.
509  */
510 makeargv()
511 {
512 	register char *cp;
513 	register char **argp = margv;
514 
515 	margc = 0;
516 	for (cp = line; *cp;) {
517 		while (isspace(*cp))
518 			cp++;
519 		if (*cp == '\0')
520 			break;
521 		*argp++ = cp;
522 		margc += 1;
523 		while (*cp != '\0' && !isspace(*cp))
524 			cp++;
525 		if (*cp == '\0')
526 			break;
527 		*cp++ = '\0';
528 	}
529 	*argp++ = 0;
530 }
531 
532 /*VARARGS*/
533 quit()
534 {
535 	exit(0);
536 }
537 
538 /*
539  * Help command.
540  */
541 help(argc, argv)
542 	int argc;
543 	char *argv[];
544 {
545 	register struct cmd *c;
546 
547 	if (argc == 1) {
548 		printf("Commands may be abbreviated.  Commands are:\n\n");
549 		for (c = cmdtab; c->name; c++)
550 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
551 		return;
552 	}
553 	while (--argc > 0) {
554 		register char *arg;
555 		arg = *++argv;
556 		c = getcmd(arg);
557 		if (c == (struct cmd *)-1)
558 			printf("?Ambiguous help command %s\n", arg);
559 		else if (c == (struct cmd *)0)
560 			printf("?Invalid help command %s\n", arg);
561 		else
562 			printf("%s\n", c->help);
563 	}
564 }
565 
566 /*
567  * Call routine with argc, argv set from args (terminated by 0).
568  */
569 /* VARARGS2 */
570 call(routine, args)
571 	int (*routine)();
572 	int args;
573 {
574 	register int *argp;
575 	register int argc;
576 
577 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
578 		;
579 	(*routine)(argc, &args);
580 }
581 
582 /*VARARGS*/
583 settrace()
584 {
585 	trace = !trace;
586 	printf("Packet tracing %s.\n", trace ? "on" : "off");
587 }
588 
589 /*VARARGS*/
590 setverbose()
591 {
592 	verbose = !verbose;
593 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
594 }
595