main.c revision 1.17 1 /* $NetBSD: main.c,v 1.17 2003/08/07 11:16:13 agc Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
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.
18 *
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.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
35 The Regents of the University of California. All rights reserved.\n");
36 #if 0
37 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
38 #else
39 __RCSID("$NetBSD: main.c,v 1.17 2003/08/07 11:16:13 agc Exp $");
40 #endif
41 #endif /* not lint */
42
43 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
44
45 /*
46 * TFTP User Program -- Command Interface.
47 */
48 #include <sys/types.h>
49 #include <sys/socket.h>
50
51 #include <netinet/in.h>
52
53 #include <arpa/inet.h>
54 #include <arpa/tftp.h>
55
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>
67
68 #include "extern.h"
69
70 #define TIMEOUT 5 /* secs between rexmt's */
71 #define LBUFLEN 200 /* size of input buffer */
72
73 struct sockaddr_storage peeraddr;
74 int f;
75 int trace;
76 int verbose;
77 int tsize=0;
78 int tout=0;
79 int def_blksize=SEGSIZE;
80 int blksize=SEGSIZE;
81 int connected;
82 char mode[32];
83 char line[LBUFLEN];
84 int margc;
85 char *margv[20];
86 char *prompt = "tftp";
87 jmp_buf toplevel;
88
89 void get __P((int, char **));
90 void help __P((int, char **));
91 void modecmd __P((int, char **));
92 void put __P((int, char **));
93 void quit __P((int, char **));
94 void setascii __P((int, char **));
95 void setbinary __P((int, char **));
96 void setpeer0 __P((char *, char *));
97 void setpeer __P((int, char **));
98 void setrexmt __P((int, char **));
99 void settimeout __P((int, char **));
100 void settrace __P((int, char **));
101 void setverbose __P((int, char **));
102 void setblksize __P((int, char **));
103 void settsize __P((int, char **));
104 void settimeoutopt __P((int, char **));
105 void status __P((int, char **));
106 char *tail __P((char *));
107 int main __P((int, char *[]));
108 void intr __P((int));
109 struct cmd *getcmd __P((char *));
110
111 static __dead void command __P((void));
112
113 static void getusage __P((char *));
114 static void makeargv __P((void));
115 static void putusage __P((char *));
116 static void settftpmode __P((char *));
117
118 #define HELPINDENT (sizeof("connect"))
119
120 struct cmd {
121 char *name;
122 char *help;
123 void (*handler) __P((int, char **));
124 };
125
126 char vhelp[] = "toggle verbose mode";
127 char thelp[] = "toggle packet tracing";
128 char tshelp[] = "toggle extended tsize option";
129 char tohelp[] = "toggle extended timeout option";
130 char blhelp[] = "set an alternative blocksize (def. 512)";
131 char chelp[] = "connect to remote tftp";
132 char qhelp[] = "exit tftp";
133 char hhelp[] = "print help information";
134 char shelp[] = "send file";
135 char rhelp[] = "receive file";
136 char mhelp[] = "set file transfer mode";
137 char sthelp[] = "show current status";
138 char xhelp[] = "set per-packet retransmission timeout";
139 char ihelp[] = "set total retransmission timeout";
140 char ashelp[] = "set mode to netascii";
141 char bnhelp[] = "set mode to octet";
142
143 struct cmd cmdtab[] = {
144 { "connect", chelp, setpeer },
145 { "mode", mhelp, modecmd },
146 { "put", shelp, put },
147 { "get", rhelp, get },
148 { "quit", qhelp, quit },
149 { "verbose", vhelp, setverbose },
150 { "blksize", blhelp, setblksize },
151 { "tsize", tshelp, settsize },
152 { "trace", thelp, settrace },
153 { "status", sthelp, status },
154 { "binary", bnhelp, setbinary },
155 { "ascii", ashelp, setascii },
156 { "rexmt", xhelp, setrexmt },
157 { "timeout", ihelp, settimeout },
158 { "tout", tohelp, settimeoutopt },
159 { "?", hhelp, help },
160 { 0 }
161 };
162
163 int
164 main(argc, argv)
165 int argc;
166 char *argv[];
167 {
168 int c;
169
170 f = -1;
171 strcpy(mode, "netascii");
172 signal(SIGINT, intr);
173
174 setprogname(argv[0]);
175 while ((c = getopt(argc, argv, "e")) != -1) {
176 switch (c) {
177 case 'e':
178 blksize = MAXSEGSIZE;
179 strcpy(mode, "octet");
180 tsize = 1;
181 tout = 1;
182 break;
183 default:
184 printf("usage: %s [-e] host-name [port]\n",
185 getprogname());
186 exit(1);
187 }
188 }
189 argc -= optind;
190 argv += optind;
191
192 if (argc >= 1) {
193 if (setjmp(toplevel) != 0)
194 exit(0);
195 setpeer(argc, argv);
196 }
197 if (setjmp(toplevel) != 0)
198 (void)putchar('\n');
199 command();
200 return (0);
201 }
202
203 char hostname[100];
204
205 void
206 setpeer0(host, port)
207 char *host;
208 char *port;
209 {
210 struct addrinfo hints, *res0, *res;
211 int error, soopt;
212 struct sockaddr_storage ss;
213 char *cause = "unknown";
214
215 if (connected) {
216 close(f);
217 f = -1;
218 }
219 connected = 0;
220
221 memset(&hints, 0, sizeof(hints));
222 hints.ai_family = PF_UNSPEC;
223 hints.ai_socktype = SOCK_DGRAM;
224 hints.ai_protocol = IPPROTO_UDP;
225 hints.ai_flags = AI_CANONNAME;
226 if (!port)
227 port = "tftp";
228 error = getaddrinfo(host, port, &hints, &res0);
229 if (error) {
230 warnx("%s", gai_strerror(error));
231 return;
232 }
233
234 for (res = res0; res; res = res->ai_next) {
235 if (res->ai_addrlen > sizeof(peeraddr))
236 continue;
237 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
238 if (f < 0) {
239 cause = "socket";
240 continue;
241 }
242
243 memset(&ss, 0, sizeof(ss));
244 ss.ss_family = res->ai_family;
245 ss.ss_len = res->ai_addrlen;
246 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
247 cause = "bind";
248 close(f);
249 f = -1;
250 continue;
251 }
252
253 break;
254 }
255
256 if (f >= 0) {
257 soopt = 65536;
258 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt))
259 < 0) {
260 close(f);
261 f = -1;
262 cause = "setsockopt SNDBUF";
263 }
264 if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt))
265 < 0) {
266 close(f);
267 f = -1;
268 cause = "setsockopt RCVBUF";
269 }
270 }
271
272 if (f < 0)
273 warn("%s", cause);
274 else {
275 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
276 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
277 if (res->ai_canonname) {
278 (void) strlcpy(hostname, res->ai_canonname,
279 sizeof(hostname));
280 } else
281 (void) strlcpy(hostname, host, sizeof(hostname));
282 connected = 1;
283 }
284
285 freeaddrinfo(res0);
286 }
287
288 void
289 setpeer(argc, argv)
290 int argc;
291 char *argv[];
292 {
293
294 if (argc < 1) {
295 strcpy(line, "Connect ");
296 printf("(to) ");
297 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
298 makeargv();
299 argc = margc;
300 argv = margv;
301 }
302 if ((argc < 1) || (argc > 2)) {
303 printf("usage: %s [-e] host-name [port]\n", getprogname());
304 return;
305 }
306 if (argc == 1)
307 setpeer0(argv[0], NULL);
308 else
309 setpeer0(argv[0], argv[1]);
310 }
311
312 struct modes {
313 char *m_name;
314 char *m_mode;
315 } modes[] = {
316 { "ascii", "netascii" },
317 { "netascii", "netascii" },
318 { "binary", "octet" },
319 { "image", "octet" },
320 { "octet", "octet" },
321 /* { "mail", "mail" }, */
322 { 0, 0 }
323 };
324
325 void
326 modecmd(argc, argv)
327 int argc;
328 char *argv[];
329 {
330 struct modes *p;
331 char *sep;
332
333 if (argc < 2) {
334 printf("Using %s mode to transfer files.\n", mode);
335 return;
336 }
337 if (argc == 2) {
338 for (p = modes; p->m_name; p++)
339 if (strcmp(argv[1], p->m_name) == 0)
340 break;
341 if (p->m_name) {
342 settftpmode(p->m_mode);
343 return;
344 }
345 printf("%s: unknown mode\n", argv[1]);
346 /* drop through and print usage message */
347 }
348
349 printf("usage: %s [", argv[0]);
350 sep = " ";
351 for (p = modes; p->m_name; p++) {
352 printf("%s%s", sep, p->m_name);
353 if (*sep == ' ')
354 sep = " | ";
355 }
356 printf(" ]\n");
357 return;
358 }
359
360 void
361 setbinary(argc, argv)
362 int argc;
363 char *argv[];
364 {
365
366 settftpmode("octet");
367 }
368
369 void
370 setascii(argc, argv)
371 int argc;
372 char *argv[];
373 {
374
375 settftpmode("netascii");
376 }
377
378 static void
379 settftpmode(newmode)
380 char *newmode;
381 {
382 strcpy(mode, newmode);
383 if (verbose)
384 printf("mode set to %s\n", mode);
385 }
386
387
388 /*
389 * Send file(s).
390 */
391 void
392 put(argc, argv)
393 int argc;
394 char *argv[];
395 {
396 int fd;
397 int n;
398 char *cp, *targ;
399
400 if (argc < 2) {
401 strcpy(line, "send ");
402 printf("(file) ");
403 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
404 makeargv();
405 argc = margc;
406 argv = margv;
407 }
408 if (argc < 2) {
409 putusage(argv[0]);
410 return;
411 }
412 targ = argv[argc - 1];
413 if (strrchr(argv[argc - 1], ':')) {
414 char *cp;
415
416 for (n = 1; n < argc - 1; n++)
417 if (strchr(argv[n], ':')) {
418 putusage(argv[0]);
419 return;
420 }
421 cp = argv[argc - 1];
422 targ = strrchr(cp, ':');
423 *targ++ = 0;
424 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
425 cp[strlen(cp) - 1] = '\0';
426 cp++;
427 }
428 setpeer0(cp, NULL);
429 }
430 if (!connected) {
431 printf("No target machine specified.\n");
432 return;
433 }
434 if (argc < 4) {
435 cp = argc == 2 ? tail(targ) : argv[1];
436 fd = open(cp, O_RDONLY);
437 if (fd < 0) {
438 warn("%s", cp);
439 return;
440 }
441 if (verbose)
442 printf("putting %s to %s:%s [%s]\n",
443 cp, hostname, targ, mode);
444 sendfile(fd, targ, mode);
445 return;
446 }
447 /* this assumes the target is a directory */
448 /* on a remote unix system. hmmmm. */
449 cp = strchr(targ, '\0');
450 *cp++ = '/';
451 for (n = 1; n < argc - 1; n++) {
452 strcpy(cp, tail(argv[n]));
453 fd = open(argv[n], O_RDONLY);
454 if (fd < 0) {
455 warn("%s", argv[n]);
456 continue;
457 }
458 if (verbose)
459 printf("putting %s to %s:%s [%s]\n",
460 argv[n], hostname, targ, mode);
461 sendfile(fd, targ, mode);
462 }
463 }
464
465 static void
466 putusage(s)
467 char *s;
468 {
469 printf("usage: %s file ... host:target, or\n", s);
470 printf(" %s file ... target (when already connected)\n", s);
471 }
472
473 /*
474 * Receive file(s).
475 */
476 void
477 get(argc, argv)
478 int argc;
479 char *argv[];
480 {
481 int fd;
482 int n;
483 char *cp;
484 char *src;
485
486 if (argc < 2) {
487 strcpy(line, "get ");
488 printf("(files) ");
489 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
490 makeargv();
491 argc = margc;
492 argv = margv;
493 }
494 if (argc < 2) {
495 getusage(argv[0]);
496 return;
497 }
498 if (!connected) {
499 for (n = 1; n < argc ; n++)
500 if (strrchr(argv[n], ':') == 0) {
501 getusage(argv[0]);
502 return;
503 }
504 }
505 for (n = 1; n < argc ; n++) {
506 src = strrchr(argv[n], ':');
507 if (src == NULL)
508 src = argv[n];
509 else {
510 char *cp;
511 *src++ = 0;
512 cp = argv[n];
513 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
514 cp[strlen(cp) - 1] = '\0';
515 cp++;
516 }
517 setpeer0(cp, NULL);
518 if (!connected)
519 continue;
520 }
521 if (argc < 4) {
522 cp = argc == 3 ? argv[2] : tail(src);
523 fd = creat(cp, 0644);
524 if (fd < 0) {
525 warn("%s", cp);
526 return;
527 }
528 if (verbose)
529 printf("getting from %s:%s to %s [%s]\n",
530 hostname, src, cp, mode);
531 recvfile(fd, src, mode);
532 break;
533 }
534 cp = tail(src); /* new .. jdg */
535 fd = creat(cp, 0644);
536 if (fd < 0) {
537 warn("%s", cp);
538 continue;
539 }
540 if (verbose)
541 printf("getting from %s:%s to %s [%s]\n",
542 hostname, src, cp, mode);
543 recvfile(fd, src, mode);
544 }
545 }
546
547 static void
548 getusage(s)
549 char *s;
550 {
551 printf("usage: %s host:file host:file ... file, or\n", s);
552 printf(" %s file file ... file if connected\n", s);
553 }
554
555 void
556 setblksize(argc, argv)
557 int argc;
558 char *argv[];
559 {
560 int t;
561
562 if (argc < 2) {
563 strcpy(line, "blksize ");
564 printf("(blksize) ");
565 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
566 makeargv();
567 argc = margc;
568 argv = margv;
569 }
570 if (argc != 2) {
571 printf("usage: %s value\n", argv[0]);
572 return;
573 }
574 t = atoi(argv[1]);
575 if (t < 8 || t > 65464)
576 printf("%s: bad value\n", argv[1]);
577 else
578 blksize = t;
579 }
580
581 int def_rexmtval = TIMEOUT;
582 int rexmtval = TIMEOUT;
583
584 void
585 setrexmt(argc, argv)
586 int argc;
587 char *argv[];
588 {
589 int t;
590
591 if (argc < 2) {
592 strcpy(line, "Rexmt-timeout ");
593 printf("(value) ");
594 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
595 makeargv();
596 argc = margc;
597 argv = margv;
598 }
599 if (argc != 2) {
600 printf("usage: %s value\n", argv[0]);
601 return;
602 }
603 t = atoi(argv[1]);
604 if (t < 0)
605 printf("%s: bad value\n", argv[1]);
606 else
607 rexmtval = t;
608 }
609
610 int maxtimeout = 5 * TIMEOUT;
611
612 void
613 settimeout(argc, argv)
614 int argc;
615 char *argv[];
616 {
617 int t;
618
619 if (argc < 2) {
620 strcpy(line, "Maximum-timeout ");
621 printf("(value) ");
622 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
623 makeargv();
624 argc = margc;
625 argv = margv;
626 }
627 if (argc != 2) {
628 printf("usage: %s value\n", argv[0]);
629 return;
630 }
631 t = atoi(argv[1]);
632 if (t < 0)
633 printf("%s: bad value\n", argv[1]);
634 else
635 maxtimeout = t;
636 }
637
638 void
639 status(argc, argv)
640 int argc;
641 char *argv[];
642 {
643 if (connected)
644 printf("Connected to %s.\n", hostname);
645 else
646 printf("Not connected.\n");
647 printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
648 verbose ? "on" : "off", trace ? "on" : "off");
649 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
650 rexmtval, maxtimeout);
651 }
652
653 void
654 intr(dummy)
655 int dummy;
656 {
657
658 signal(SIGALRM, SIG_IGN);
659 alarm(0);
660 longjmp(toplevel, -1);
661 }
662
663 char *
664 tail(filename)
665 char *filename;
666 {
667 char *s;
668
669 while (*filename) {
670 s = strrchr(filename, '/');
671 if (s == NULL)
672 break;
673 if (s[1])
674 return (s + 1);
675 *s = '\0';
676 }
677 return (filename);
678 }
679
680 /*
681 * Command parser.
682 */
683 static __dead void
684 command()
685 {
686 struct cmd *c;
687
688 for (;;) {
689 printf("%s> ", prompt);
690 if (fgets(line, LBUFLEN, stdin) == 0) {
691 if (feof(stdin)) {
692 exit(0);
693 } else {
694 continue;
695 }
696 }
697 if ((line[0] == 0) || (line[0] == '\n'))
698 continue;
699 makeargv();
700 if (margc == 0)
701 continue;
702 c = getcmd(margv[0]);
703 if (c == (struct cmd *)-1) {
704 printf("?Ambiguous command\n");
705 continue;
706 }
707 if (c == 0) {
708 printf("?Invalid command\n");
709 continue;
710 }
711 (*c->handler)(margc, margv);
712 }
713 }
714
715 struct cmd *
716 getcmd(name)
717 char *name;
718 {
719 char *p, *q;
720 struct cmd *c, *found;
721 int nmatches, longest;
722
723 longest = 0;
724 nmatches = 0;
725 found = 0;
726 for (c = cmdtab; (p = c->name) != NULL; c++) {
727 for (q = name; *q == *p++; q++)
728 if (*q == 0) /* exact match? */
729 return (c);
730 if (!*q) { /* the name was a prefix */
731 if (q - name > longest) {
732 longest = q - name;
733 nmatches = 1;
734 found = c;
735 } else if (q - name == longest)
736 nmatches++;
737 }
738 }
739 if (nmatches > 1)
740 return ((struct cmd *)-1);
741 return (found);
742 }
743
744 /*
745 * Slice a string up into argc/argv.
746 */
747 static void
748 makeargv()
749 {
750 char *cp;
751 char **argp = margv;
752
753 margc = 0;
754 for (cp = line; *cp;) {
755 while (isspace((unsigned char)*cp))
756 cp++;
757 if (*cp == '\0')
758 break;
759 *argp++ = cp;
760 margc += 1;
761 while (*cp != '\0' && !isspace((unsigned char)*cp))
762 cp++;
763 if (*cp == '\0')
764 break;
765 *cp++ = '\0';
766 }
767 *argp++ = 0;
768 }
769
770 void
771 quit(argc, argv)
772 int argc;
773 char *argv[];
774 {
775
776 exit(0);
777 }
778
779 /*
780 * Help command.
781 */
782 void
783 help(argc, argv)
784 int argc;
785 char *argv[];
786 {
787 struct cmd *c;
788
789 if (argc == 1) {
790 printf("Commands may be abbreviated. Commands are:\n\n");
791 for (c = cmdtab; c->name; c++)
792 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
793 return;
794 }
795 while (--argc > 0) {
796 char *arg;
797 arg = *++argv;
798 c = getcmd(arg);
799 if (c == (struct cmd *)-1)
800 printf("?Ambiguous help command %s\n", arg);
801 else if (c == (struct cmd *)0)
802 printf("?Invalid help command %s\n", arg);
803 else
804 printf("%s\n", c->help);
805 }
806 }
807
808 void
809 settrace(argc, argv)
810 int argc;
811 char **argv;
812 {
813 trace = !trace;
814 printf("Packet tracing %s.\n", trace ? "on" : "off");
815 }
816
817 void
818 setverbose(argc, argv)
819 int argc;
820 char **argv;
821 {
822 verbose = !verbose;
823 printf("Verbose mode %s.\n", verbose ? "on" : "off");
824 }
825
826 void
827 settsize(argc, argv)
828 int argc;
829 char **argv;
830 {
831 tsize = !tsize;
832 printf("Tsize mode %s.\n", tsize ? "on" : "off");
833 }
834
835 void
836 settimeoutopt(argc, argv)
837 int argc;
838 char **argv;
839 {
840 tout = !tout;
841 printf("Timeout option %s.\n", tout ? "on" : "off");
842 }
843