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