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