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