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