main.c revision 1.12 1 /* $NetBSD: main.c,v 1.12 1999/07/12 20:50:54 itojun 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 1999/07/12 20:50:54 itojun 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 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
205 if (f < 0) {
206 cause = "socket";
207 continue;
208 }
209
210 memset(&ss, 0, sizeof(ss));
211 ss.ss_family = res->ai_family;
212 ss.ss_len = res->ai_addrlen;
213 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
214 cause = "bind";
215 close(f);
216 f = -1;
217 continue;
218 }
219
220 break;
221 }
222
223 if (f < 0)
224 warn("%s", cause);
225 else {
226 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
227 if (res->ai_canonname) {
228 (void) strncpy(hostname, res->ai_canonname,
229 sizeof(hostname));
230 } else
231 (void) strncpy(hostname, host, sizeof(hostname));
232 hostname[sizeof(hostname)-1] = 0;
233 connected = 1;
234 }
235
236 freeaddrinfo(res0);
237 }
238
239 void
240 setpeer(argc, argv)
241 int argc;
242 char *argv[];
243 {
244
245 if (argc < 2) {
246 strcpy(line, "Connect ");
247 printf("(to) ");
248 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
249 makeargv();
250 argc = margc;
251 argv = margv;
252 }
253 if ((argc < 2) || (argc > 3)) {
254 printf("usage: %s host-name [port]\n", argv[0]);
255 return;
256 }
257 if (argc == 3)
258 setpeer0(argv[1], NULL);
259 else
260 setpeer0(argv[1], argv[2]);
261 }
262
263 struct modes {
264 char *m_name;
265 char *m_mode;
266 } modes[] = {
267 { "ascii", "netascii" },
268 { "netascii", "netascii" },
269 { "binary", "octet" },
270 { "image", "octet" },
271 { "octet", "octet" },
272 /* { "mail", "mail" }, */
273 { 0, 0 }
274 };
275
276 void
277 modecmd(argc, argv)
278 int argc;
279 char *argv[];
280 {
281 struct modes *p;
282 char *sep;
283
284 if (argc < 2) {
285 printf("Using %s mode to transfer files.\n", mode);
286 return;
287 }
288 if (argc == 2) {
289 for (p = modes; p->m_name; p++)
290 if (strcmp(argv[1], p->m_name) == 0)
291 break;
292 if (p->m_name) {
293 settftpmode(p->m_mode);
294 return;
295 }
296 printf("%s: unknown mode\n", argv[1]);
297 /* drop through and print usage message */
298 }
299
300 printf("usage: %s [", argv[0]);
301 sep = " ";
302 for (p = modes; p->m_name; p++) {
303 printf("%s%s", sep, p->m_name);
304 if (*sep == ' ')
305 sep = " | ";
306 }
307 printf(" ]\n");
308 return;
309 }
310
311 void
312 setbinary(argc, argv)
313 int argc;
314 char *argv[];
315 {
316
317 settftpmode("octet");
318 }
319
320 void
321 setascii(argc, argv)
322 int argc;
323 char *argv[];
324 {
325
326 settftpmode("netascii");
327 }
328
329 static void
330 settftpmode(newmode)
331 char *newmode;
332 {
333 strcpy(mode, newmode);
334 if (verbose)
335 printf("mode set to %s\n", mode);
336 }
337
338
339 /*
340 * Send file(s).
341 */
342 void
343 put(argc, argv)
344 int argc;
345 char *argv[];
346 {
347 int fd;
348 int n;
349 char *cp, *targ;
350
351 if (argc < 2) {
352 strcpy(line, "send ");
353 printf("(file) ");
354 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
355 makeargv();
356 argc = margc;
357 argv = margv;
358 }
359 if (argc < 2) {
360 putusage(argv[0]);
361 return;
362 }
363 targ = argv[argc - 1];
364 if (strrchr(argv[argc - 1], ':')) {
365 char *cp;
366
367 for (n = 1; n < argc - 1; n++)
368 if (strchr(argv[n], ':')) {
369 putusage(argv[0]);
370 return;
371 }
372 cp = argv[argc - 1];
373 targ = strrchr(cp, ':');
374 *targ++ = 0;
375 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
376 cp[strlen(cp) - 1] = '\0';
377 cp++;
378 }
379 setpeer0(cp, NULL);
380 }
381 if (!connected) {
382 printf("No target machine specified.\n");
383 return;
384 }
385 if (argc < 4) {
386 cp = argc == 2 ? tail(targ) : argv[1];
387 fd = open(cp, O_RDONLY);
388 if (fd < 0) {
389 warn("%s", cp);
390 return;
391 }
392 if (verbose)
393 printf("putting %s to %s:%s [%s]\n",
394 cp, hostname, targ, mode);
395 sendfile(fd, targ, mode);
396 return;
397 }
398 /* this assumes the target is a directory */
399 /* on a remote unix system. hmmmm. */
400 cp = strchr(targ, '\0');
401 *cp++ = '/';
402 for (n = 1; n < argc - 1; n++) {
403 strcpy(cp, tail(argv[n]));
404 fd = open(argv[n], O_RDONLY);
405 if (fd < 0) {
406 warn("%s", argv[n]);
407 continue;
408 }
409 if (verbose)
410 printf("putting %s to %s:%s [%s]\n",
411 argv[n], hostname, targ, mode);
412 sendfile(fd, targ, mode);
413 }
414 }
415
416 static void
417 putusage(s)
418 char *s;
419 {
420 printf("usage: %s file ... host:target, or\n", s);
421 printf(" %s file ... target (when already connected)\n", s);
422 }
423
424 /*
425 * Receive file(s).
426 */
427 void
428 get(argc, argv)
429 int argc;
430 char *argv[];
431 {
432 int fd;
433 int n;
434 char *cp;
435 char *src;
436
437 if (argc < 2) {
438 strcpy(line, "get ");
439 printf("(files) ");
440 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
441 makeargv();
442 argc = margc;
443 argv = margv;
444 }
445 if (argc < 2) {
446 getusage(argv[0]);
447 return;
448 }
449 if (!connected) {
450 for (n = 1; n < argc ; n++)
451 if (strrchr(argv[n], ':') == 0) {
452 getusage(argv[0]);
453 return;
454 }
455 }
456 for (n = 1; n < argc ; n++) {
457 src = strrchr(argv[n], ':');
458 if (src == NULL)
459 src = argv[n];
460 else {
461 char *cp;
462 *src++ = 0;
463 cp = argv[n];
464 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
465 cp[strlen(cp) - 1] = '\0';
466 cp++;
467 }
468 setpeer0(cp, NULL);
469 if (!connected)
470 continue;
471 }
472 if (argc < 4) {
473 cp = argc == 3 ? argv[2] : tail(src);
474 fd = creat(cp, 0644);
475 if (fd < 0) {
476 warn("%s", cp);
477 return;
478 }
479 if (verbose)
480 printf("getting from %s:%s to %s [%s]\n",
481 hostname, src, cp, mode);
482 recvfile(fd, src, mode);
483 break;
484 }
485 cp = tail(src); /* new .. jdg */
486 fd = creat(cp, 0644);
487 if (fd < 0) {
488 warn("%s", cp);
489 continue;
490 }
491 if (verbose)
492 printf("getting from %s:%s to %s [%s]\n",
493 hostname, src, cp, mode);
494 recvfile(fd, src, mode);
495 }
496 }
497
498 static void
499 getusage(s)
500 char *s;
501 {
502 printf("usage: %s host:file host:file ... file, or\n", s);
503 printf(" %s file file ... file if connected\n", s);
504 }
505
506 int rexmtval = TIMEOUT;
507
508 void
509 setrexmt(argc, argv)
510 int argc;
511 char *argv[];
512 {
513 int t;
514
515 if (argc < 2) {
516 strcpy(line, "Rexmt-timeout ");
517 printf("(value) ");
518 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
519 makeargv();
520 argc = margc;
521 argv = margv;
522 }
523 if (argc != 2) {
524 printf("usage: %s value\n", argv[0]);
525 return;
526 }
527 t = atoi(argv[1]);
528 if (t < 0)
529 printf("%s: bad value\n", argv[1]);
530 else
531 rexmtval = t;
532 }
533
534 int maxtimeout = 5 * TIMEOUT;
535
536 void
537 settimeout(argc, argv)
538 int argc;
539 char *argv[];
540 {
541 int t;
542
543 if (argc < 2) {
544 strcpy(line, "Maximum-timeout ");
545 printf("(value) ");
546 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin);
547 makeargv();
548 argc = margc;
549 argv = margv;
550 }
551 if (argc != 2) {
552 printf("usage: %s value\n", argv[0]);
553 return;
554 }
555 t = atoi(argv[1]);
556 if (t < 0)
557 printf("%s: bad value\n", argv[1]);
558 else
559 maxtimeout = t;
560 }
561
562 void
563 status(argc, argv)
564 int argc;
565 char *argv[];
566 {
567 if (connected)
568 printf("Connected to %s.\n", hostname);
569 else
570 printf("Not connected.\n");
571 printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
572 verbose ? "on" : "off", trace ? "on" : "off");
573 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
574 rexmtval, maxtimeout);
575 }
576
577 void
578 intr(dummy)
579 int dummy;
580 {
581
582 signal(SIGALRM, SIG_IGN);
583 alarm(0);
584 longjmp(toplevel, -1);
585 }
586
587 char *
588 tail(filename)
589 char *filename;
590 {
591 char *s;
592
593 while (*filename) {
594 s = strrchr(filename, '/');
595 if (s == NULL)
596 break;
597 if (s[1])
598 return (s + 1);
599 *s = '\0';
600 }
601 return (filename);
602 }
603
604 /*
605 * Command parser.
606 */
607 static __dead void
608 command()
609 {
610 struct cmd *c;
611
612 for (;;) {
613 printf("%s> ", prompt);
614 if (fgets(line, LBUFLEN, stdin) == 0) {
615 if (feof(stdin)) {
616 exit(0);
617 } else {
618 continue;
619 }
620 }
621 if ((line[0] == 0) || (line[0] == '\n'))
622 continue;
623 makeargv();
624 if (margc == 0)
625 continue;
626 c = getcmd(margv[0]);
627 if (c == (struct cmd *)-1) {
628 printf("?Ambiguous command\n");
629 continue;
630 }
631 if (c == 0) {
632 printf("?Invalid command\n");
633 continue;
634 }
635 (*c->handler)(margc, margv);
636 }
637 }
638
639 struct cmd *
640 getcmd(name)
641 char *name;
642 {
643 char *p, *q;
644 struct cmd *c, *found;
645 int nmatches, longest;
646
647 longest = 0;
648 nmatches = 0;
649 found = 0;
650 for (c = cmdtab; (p = c->name) != NULL; c++) {
651 for (q = name; *q == *p++; q++)
652 if (*q == 0) /* exact match? */
653 return (c);
654 if (!*q) { /* the name was a prefix */
655 if (q - name > longest) {
656 longest = q - name;
657 nmatches = 1;
658 found = c;
659 } else if (q - name == longest)
660 nmatches++;
661 }
662 }
663 if (nmatches > 1)
664 return ((struct cmd *)-1);
665 return (found);
666 }
667
668 /*
669 * Slice a string up into argc/argv.
670 */
671 static void
672 makeargv()
673 {
674 char *cp;
675 char **argp = margv;
676
677 margc = 0;
678 for (cp = line; *cp;) {
679 while (isspace((unsigned char)*cp))
680 cp++;
681 if (*cp == '\0')
682 break;
683 *argp++ = cp;
684 margc += 1;
685 while (*cp != '\0' && !isspace((unsigned char)*cp))
686 cp++;
687 if (*cp == '\0')
688 break;
689 *cp++ = '\0';
690 }
691 *argp++ = 0;
692 }
693
694 void
695 quit(argc, argv)
696 int argc;
697 char *argv[];
698 {
699
700 exit(0);
701 }
702
703 /*
704 * Help command.
705 */
706 void
707 help(argc, argv)
708 int argc;
709 char *argv[];
710 {
711 struct cmd *c;
712
713 if (argc == 1) {
714 printf("Commands may be abbreviated. Commands are:\n\n");
715 for (c = cmdtab; c->name; c++)
716 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
717 return;
718 }
719 while (--argc > 0) {
720 char *arg;
721 arg = *++argv;
722 c = getcmd(arg);
723 if (c == (struct cmd *)-1)
724 printf("?Ambiguous help command %s\n", arg);
725 else if (c == (struct cmd *)0)
726 printf("?Invalid help command %s\n", arg);
727 else
728 printf("%s\n", c->help);
729 }
730 }
731
732 void
733 settrace(argc, argv)
734 int argc;
735 char **argv;
736 {
737 trace = !trace;
738 printf("Packet tracing %s.\n", trace ? "on" : "off");
739 }
740
741 void
742 setverbose(argc, argv)
743 int argc;
744 char **argv;
745 {
746 verbose = !verbose;
747 printf("Verbose mode %s.\n", verbose ? "on" : "off");
748 }
749