Annotation of src/usr.bin/tftp/main.c, Revision 1.13
1.13 ! millert 1: /* $OpenBSD: main.c,v 1.12 2003/04/17 17:17:27 henning Exp $ */
1.1 deraadt 2: /* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */
3:
4: /*
5: * Copyright (c) 1983, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.13 ! millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: #ifndef lint
1.12 henning 34: static const char copyright[] =
1.1 deraadt 35: "@(#) Copyright (c) 1983, 1993\n\
36: The Regents of the University of California. All rights reserved.\n";
37: #endif /* not lint */
38:
39: #ifndef lint
40: #if 0
41: static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
42: #endif
1.13 ! millert 43: static const char rcsid[] = "$OpenBSD: main.c,v 1.12 2003/04/17 17:17:27 henning Exp $";
1.1 deraadt 44: #endif /* not lint */
45:
46: /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
47:
48: /*
49: * TFTP User Program -- Command Interface.
50: */
51: #include <sys/types.h>
52: #include <sys/socket.h>
53: #include <sys/file.h>
54:
55: #include <netinet/in.h>
56:
57: #include <arpa/inet.h>
58:
59: #include <ctype.h>
60: #include <errno.h>
61: #include <netdb.h>
62: #include <setjmp.h>
63: #include <signal.h>
64: #include <stdio.h>
65: #include <stdlib.h>
66: #include <string.h>
67: #include <unistd.h>
1.6 mickey 68: #include <err.h>
1.1 deraadt 69:
70: #include "extern.h"
71:
72: #define TIMEOUT 5 /* secs between rexmt's */
73: #define LBUFLEN 200 /* size of input buffer */
1.5 deraadt 74: #define MAXARGV 20
1.1 deraadt 75:
76: struct sockaddr_in peeraddr;
77: int f;
78: short port;
79: int trace;
80: int verbose;
81: int connected;
82: char mode[32];
83: char line[LBUFLEN];
84: int margc;
1.5 deraadt 85: char *margv[MAXARGV+1];
1.1 deraadt 86: char *prompt = "tftp";
87: jmp_buf toplevel;
88: void intr();
89: struct servent *sp;
90:
1.10 millert 91: void get(int, char **);
92: void help(int, char **);
93: void modecmd(int, char **);
94: void put(int, char **);
95: void quit(int, char **);
96: void setascii(int, char **);
97: void setbinary(int, char **);
98: void setpeer(int, char **);
99: void setrexmt(int, char **);
100: void settimeout(int, char **);
101: void settrace(int, char **);
102: void setverbose(int, char **);
103: void status(int, char **);
104:
105: static __dead void command(void);
106:
107: static void getusage(char *);
108: static int makeargv(void);
109: static void putusage(char *);
110: static void settftpmode(char *);
1.1 deraadt 111:
112: #define HELPINDENT (sizeof("connect"))
113:
114: struct cmd {
115: char *name;
116: char *help;
1.10 millert 117: void (*handler)(int, char **);
1.1 deraadt 118: };
119:
120: char vhelp[] = "toggle verbose mode";
121: char thelp[] = "toggle packet tracing";
122: char chelp[] = "connect to remote tftp";
123: char qhelp[] = "exit tftp";
124: char hhelp[] = "print help information";
125: char shelp[] = "send file";
126: char rhelp[] = "receive file";
127: char mhelp[] = "set file transfer mode";
128: char sthelp[] = "show current status";
129: char xhelp[] = "set per-packet retransmission timeout";
130: char ihelp[] = "set total retransmission timeout";
131: char ashelp[] = "set mode to netascii";
132: char bnhelp[] = "set mode to octet";
133:
134: struct cmd cmdtab[] = {
135: { "connect", chelp, setpeer },
136: { "mode", mhelp, modecmd },
137: { "put", shelp, put },
138: { "get", rhelp, get },
139: { "quit", qhelp, quit },
140: { "verbose", vhelp, setverbose },
141: { "trace", thelp, settrace },
142: { "status", sthelp, status },
143: { "binary", bnhelp, setbinary },
144: { "ascii", ashelp, setascii },
145: { "rexmt", xhelp, setrexmt },
146: { "timeout", ihelp, settimeout },
147: { "?", hhelp, help },
1.8 mpech 148: { NULL, NULL, NULL }
1.1 deraadt 149: };
150:
151: struct cmd *getcmd();
152: char *tail();
153:
154: int
155: main(argc, argv)
156: int argc;
157: char *argv[];
158: {
159: struct sockaddr_in s_in;
160:
161: sp = getservbyname("tftp", "udp");
1.6 mickey 162: if (sp == 0)
163: errx(1, "udp/tftp: unknown service");
1.1 deraadt 164: f = socket(AF_INET, SOCK_DGRAM, 0);
1.6 mickey 165: if (f < 0)
166: err(3, "tftp: socket");
1.1 deraadt 167: bzero((char *)&s_in, sizeof (s_in));
168: s_in.sin_family = AF_INET;
1.6 mickey 169: if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0)
170: err(1, "tftp: bind");
1.11 deraadt 171: strlcpy(mode, "netascii", sizeof mode);
1.1 deraadt 172: signal(SIGINT, intr);
173: if (argc > 1) {
174: if (setjmp(toplevel) != 0)
175: exit(0);
176: setpeer(argc, argv);
177: }
178: if (setjmp(toplevel) != 0)
179: (void)putchar('\n');
180: command();
1.7 pvalchev 181: return (0);
1.1 deraadt 182: }
183:
1.3 deraadt 184: char hostname[MAXHOSTNAMELEN];
1.1 deraadt 185:
186: void
187: setpeer(argc, argv)
188: int argc;
189: char *argv[];
190: {
191: struct hostent *host;
192:
193: if (argc < 2) {
1.11 deraadt 194: strlcpy(line, "Connect ", sizeof line);
1.1 deraadt 195: printf("(to) ");
196: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
1.5 deraadt 197: if (makeargv())
198: return;
1.1 deraadt 199: argc = margc;
200: argv = margv;
201: }
202: if ((argc < 2) || (argc > 3)) {
203: printf("usage: %s host-name [port]\n", argv[0]);
204: return;
205: }
206: if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) {
207: peeraddr.sin_family = AF_INET;
1.3 deraadt 208: (void) strncpy(hostname, argv[1], sizeof hostname);
209: hostname[sizeof(hostname)-1] = '\0';
1.1 deraadt 210: } else {
211: host = gethostbyname(argv[1]);
212: if (host == 0) {
213: connected = 0;
214: printf("%s: unknown host\n", argv[1]);
215: return;
216: }
217: peeraddr.sin_family = host->h_addrtype;
218: bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
1.11 deraadt 219: (void) strlcpy(hostname, host->h_name, sizeof hostname);
1.1 deraadt 220: }
221: port = sp->s_port;
222: if (argc == 3) {
223: port = atoi(argv[2]);
224: if (port < 0) {
225: printf("%s: bad port number\n", argv[2]);
226: connected = 0;
227: return;
228: }
229: port = htons(port);
230: }
231: connected = 1;
232: }
233:
234: struct modes {
235: char *m_name;
236: char *m_mode;
237: } modes[] = {
238: { "ascii", "netascii" },
239: { "netascii", "netascii" },
240: { "binary", "octet" },
241: { "image", "octet" },
242: { "octet", "octet" },
243: /* { "mail", "mail" }, */
1.8 mpech 244: { NULL, NULL }
1.1 deraadt 245: };
246:
247: void
248: modecmd(argc, argv)
249: int argc;
250: char *argv[];
251: {
1.9 mpech 252: struct modes *p;
1.1 deraadt 253: char *sep;
254:
255: if (argc < 2) {
256: printf("Using %s mode to transfer files.\n", mode);
257: return;
258: }
259: if (argc == 2) {
1.8 mpech 260: for (p = modes; p->m_name != NULL; p++)
1.1 deraadt 261: if (strcmp(argv[1], p->m_name) == 0)
262: break;
263: if (p->m_name) {
264: settftpmode(p->m_mode);
265: return;
266: }
267: printf("%s: unknown mode\n", argv[1]);
268: /* drop through and print usage message */
269: }
270:
271: printf("usage: %s [", argv[0]);
272: sep = " ";
1.8 mpech 273: for (p = modes; p->m_name != NULL; p++) {
1.1 deraadt 274: printf("%s%s", sep, p->m_name);
275: if (*sep == ' ')
276: sep = " | ";
277: }
278: printf(" ]\n");
279: return;
280: }
281:
282: void
283: setbinary(argc, argv)
284: int argc;
285: char *argv[];
1.6 mickey 286: {
1.1 deraadt 287:
288: settftpmode("octet");
289: }
290:
291: void
292: setascii(argc, argv)
293: int argc;
294: char *argv[];
295: {
296:
297: settftpmode("netascii");
298: }
299:
300: static void
301: settftpmode(newmode)
302: char *newmode;
303: {
1.11 deraadt 304: strlcpy(mode, newmode, sizeof mode);
1.1 deraadt 305: if (verbose)
306: printf("mode set to %s\n", mode);
307: }
308:
309:
310: /*
311: * Send file(s).
312: */
313: void
314: put(argc, argv)
315: int argc;
316: char *argv[];
317: {
318: int fd;
1.9 mpech 319: int n;
320: char *cp, *targ;
1.1 deraadt 321:
322: if (argc < 2) {
1.11 deraadt 323: strlcpy(line, "send ", sizeof line);
1.1 deraadt 324: printf("(file) ");
325: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
1.5 deraadt 326: if (makeargv())
327: return;
1.1 deraadt 328: argc = margc;
329: argv = margv;
330: }
331: if (argc < 2) {
332: putusage(argv[0]);
333: return;
334: }
335: targ = argv[argc - 1];
1.4 millert 336: if (strchr(argv[argc - 1], ':')) {
1.1 deraadt 337: char *cp;
338: struct hostent *hp;
339:
340: for (n = 1; n < argc - 1; n++)
1.4 millert 341: if (strchr(argv[n], ':')) {
1.1 deraadt 342: putusage(argv[0]);
343: return;
344: }
345: cp = argv[argc - 1];
1.4 millert 346: targ = strchr(cp, ':');
1.1 deraadt 347: *targ++ = 0;
348: hp = gethostbyname(cp);
349: if (hp == NULL) {
1.6 mickey 350: warnx("%s: %s", cp, hstrerror(h_errno));
1.1 deraadt 351: return;
352: }
353: bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
354: peeraddr.sin_family = hp->h_addrtype;
355: connected = 1;
1.11 deraadt 356: strlcpy(hostname, hp->h_name, sizeof hostname);
1.1 deraadt 357: }
358: if (!connected) {
359: printf("No target machine specified.\n");
360: return;
361: }
362: if (argc < 4) {
363: cp = argc == 2 ? tail(targ) : argv[1];
364: fd = open(cp, O_RDONLY);
365: if (fd < 0) {
1.6 mickey 366: warn("open: %s", cp);
1.1 deraadt 367: return;
368: }
369: if (verbose)
370: printf("putting %s to %s:%s [%s]\n",
371: cp, hostname, targ, mode);
372: peeraddr.sin_port = port;
373: sendfile(fd, targ, mode);
374: return;
375: }
376: /* this assumes the target is a directory */
377: /* on a remote unix system. hmmmm. */
378: for (n = 1; n < argc - 1; n++) {
1.12 henning 379: if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
380: err(1, "asprintf");
1.1 deraadt 381: fd = open(argv[n], O_RDONLY);
382: if (fd < 0) {
1.6 mickey 383: warn("open: %s", argv[n]);
1.1 deraadt 384: continue;
385: }
386: if (verbose)
387: printf("putting %s to %s:%s [%s]\n",
1.12 henning 388: argv[n], hostname, cp, mode);
1.1 deraadt 389: peeraddr.sin_port = port;
1.12 henning 390: sendfile(fd, cp, mode);
391: free(cp);
1.1 deraadt 392: }
393: }
394:
395: static void
396: putusage(s)
397: char *s;
398: {
399: printf("usage: %s file ... host:target, or\n", s);
400: printf(" %s file ... target (when already connected)\n", s);
401: }
402:
403: /*
404: * Receive file(s).
405: */
406: void
407: get(argc, argv)
408: int argc;
409: char *argv[];
410: {
411: int fd;
1.9 mpech 412: int n;
413: char *cp;
1.1 deraadt 414: char *src;
415:
416: if (argc < 2) {
1.11 deraadt 417: strlcpy(line, "get ", sizeof line);
1.1 deraadt 418: printf("(files) ");
419: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
1.5 deraadt 420: if (makeargv())
421: return;
1.1 deraadt 422: argc = margc;
423: argv = margv;
424: }
425: if (argc < 2) {
426: getusage(argv[0]);
427: return;
428: }
429: if (!connected) {
430: for (n = 1; n < argc ; n++)
1.4 millert 431: if (strchr(argv[n], ':') == 0) {
1.1 deraadt 432: getusage(argv[0]);
433: return;
434: }
435: }
436: for (n = 1; n < argc ; n++) {
1.4 millert 437: src = strchr(argv[n], ':');
1.1 deraadt 438: if (src == NULL)
439: src = argv[n];
440: else {
441: struct hostent *hp;
442:
443: *src++ = 0;
444: hp = gethostbyname(argv[n]);
445: if (hp == NULL) {
1.6 mickey 446: warnx("%s: %s", argv[n], hstrerror(h_errno));
1.1 deraadt 447: continue;
448: }
449: bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
450: hp->h_length);
451: peeraddr.sin_family = hp->h_addrtype;
452: connected = 1;
1.11 deraadt 453: strlcpy(hostname, hp->h_name, sizeof hostname);
1.1 deraadt 454: }
455: if (argc < 4) {
456: cp = argc == 3 ? argv[2] : tail(src);
457: fd = creat(cp, 0644);
458: if (fd < 0) {
1.6 mickey 459: warn("create: %s", cp);
1.1 deraadt 460: return;
461: }
462: if (verbose)
463: printf("getting from %s:%s to %s [%s]\n",
464: hostname, src, cp, mode);
465: peeraddr.sin_port = port;
466: recvfile(fd, src, mode);
467: break;
468: }
469: cp = tail(src); /* new .. jdg */
470: fd = creat(cp, 0644);
471: if (fd < 0) {
1.6 mickey 472: warn("create: %s", cp);
1.1 deraadt 473: continue;
474: }
475: if (verbose)
476: printf("getting from %s:%s to %s [%s]\n",
477: hostname, src, cp, mode);
478: peeraddr.sin_port = port;
479: recvfile(fd, src, mode);
480: }
481: }
482:
483: static void
484: getusage(s)
485: char *s;
486: {
487: printf("usage: %s host:file host:file ... file, or\n", s);
488: printf(" %s file file ... file if connected\n", s);
489: }
490:
491: int rexmtval = TIMEOUT;
492:
493: void
494: setrexmt(argc, argv)
495: int argc;
496: char *argv[];
497: {
498: int t;
499:
500: if (argc < 2) {
1.11 deraadt 501: strlcpy(line, "Rexmt-timeout ", sizeof line);
1.1 deraadt 502: printf("(value) ");
503: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
1.5 deraadt 504: if (makeargv())
505: return;
1.1 deraadt 506: argc = margc;
507: argv = margv;
508: }
509: if (argc != 2) {
510: printf("usage: %s value\n", argv[0]);
511: return;
512: }
513: t = atoi(argv[1]);
514: if (t < 0)
515: printf("%s: bad value\n", argv[1]);
516: else
517: rexmtval = t;
518: }
519:
520: int maxtimeout = 5 * TIMEOUT;
521:
522: void
523: settimeout(argc, argv)
524: int argc;
525: char *argv[];
526: {
527: int t;
528:
529: if (argc < 2) {
1.11 deraadt 530: strlcpy(line, "Maximum-timeout ", sizeof line);
1.1 deraadt 531: printf("(value) ");
532: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
1.5 deraadt 533: if (makeargv())
534: return;
1.1 deraadt 535: argc = margc;
536: argv = margv;
537: }
538: if (argc != 2) {
539: printf("usage: %s value\n", argv[0]);
540: return;
541: }
542: t = atoi(argv[1]);
543: if (t < 0)
544: printf("%s: bad value\n", argv[1]);
545: else
546: maxtimeout = t;
547: }
548:
549: void
550: status(argc, argv)
551: int argc;
552: char *argv[];
553: {
554: if (connected)
555: printf("Connected to %s.\n", hostname);
556: else
557: printf("Not connected.\n");
558: printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
559: verbose ? "on" : "off", trace ? "on" : "off");
560: printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
561: rexmtval, maxtimeout);
562: }
563:
564: void
565: intr()
566: {
567:
568: signal(SIGALRM, SIG_IGN);
569: alarm(0);
570: longjmp(toplevel, -1);
571: }
572:
573: char *
574: tail(filename)
575: char *filename;
576: {
1.9 mpech 577: char *s;
1.6 mickey 578:
1.1 deraadt 579: while (*filename) {
1.4 millert 580: s = strrchr(filename, '/');
1.1 deraadt 581: if (s == NULL)
582: break;
583: if (s[1])
584: return (s + 1);
585: *s = '\0';
586: }
587: return (filename);
588: }
589:
590: /*
591: * Command parser.
592: */
593: static __dead void
594: command()
595: {
1.9 mpech 596: struct cmd *c;
1.1 deraadt 597:
598: for (;;) {
599: printf("%s> ", prompt);
600: if (fgets(line, LBUFLEN, stdin) == 0) {
601: if (feof(stdin)) {
602: exit(0);
603: } else {
604: continue;
605: }
606: }
607: if ((line[0] == 0) || (line[0] == '\n'))
608: continue;
1.5 deraadt 609: if (makeargv())
610: continue;
1.1 deraadt 611: if (margc == 0)
612: continue;
613: c = getcmd(margv[0]);
614: if (c == (struct cmd *)-1) {
615: printf("?Ambiguous command\n");
616: continue;
617: }
618: if (c == 0) {
619: printf("?Invalid command\n");
620: continue;
621: }
622: (*c->handler)(margc, margv);
623: }
624: }
625:
626: struct cmd *
627: getcmd(name)
1.9 mpech 628: char *name;
1.1 deraadt 629: {
1.9 mpech 630: char *p, *q;
631: struct cmd *c, *found;
632: int nmatches, longest;
1.1 deraadt 633:
634: longest = 0;
635: nmatches = 0;
636: found = 0;
637: for (c = cmdtab; (p = c->name) != NULL; c++) {
638: for (q = name; *q == *p++; q++)
639: if (*q == 0) /* exact match? */
640: return (c);
641: if (!*q) { /* the name was a prefix */
642: if (q - name > longest) {
643: longest = q - name;
644: nmatches = 1;
645: found = c;
646: } else if (q - name == longest)
647: nmatches++;
648: }
649: }
650: if (nmatches > 1)
651: return ((struct cmd *)-1);
652: return (found);
653: }
654:
655: /*
656: * Slice a string up into argc/argv.
657: */
1.5 deraadt 658: static int
1.1 deraadt 659: makeargv()
660: {
1.9 mpech 661: char *cp;
662: char **argp = margv;
1.5 deraadt 663: int ret = 0;
1.1 deraadt 664:
665: margc = 0;
666: for (cp = line; *cp;) {
1.5 deraadt 667: if (margc >= MAXARGV) {
668: printf("too many arguments\n");
669: ret = 1;
670: break;
671: }
1.1 deraadt 672: while (isspace(*cp))
673: cp++;
674: if (*cp == '\0')
675: break;
676: *argp++ = cp;
677: margc += 1;
678: while (*cp != '\0' && !isspace(*cp))
679: cp++;
680: if (*cp == '\0')
681: break;
682: *cp++ = '\0';
683: }
684: *argp++ = 0;
1.5 deraadt 685: return (ret);
1.1 deraadt 686: }
687:
688: void
689: quit(argc, argv)
690: int argc;
691: char *argv[];
692: {
693:
694: exit(0);
695: }
696:
697: /*
698: * Help command.
699: */
700: void
701: help(argc, argv)
702: int argc;
703: char *argv[];
704: {
1.9 mpech 705: struct cmd *c;
1.1 deraadt 706:
707: if (argc == 1) {
708: printf("Commands may be abbreviated. Commands are:\n\n");
1.8 mpech 709: for (c = cmdtab; c->name != NULL; c++)
1.1 deraadt 710: printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
711: return;
712: }
713: while (--argc > 0) {
1.9 mpech 714: char *arg;
1.1 deraadt 715: arg = *++argv;
716: c = getcmd(arg);
717: if (c == (struct cmd *)-1)
718: printf("?Ambiguous help command %s\n", arg);
719: else if (c == (struct cmd *)0)
720: printf("?Invalid help command %s\n", arg);
721: else
722: printf("%s\n", c->help);
723: }
724: }
725:
726: void
727: settrace(argc, argv)
728: int argc;
729: char **argv;
730: {
731: trace = !trace;
732: printf("Packet tracing %s.\n", trace ? "on" : "off");
733: }
734:
735: void
736: setverbose(argc, argv)
737: int argc;
738: char **argv;
739: {
740: verbose = !verbose;
741: printf("Verbose mode %s.\n", verbose ? "on" : "off");
742: }