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