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