Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / usr.bin / tftp / main.c
blob22915a555b905c7b157c84fa93bcaf936f141e99
1 /* $NetBSD: main.c,v 1.25 2006/10/22 16:44:46 christos Exp $ */
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
35 The Regents of the University of California. All rights reserved.");
36 #if 0
37 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
38 #else
39 __RCSID("$NetBSD: main.c,v 1.25 2006/10/22 16:44:46 christos Exp $");
40 #endif
41 #endif /* not lint */
43 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
46 * TFTP User Program -- Command Interface.
48 #include <sys/types.h>
49 #include <sys/socket.h>
51 #include <netinet/in.h>
53 #include <arpa/inet.h>
54 #include <arpa/tftp.h>
56 #include <ctype.h>
57 #include <fcntl.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <netdb.h>
61 #include <setjmp.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
68 #include "extern.h"
70 #define TIMEOUT 5 /* secs between rexmt's */
71 #define LBUFLEN 200 /* size of input buffer */
73 struct sockaddr_storage peeraddr;
74 int f;
75 int mf;
76 int trace;
77 int verbose;
78 int tsize=0;
79 int tout=0;
80 size_t def_blksize=SEGSIZE;
81 size_t blksize=SEGSIZE;
82 in_addr_t mcaddr = INADDR_NONE;
83 uint16_t mcport;
84 ushort mcmasterslave;
85 int connected;
86 char mode[32];
87 char line[LBUFLEN];
88 int margc;
89 char *margv[20];
90 const char *prompt = "tftp";
91 jmp_buf toplevel;
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 setpeer0 __P((const char *, const char *));
101 void setpeer __P((int, char **));
102 void setrexmt __P((int, char **));
103 void settimeout __P((int, char **));
104 void settrace __P((int, char **));
105 void setverbose __P((int, char **));
106 void setblksize __P((int, char **));
107 void settsize __P((int, char **));
108 void settimeoutopt __P((int, char **));
109 void status __P((int, char **));
110 char *tail __P((char *));
111 int main __P((int, char *[]));
112 void intr __P((int));
113 const struct cmd *getcmd __P((char *));
115 static __dead void command __P((void));
117 static void getusage __P((char *));
118 static void makeargv __P((void));
119 static void putusage __P((char *));
120 static void settftpmode __P((const char *));
122 #define HELPINDENT (sizeof("connect"))
124 struct cmd {
125 const char *name;
126 const char *help;
127 void (*handler) __P((int, char **));
130 const char vhelp[] = "toggle verbose mode";
131 const char thelp[] = "toggle packet tracing";
132 const char tshelp[] = "toggle extended tsize option";
133 const char tohelp[] = "toggle extended timeout option";
134 const char blhelp[] = "set an alternative blocksize (def. 512)";
135 const char chelp[] = "connect to remote tftp";
136 const char qhelp[] = "exit tftp";
137 const char hhelp[] = "print help information";
138 const char shelp[] = "send file";
139 const char rhelp[] = "receive file";
140 const char mhelp[] = "set file transfer mode";
141 const char sthelp[] = "show current status";
142 const char xhelp[] = "set per-packet retransmission timeout";
143 const char ihelp[] = "set total retransmission timeout";
144 const char ashelp[] = "set mode to netascii";
145 const char bnhelp[] = "set mode to octet";
147 const struct cmd cmdtab[] = {
148 { "connect", chelp, setpeer },
149 { "mode", mhelp, modecmd },
150 { "put", shelp, put },
151 { "get", rhelp, get },
152 { "quit", qhelp, quit },
153 { "verbose", vhelp, setverbose },
154 { "blksize", blhelp, setblksize },
155 { "tsize", tshelp, settsize },
156 { "trace", thelp, settrace },
157 { "status", sthelp, status },
158 { "binary", bnhelp, setbinary },
159 { "ascii", ashelp, setascii },
160 { "rexmt", xhelp, setrexmt },
161 { "timeout", ihelp, settimeout },
162 { "tout", tohelp, settimeoutopt },
163 { "?", hhelp, help },
164 { .name = NULL }
168 main(argc, argv)
169 int argc;
170 char *argv[];
172 int c;
174 f = mf = -1;
175 (void)strlcpy(mode, "netascii", sizeof(mode));
176 (void)signal(SIGINT, intr);
178 setprogname(argv[0]);
179 while ((c = getopt(argc, argv, "e")) != -1) {
180 switch (c) {
181 case 'e':
182 blksize = MAXSEGSIZE;
183 (void)strlcpy(mode, "octet", sizeof(mode));
184 tsize = 1;
185 tout = 1;
186 break;
187 default:
188 (void)printf("usage: %s [-e] host-name [port]\n",
189 getprogname());
190 exit(1);
193 argc -= optind;
194 argv += optind;
196 if (argc >= 1) {
197 if (setjmp(toplevel) != 0)
198 exit(0);
199 argc++;
200 argv--;
201 setpeer(argc, argv);
203 if (setjmp(toplevel) != 0)
204 (void)putchar('\n');
205 command();
206 return (0);
209 char hostname[100];
211 void
212 setpeer0(host, port)
213 const char *host;
214 const char *port;
216 struct addrinfo hints, *res0, *res;
217 int error, soopt;
218 struct sockaddr_storage ss;
219 const char *cause = "unknown";
221 if (connected) {
222 (void)close(f);
223 f = -1;
225 connected = 0;
227 (void)memset(&hints, 0, sizeof(hints));
228 hints.ai_family = PF_UNSPEC;
229 hints.ai_socktype = SOCK_DGRAM;
230 hints.ai_protocol = IPPROTO_UDP;
231 hints.ai_flags = AI_CANONNAME;
232 if (!port)
233 port = "tftp";
234 error = getaddrinfo(host, port, &hints, &res0);
235 if (error) {
236 warnx("%s", gai_strerror(error));
237 return;
240 for (res = res0; res; res = res->ai_next) {
241 if (res->ai_addrlen > sizeof(peeraddr))
242 continue;
243 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
244 if (f < 0) {
245 cause = "socket";
246 continue;
249 (void)memset(&ss, 0, sizeof(ss));
250 ss.ss_family = res->ai_family;
251 ss.ss_len = res->ai_addrlen;
252 if (bind(f, (struct sockaddr *)(void *)&ss,
253 (socklen_t)ss.ss_len) < 0) {
254 cause = "bind";
255 (void)close(f);
256 f = -1;
257 continue;
260 break;
263 if (f >= 0) {
264 soopt = 65536;
265 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt))
266 < 0) {
267 (void)close(f);
268 f = -1;
269 cause = "setsockopt SNDBUF";
271 else if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt))
272 < 0) {
273 (void)close(f);
274 f = -1;
275 cause = "setsockopt RCVBUF";
279 if (f < 0 || res == NULL)
280 warn("%s", cause);
281 else {
282 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
283 (void)memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
284 if (res->ai_canonname) {
285 (void)strlcpy(hostname, res->ai_canonname,
286 sizeof(hostname));
287 } else
288 (void)strlcpy(hostname, host, sizeof(hostname));
289 connected = 1;
292 freeaddrinfo(res0);
295 void
296 setpeer(argc, argv)
297 int argc;
298 char *argv[];
301 if (argc < 2) {
302 (void)strlcpy(line, "Connect ", sizeof(line));
303 (void)printf("(to) ");
304 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
305 makeargv();
306 argc = margc;
307 argv = margv;
309 if ((argc < 2) || (argc > 3)) {
310 (void)printf("usage: %s [-e] host-name [port]\n", getprogname());
311 return;
313 if (argc == 2)
314 setpeer0(argv[1], NULL);
315 else
316 setpeer0(argv[1], argv[2]);
319 struct modes {
320 const char *m_name;
321 const char *m_mode;
322 } modes[] = {
323 { "ascii", "netascii" },
324 { "netascii", "netascii" },
325 { "binary", "octet" },
326 { "image", "octet" },
327 { "octet", "octet" },
328 /* { "mail", "mail" }, */
329 { 0, 0 }
332 void
333 modecmd(argc, argv)
334 int argc;
335 char *argv[];
337 struct modes *p;
338 const char *sep;
340 if (argc < 2) {
341 (void)printf("Using %s mode to transfer files.\n", mode);
342 return;
344 if (argc == 2) {
345 for (p = modes; p->m_name; p++)
346 if (strcmp(argv[1], p->m_name) == 0)
347 break;
348 if (p->m_name) {
349 settftpmode(p->m_mode);
350 return;
352 (void)printf("%s: unknown mode\n", argv[1]);
353 /* drop through and print usage message */
356 (void)printf("usage: %s [", argv[0]);
357 sep = " ";
358 for (p = modes; p->m_name; p++) {
359 (void)printf("%s%s", sep, p->m_name);
360 if (*sep == ' ')
361 sep = " | ";
363 (void)printf(" ]\n");
364 return;
367 void
368 /*ARGSUSED*/
369 setbinary(argc, argv)
370 int argc;
371 char *argv[];
374 settftpmode("octet");
377 void
378 /*ARGSUSED*/
379 setascii(argc, argv)
380 int argc;
381 char *argv[];
384 settftpmode("netascii");
387 static void
388 settftpmode(newmode)
389 const char *newmode;
391 (void)strlcpy(mode, newmode, sizeof(mode));
392 if (verbose)
393 (void)printf("mode set to %s\n", mode);
398 * Send file(s).
400 void
401 put(argc, argv)
402 int argc;
403 char *argv[];
405 int fd;
406 int n;
407 char *targ, *p;
409 if (argc < 2) {
410 (void)strlcpy(line, "send ", sizeof(line));
411 (void)printf("(file) ");
412 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
413 makeargv();
414 argc = margc;
415 argv = margv;
417 if (argc < 2) {
418 putusage(argv[0]);
419 return;
421 targ = argv[argc - 1];
422 if (strrchr(argv[argc - 1], ':')) {
423 char *cp;
425 for (n = 1; n < argc - 1; n++)
426 if (strchr(argv[n], ':')) {
427 putusage(argv[0]);
428 return;
430 cp = argv[argc - 1];
431 targ = strrchr(cp, ':');
432 *targ++ = 0;
433 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
434 cp[strlen(cp) - 1] = '\0';
435 cp++;
437 setpeer0(cp, NULL);
439 if (!connected) {
440 (void)printf("No target machine specified.\n");
441 return;
443 if (argc < 4) {
444 char *cp = argc == 2 ? tail(targ) : argv[1];
445 fd = open(cp, O_RDONLY);
446 if (fd < 0) {
447 warn("%s", cp);
448 return;
450 if (verbose)
451 (void)printf("putting %s to %s:%s [%s]\n",
452 cp, hostname, targ, mode);
453 sendfile(fd, targ, mode);
454 return;
456 /* this assumes the target is a directory */
457 /* on a remote unix system. hmmmm. */
458 p = strchr(targ, '\0');
459 *p++ = '/';
460 for (n = 1; n < argc - 1; n++) {
461 (void)strcpy(p, tail(argv[n]));
462 fd = open(argv[n], O_RDONLY);
463 if (fd < 0) {
464 warn("%s", argv[n]);
465 continue;
467 if (verbose)
468 (void)printf("putting %s to %s:%s [%s]\n",
469 argv[n], hostname, targ, mode);
470 sendfile(fd, targ, mode);
474 static void
475 putusage(s)
476 char *s;
478 (void)printf("usage: %s file ... host:target, or\n", s);
479 (void)printf(" %s file ... target (when already connected)\n", s);
483 * Receive file(s).
485 void
486 get(argc, argv)
487 int argc;
488 char *argv[];
490 int fd;
491 int n;
492 char *p;
493 char *src;
495 if (argc < 2) {
496 (void)strlcpy(line, "get ", sizeof(line));
497 (void)printf("(files) ");
498 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
499 makeargv();
500 argc = margc;
501 argv = margv;
503 if (argc < 2) {
504 getusage(argv[0]);
505 return;
507 if (!connected) {
508 for (n = 1; n < argc ; n++)
509 if (strrchr(argv[n], ':') == 0) {
510 getusage(argv[0]);
511 return;
514 for (n = 1; n < argc ; n++) {
515 src = strrchr(argv[n], ':');
516 if (src == NULL)
517 src = argv[n];
518 else {
519 char *cp;
520 *src++ = 0;
521 cp = argv[n];
522 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
523 cp[strlen(cp) - 1] = '\0';
524 cp++;
526 setpeer0(cp, NULL);
527 if (!connected)
528 continue;
530 if (argc < 4) {
531 char *cp = argc == 3 ? argv[2] : tail(src);
532 fd = creat(cp, 0644);
533 if (fd < 0) {
534 warn("%s", cp);
535 return;
537 if (verbose)
538 (void)printf("getting from %s:%s to %s [%s]\n",
539 hostname, src, cp, mode);
540 recvfile(fd, src, mode);
541 break;
543 p = tail(src); /* new .. jdg */
544 fd = creat(p, 0644);
545 if (fd < 0) {
546 warn("%s", p);
547 continue;
549 if (verbose)
550 (void)printf("getting from %s:%s to %s [%s]\n",
551 hostname, src, p, mode);
552 recvfile(fd, src, mode);
556 static void
557 getusage(s)
558 char *s;
560 (void)printf("usage: %s host:file host:file ... file, or\n", s);
561 (void)printf(" %s file file ... file if connected\n", s);
564 void
565 setblksize(argc, argv)
566 int argc;
567 char *argv[];
569 int t;
571 if (argc < 2) {
572 (void)strlcpy(line, "blksize ", sizeof(line));
573 (void)printf("(blksize) ");
574 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
575 makeargv();
576 argc = margc;
577 argv = margv;
579 if (argc != 2) {
580 (void)printf("usage: %s value\n", argv[0]);
581 return;
583 t = atoi(argv[1]);
584 if (t < 8 || t > 65464)
585 (void)printf("%s: bad value\n", argv[1]);
586 else
587 blksize = t;
590 unsigned int def_rexmtval = TIMEOUT;
591 unsigned int rexmtval = TIMEOUT;
593 void
594 setrexmt(argc, argv)
595 int argc;
596 char *argv[];
598 int t;
600 if (argc < 2) {
601 (void)strlcpy(line, "Rexmt-timeout ", sizeof(line));
602 (void)printf("(value) ");
603 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
604 makeargv();
605 argc = margc;
606 argv = margv;
608 if (argc != 2) {
609 (void)printf("usage: %s value\n", argv[0]);
610 return;
612 t = atoi(argv[1]);
613 if (t < 0)
614 (void)printf("%s: bad value\n", argv[1]);
615 else
616 rexmtval = t;
619 int maxtimeout = 5 * TIMEOUT;
621 void
622 settimeout(argc, argv)
623 int argc;
624 char *argv[];
626 int t;
628 if (argc < 2) {
629 (void)strlcpy(line, "Maximum-timeout ", sizeof(line));
630 (void)printf("(value) ");
631 (void)fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin);
632 makeargv();
633 argc = margc;
634 argv = margv;
636 if (argc != 2) {
637 (void)printf("usage: %s value\n", argv[0]);
638 return;
640 t = atoi(argv[1]);
641 if (t < 0)
642 (void)printf("%s: bad value\n", argv[1]);
643 else
644 maxtimeout = t;
647 void
648 /*ARGSUSED*/
649 status(argc, argv)
650 int argc;
651 char *argv[];
653 if (connected)
654 (void)printf("Connected to %s.\n", hostname);
655 else
656 (void)printf("Not connected.\n");
657 (void)printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
658 verbose ? "on" : "off", trace ? "on" : "off");
659 (void)printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
660 rexmtval, maxtimeout);
663 void
664 /*ARGSUSED*/
665 intr(dummy)
666 int dummy;
669 (void)signal(SIGALRM, SIG_IGN);
670 (void)alarm(0);
671 longjmp(toplevel, -1);
674 char *
675 tail(filename)
676 char *filename;
678 char *s;
680 while (*filename) {
681 s = strrchr(filename, '/');
682 if (s == NULL)
683 break;
684 if (s[1])
685 return (s + 1);
686 *s = '\0';
688 return (filename);
692 * Command parser.
694 static __dead void
695 command()
697 const struct cmd *c;
699 for (;;) {
700 (void)printf("%s> ", prompt);
701 if (fgets(line, LBUFLEN, stdin) == 0) {
702 if (feof(stdin)) {
703 exit(0);
704 } else {
705 continue;
708 if ((line[0] == 0) || (line[0] == '\n'))
709 continue;
710 makeargv();
711 if (margc == 0)
712 continue;
713 c = getcmd(margv[0]);
714 if (c == (struct cmd *)-1) {
715 (void)printf("?Ambiguous command\n");
716 continue;
718 if (c == 0) {
719 (void)printf("?Invalid command\n");
720 continue;
722 (*c->handler)(margc, margv);
726 const struct cmd *
727 getcmd(name)
728 char *name;
730 const char *p, *q;
731 const struct cmd *c, *found;
732 int nmatches, longest;
734 longest = 0;
735 nmatches = 0;
736 found = 0;
737 for (c = cmdtab; (p = c->name) != NULL; c++) {
738 for (q = name; *q == *p++; q++)
739 if (*q == 0) /* exact match? */
740 return (c);
741 if (!*q) { /* the name was a prefix */
742 if (q - name > longest) {
743 longest = q - name;
744 nmatches = 1;
745 found = c;
746 } else if (q - name == longest)
747 nmatches++;
750 if (nmatches > 1)
751 return ((struct cmd *)-1);
752 return (found);
756 * Slice a string up into argc/argv.
758 static void
759 makeargv()
761 char *cp;
762 char **argp = margv;
764 margc = 0;
765 for (cp = line; *cp;) {
766 while (isspace((unsigned char)*cp))
767 cp++;
768 if (*cp == '\0')
769 break;
770 *argp++ = cp;
771 margc += 1;
772 while (*cp != '\0' && !isspace((unsigned char)*cp))
773 cp++;
774 if (*cp == '\0')
775 break;
776 *cp++ = '\0';
778 *argp++ = 0;
781 void
782 /*ARGSUSED*/
783 quit(argc, argv)
784 int argc;
785 char *argv[];
788 exit(0);
792 * Help command.
794 void
795 help(argc, argv)
796 int argc;
797 char *argv[];
799 const struct cmd *c;
801 if (argc == 1) {
802 (void)printf("Commands may be abbreviated. Commands are:\n\n");
803 for (c = cmdtab; c->name; c++)
804 (void)printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
805 return;
807 while (--argc > 0) {
808 char *arg;
809 arg = *++argv;
810 c = getcmd(arg);
811 if (c == (struct cmd *)-1)
812 (void)printf("?Ambiguous help command %s\n", arg);
813 else if (c == (struct cmd *)0)
814 (void)printf("?Invalid help command %s\n", arg);
815 else
816 (void)printf("%s\n", c->help);
820 void
821 /*ARGSUSED*/
822 settrace(argc, argv)
823 int argc;
824 char **argv;
826 trace = !trace;
827 (void)printf("Packet tracing %s.\n", trace ? "on" : "off");
830 void
831 /*ARGSUSED*/
832 setverbose(argc, argv)
833 int argc;
834 char **argv;
836 verbose = !verbose;
837 (void)printf("Verbose mode %s.\n", verbose ? "on" : "off");
840 void
841 /*ARGSUSED*/
842 settsize(argc, argv)
843 int argc;
844 char **argv;
846 tsize = !tsize;
847 (void)printf("Tsize mode %s.\n", tsize ? "on" : "off");
850 void
851 /*ARGSUSED*/
852 settimeoutopt(argc, argv)
853 int argc;
854 char **argv;
856 tout = !tout;
857 (void)printf("Timeout option %s.\n", tout ? "on" : "off");