Annotation of src/usr.bin/tftp/main.c, Revision 1.4
1.4 ! millert 1: /* $OpenBSD: main.c,v 1.3 1996/08/16 23:31:00 deraadt 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.4 ! millert 47: static char rcsid[] = "$OpenBSD: main.c,v 1.3 1996/08/16 23:31:00 deraadt 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>
72:
73: #include "extern.h"
74:
75: #define TIMEOUT 5 /* secs between rexmt's */
76: #define LBUFLEN 200 /* size of input buffer */
77:
78: struct sockaddr_in peeraddr;
79: int f;
80: short port;
81: int trace;
82: int verbose;
83: int connected;
84: char mode[32];
85: char line[LBUFLEN];
86: int margc;
87: char *margv[20];
88: char *prompt = "tftp";
89: jmp_buf toplevel;
90: void intr();
91: struct servent *sp;
92:
93: void get __P((int, char **));
94: void help __P((int, char **));
95: void modecmd __P((int, char **));
96: void put __P((int, char **));
97: void quit __P((int, char **));
98: void setascii __P((int, char **));
99: void setbinary __P((int, char **));
100: void setpeer __P((int, char **));
101: void setrexmt __P((int, char **));
102: void settimeout __P((int, char **));
103: void settrace __P((int, char **));
104: void setverbose __P((int, char **));
105: void status __P((int, char **));
106:
107: static __dead void command __P((void));
108:
109: static void getusage __P((char *));
110: static void makeargv __P((void));
111: static void putusage __P((char *));
112: static void settftpmode __P((char *));
113:
114: #define HELPINDENT (sizeof("connect"))
115:
116: struct cmd {
117: char *name;
118: char *help;
119: void (*handler) __P((int, char **));
120: };
121:
122: char vhelp[] = "toggle verbose mode";
123: char thelp[] = "toggle packet tracing";
124: char chelp[] = "connect to remote tftp";
125: char qhelp[] = "exit tftp";
126: char hhelp[] = "print help information";
127: char shelp[] = "send file";
128: char rhelp[] = "receive file";
129: char mhelp[] = "set file transfer mode";
130: char sthelp[] = "show current status";
131: char xhelp[] = "set per-packet retransmission timeout";
132: char ihelp[] = "set total retransmission timeout";
133: char ashelp[] = "set mode to netascii";
134: char bnhelp[] = "set mode to octet";
135:
136: struct cmd cmdtab[] = {
137: { "connect", chelp, setpeer },
138: { "mode", mhelp, modecmd },
139: { "put", shelp, put },
140: { "get", rhelp, get },
141: { "quit", qhelp, quit },
142: { "verbose", vhelp, setverbose },
143: { "trace", thelp, settrace },
144: { "status", sthelp, status },
145: { "binary", bnhelp, setbinary },
146: { "ascii", ashelp, setascii },
147: { "rexmt", xhelp, setrexmt },
148: { "timeout", ihelp, settimeout },
149: { "?", hhelp, help },
150: { 0 }
151: };
152:
153: struct cmd *getcmd();
154: char *tail();
155:
156: int
157: main(argc, argv)
158: int argc;
159: char *argv[];
160: {
161: struct sockaddr_in s_in;
162:
163: sp = getservbyname("tftp", "udp");
164: if (sp == 0) {
165: fprintf(stderr, "tftp: udp/tftp: unknown service\n");
166: exit(1);
167: }
168: f = socket(AF_INET, SOCK_DGRAM, 0);
169: if (f < 0) {
170: perror("tftp: socket");
171: exit(3);
172: }
173: bzero((char *)&s_in, sizeof (s_in));
174: s_in.sin_family = AF_INET;
175: if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) {
176: perror("tftp: bind");
177: exit(1);
178: }
179: strcpy(mode, "netascii");
180: signal(SIGINT, intr);
181: if (argc > 1) {
182: if (setjmp(toplevel) != 0)
183: exit(0);
184: setpeer(argc, argv);
185: }
186: if (setjmp(toplevel) != 0)
187: (void)putchar('\n');
188: command();
189: }
190:
1.3 deraadt 191: char hostname[MAXHOSTNAMELEN];
1.1 deraadt 192:
193: void
194: setpeer(argc, argv)
195: int argc;
196: char *argv[];
197: {
198: struct hostent *host;
199:
200: if (argc < 2) {
201: strcpy(line, "Connect ");
202: printf("(to) ");
203: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
204: makeargv();
205: argc = margc;
206: argv = margv;
207: }
208: if ((argc < 2) || (argc > 3)) {
209: printf("usage: %s host-name [port]\n", argv[0]);
210: return;
211: }
212: if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) {
213: peeraddr.sin_family = AF_INET;
1.3 deraadt 214: (void) strncpy(hostname, argv[1], sizeof hostname);
215: hostname[sizeof(hostname)-1] = '\0';
1.1 deraadt 216: } else {
217: host = gethostbyname(argv[1]);
218: if (host == 0) {
219: connected = 0;
220: printf("%s: unknown host\n", argv[1]);
221: return;
222: }
223: peeraddr.sin_family = host->h_addrtype;
224: bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
225: (void) strcpy(hostname, host->h_name);
226: }
227: port = sp->s_port;
228: if (argc == 3) {
229: port = atoi(argv[2]);
230: if (port < 0) {
231: printf("%s: bad port number\n", argv[2]);
232: connected = 0;
233: return;
234: }
235: port = htons(port);
236: }
237: connected = 1;
238: }
239:
240: struct modes {
241: char *m_name;
242: char *m_mode;
243: } modes[] = {
244: { "ascii", "netascii" },
245: { "netascii", "netascii" },
246: { "binary", "octet" },
247: { "image", "octet" },
248: { "octet", "octet" },
249: /* { "mail", "mail" }, */
250: { 0, 0 }
251: };
252:
253: void
254: modecmd(argc, argv)
255: int argc;
256: char *argv[];
257: {
258: register struct modes *p;
259: char *sep;
260:
261: if (argc < 2) {
262: printf("Using %s mode to transfer files.\n", mode);
263: return;
264: }
265: if (argc == 2) {
266: for (p = modes; p->m_name; p++)
267: if (strcmp(argv[1], p->m_name) == 0)
268: break;
269: if (p->m_name) {
270: settftpmode(p->m_mode);
271: return;
272: }
273: printf("%s: unknown mode\n", argv[1]);
274: /* drop through and print usage message */
275: }
276:
277: printf("usage: %s [", argv[0]);
278: sep = " ";
279: for (p = modes; p->m_name; p++) {
280: printf("%s%s", sep, p->m_name);
281: if (*sep == ' ')
282: sep = " | ";
283: }
284: printf(" ]\n");
285: return;
286: }
287:
288: void
289: setbinary(argc, argv)
290: int argc;
291: char *argv[];
292: {
293:
294: settftpmode("octet");
295: }
296:
297: void
298: setascii(argc, argv)
299: int argc;
300: char *argv[];
301: {
302:
303: settftpmode("netascii");
304: }
305:
306: static void
307: settftpmode(newmode)
308: char *newmode;
309: {
310: strcpy(mode, newmode);
311: if (verbose)
312: printf("mode set to %s\n", mode);
313: }
314:
315:
316: /*
317: * Send file(s).
318: */
319: void
320: put(argc, argv)
321: int argc;
322: char *argv[];
323: {
324: int fd;
325: register int n;
326: register char *cp, *targ;
327:
328: if (argc < 2) {
329: strcpy(line, "send ");
330: printf("(file) ");
331: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
332: makeargv();
333: argc = margc;
334: argv = margv;
335: }
336: if (argc < 2) {
337: putusage(argv[0]);
338: return;
339: }
340: targ = argv[argc - 1];
1.4 ! millert 341: if (strchr(argv[argc - 1], ':')) {
1.1 deraadt 342: char *cp;
343: struct hostent *hp;
344:
345: for (n = 1; n < argc - 1; n++)
1.4 ! millert 346: if (strchr(argv[n], ':')) {
1.1 deraadt 347: putusage(argv[0]);
348: return;
349: }
350: cp = argv[argc - 1];
1.4 ! millert 351: targ = strchr(cp, ':');
1.1 deraadt 352: *targ++ = 0;
353: hp = gethostbyname(cp);
354: if (hp == NULL) {
355: fprintf(stderr, "tftp: %s: ", cp);
356: herror((char *)NULL);
357: return;
358: }
359: bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
360: peeraddr.sin_family = hp->h_addrtype;
361: connected = 1;
362: strcpy(hostname, hp->h_name);
363: }
364: if (!connected) {
365: printf("No target machine specified.\n");
366: return;
367: }
368: if (argc < 4) {
369: cp = argc == 2 ? tail(targ) : argv[1];
370: fd = open(cp, O_RDONLY);
371: if (fd < 0) {
372: fprintf(stderr, "tftp: "); perror(cp);
373: return;
374: }
375: if (verbose)
376: printf("putting %s to %s:%s [%s]\n",
377: cp, hostname, targ, mode);
378: peeraddr.sin_port = port;
379: sendfile(fd, targ, mode);
380: return;
381: }
382: /* this assumes the target is a directory */
383: /* on a remote unix system. hmmmm. */
1.4 ! millert 384: cp = strchr(targ, '\0');
1.1 deraadt 385: *cp++ = '/';
386: for (n = 1; n < argc - 1; n++) {
387: strcpy(cp, tail(argv[n]));
388: fd = open(argv[n], O_RDONLY);
389: if (fd < 0) {
390: fprintf(stderr, "tftp: "); perror(argv[n]);
391: continue;
392: }
393: if (verbose)
394: printf("putting %s to %s:%s [%s]\n",
395: argv[n], hostname, targ, mode);
396: peeraddr.sin_port = port;
397: sendfile(fd, targ, mode);
398: }
399: }
400:
401: static void
402: putusage(s)
403: char *s;
404: {
405: printf("usage: %s file ... host:target, or\n", s);
406: printf(" %s file ... target (when already connected)\n", s);
407: }
408:
409: /*
410: * Receive file(s).
411: */
412: void
413: get(argc, argv)
414: int argc;
415: char *argv[];
416: {
417: int fd;
418: register int n;
419: register char *cp;
420: char *src;
421:
422: if (argc < 2) {
423: strcpy(line, "get ");
424: printf("(files) ");
425: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
426: makeargv();
427: argc = margc;
428: argv = margv;
429: }
430: if (argc < 2) {
431: getusage(argv[0]);
432: return;
433: }
434: if (!connected) {
435: for (n = 1; n < argc ; n++)
1.4 ! millert 436: if (strchr(argv[n], ':') == 0) {
1.1 deraadt 437: getusage(argv[0]);
438: return;
439: }
440: }
441: for (n = 1; n < argc ; n++) {
1.4 ! millert 442: src = strchr(argv[n], ':');
1.1 deraadt 443: if (src == NULL)
444: src = argv[n];
445: else {
446: struct hostent *hp;
447:
448: *src++ = 0;
449: hp = gethostbyname(argv[n]);
450: if (hp == NULL) {
451: fprintf(stderr, "tftp: %s: ", argv[n]);
452: herror((char *)NULL);
453: continue;
454: }
455: bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
456: hp->h_length);
457: peeraddr.sin_family = hp->h_addrtype;
458: connected = 1;
459: strcpy(hostname, hp->h_name);
460: }
461: if (argc < 4) {
462: cp = argc == 3 ? argv[2] : tail(src);
463: fd = creat(cp, 0644);
464: if (fd < 0) {
465: fprintf(stderr, "tftp: "); perror(cp);
466: return;
467: }
468: if (verbose)
469: printf("getting from %s:%s to %s [%s]\n",
470: hostname, src, cp, mode);
471: peeraddr.sin_port = port;
472: recvfile(fd, src, mode);
473: break;
474: }
475: cp = tail(src); /* new .. jdg */
476: fd = creat(cp, 0644);
477: if (fd < 0) {
478: fprintf(stderr, "tftp: "); perror(cp);
479: continue;
480: }
481: if (verbose)
482: printf("getting from %s:%s to %s [%s]\n",
483: hostname, src, cp, mode);
484: peeraddr.sin_port = port;
485: recvfile(fd, src, mode);
486: }
487: }
488:
489: static void
490: getusage(s)
491: char *s;
492: {
493: printf("usage: %s host:file host:file ... file, or\n", s);
494: printf(" %s file file ... file if connected\n", s);
495: }
496:
497: int rexmtval = TIMEOUT;
498:
499: void
500: setrexmt(argc, argv)
501: int argc;
502: char *argv[];
503: {
504: int t;
505:
506: if (argc < 2) {
507: strcpy(line, "Rexmt-timeout ");
508: printf("(value) ");
509: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
510: makeargv();
511: argc = margc;
512: argv = margv;
513: }
514: if (argc != 2) {
515: printf("usage: %s value\n", argv[0]);
516: return;
517: }
518: t = atoi(argv[1]);
519: if (t < 0)
520: printf("%s: bad value\n", argv[1]);
521: else
522: rexmtval = t;
523: }
524:
525: int maxtimeout = 5 * TIMEOUT;
526:
527: void
528: settimeout(argc, argv)
529: int argc;
530: char *argv[];
531: {
532: int t;
533:
534: if (argc < 2) {
535: strcpy(line, "Maximum-timeout ");
536: printf("(value) ");
537: fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
538: makeargv();
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;
582:
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;
613: makeargv();
614: if (margc == 0)
615: continue;
616: c = getcmd(margv[0]);
617: if (c == (struct cmd *)-1) {
618: printf("?Ambiguous command\n");
619: continue;
620: }
621: if (c == 0) {
622: printf("?Invalid command\n");
623: continue;
624: }
625: (*c->handler)(margc, margv);
626: }
627: }
628:
629: struct cmd *
630: getcmd(name)
631: register char *name;
632: {
633: register char *p, *q;
634: register struct cmd *c, *found;
635: register int nmatches, longest;
636:
637: longest = 0;
638: nmatches = 0;
639: found = 0;
640: for (c = cmdtab; (p = c->name) != NULL; c++) {
641: for (q = name; *q == *p++; q++)
642: if (*q == 0) /* exact match? */
643: return (c);
644: if (!*q) { /* the name was a prefix */
645: if (q - name > longest) {
646: longest = q - name;
647: nmatches = 1;
648: found = c;
649: } else if (q - name == longest)
650: nmatches++;
651: }
652: }
653: if (nmatches > 1)
654: return ((struct cmd *)-1);
655: return (found);
656: }
657:
658: /*
659: * Slice a string up into argc/argv.
660: */
661: static void
662: makeargv()
663: {
664: register char *cp;
665: register char **argp = margv;
666:
667: margc = 0;
668: for (cp = line; *cp;) {
669: while (isspace(*cp))
670: cp++;
671: if (*cp == '\0')
672: break;
673: *argp++ = cp;
674: margc += 1;
675: while (*cp != '\0' && !isspace(*cp))
676: cp++;
677: if (*cp == '\0')
678: break;
679: *cp++ = '\0';
680: }
681: *argp++ = 0;
682: }
683:
684: void
685: quit(argc, argv)
686: int argc;
687: char *argv[];
688: {
689:
690: exit(0);
691: }
692:
693: /*
694: * Help command.
695: */
696: void
697: help(argc, argv)
698: int argc;
699: char *argv[];
700: {
701: register struct cmd *c;
702:
703: if (argc == 1) {
704: printf("Commands may be abbreviated. Commands are:\n\n");
705: for (c = cmdtab; c->name; c++)
706: printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
707: return;
708: }
709: while (--argc > 0) {
710: register char *arg;
711: arg = *++argv;
712: c = getcmd(arg);
713: if (c == (struct cmd *)-1)
714: printf("?Ambiguous help command %s\n", arg);
715: else if (c == (struct cmd *)0)
716: printf("?Invalid help command %s\n", arg);
717: else
718: printf("%s\n", c->help);
719: }
720: }
721:
722: void
723: settrace(argc, argv)
724: int argc;
725: char **argv;
726: {
727: trace = !trace;
728: printf("Packet tracing %s.\n", trace ? "on" : "off");
729: }
730:
731: void
732: setverbose(argc, argv)
733: int argc;
734: char **argv;
735: {
736: verbose = !verbose;
737: printf("Verbose mode %s.\n", verbose ? "on" : "off");
738: }