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