]> git.saurik.com Git - apple/network_cmds.git/blob - tftp.tproj/main.c
network_cmds-201.tar.gz
[apple/network_cmds.git] / tftp.tproj / main.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright (c) 1983, 1993
27 * The Regents of the University of California. All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58
59 #ifndef lint
60 static char copyright[] =
61 "@(#) Copyright (c) 1983, 1993\n\
62 The Regents of the University of California. All rights reserved.\n";
63 #endif /* not lint */
64
65 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
66
67 /*
68 * TFTP User Program -- Command Interface.
69 */
70 #include <sys/types.h>
71 #include <sys/socket.h>
72 #include <sys/file.h>
73
74 #include <netinet/in.h>
75
76 #include <arpa/inet.h>
77
78 #include <ctype.h>
79 #include <errno.h>
80 #include <netdb.h>
81 #include <setjmp.h>
82 #include <signal.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <unistd.h>
87
88 #include "extern.h"
89
90 #define TIMEOUT 5 /* secs between rexmt's */
91
92 struct sockaddr_in peeraddr;
93 int f;
94 short port;
95 int trace;
96 int verbose;
97 int connected;
98 char mode[32];
99 char line[BUFSIZ];
100 int margc;
101 char *margv[20];
102 char *prompt = "tftp";
103 jmp_buf toplevel;
104 void intr();
105 struct servent *sp;
106
107 void get __P((int, char **));
108 void help __P((int, char **));
109 void modecmd __P((int, char **));
110 void put __P((int, char **));
111 void quit __P((int, char **));
112 void setascii __P((int, char **));
113 void setbinary __P((int, char **));
114 void setpeer __P((int, char **));
115 void setrexmt __P((int, char **));
116 void settimeout __P((int, char **));
117 void settrace __P((int, char **));
118 void setverbose __P((int, char **));
119 void status __P((int, char **));
120
121 static __dead void command __P((void));
122
123 static void getusage __P((char *));
124 static void makeargv __P((void));
125 static void putusage __P((char *));
126 static void settftpmode __P((char *));
127
128 #define HELPINDENT (sizeof("connect"))
129
130 struct cmd {
131 char *name;
132 char *help;
133 void (*handler) __P((int, char **));
134 };
135
136 char vhelp[] = "toggle verbose mode";
137 char thelp[] = "toggle packet tracing";
138 char chelp[] = "connect to remote tftp";
139 char qhelp[] = "exit tftp";
140 char hhelp[] = "print help information";
141 char shelp[] = "send file";
142 char rhelp[] = "receive file";
143 char mhelp[] = "set file transfer mode";
144 char sthelp[] = "show current status";
145 char xhelp[] = "set per-packet retransmission timeout";
146 char ihelp[] = "set total retransmission timeout";
147 char ashelp[] = "set mode to netascii";
148 char bnhelp[] = "set mode to octet";
149
150 struct cmd cmdtab[] = {
151 { "connect", chelp, setpeer },
152 { "mode", mhelp, modecmd },
153 { "put", shelp, put },
154 { "get", rhelp, get },
155 { "quit", qhelp, quit },
156 { "verbose", vhelp, setverbose },
157 { "trace", thelp, settrace },
158 { "status", sthelp, status },
159 { "binary", bnhelp, setbinary },
160 { "ascii", ashelp, setascii },
161 { "rexmt", xhelp, setrexmt },
162 { "timeout", ihelp, settimeout },
163 { "?", hhelp, help },
164 { 0 }
165 };
166
167 struct cmd *getcmd();
168 char *tail();
169 char *index();
170 char *rindex();
171
172 int
173 main(argc, argv)
174 int argc;
175 char *argv[];
176 {
177 struct sockaddr_in sin;
178
179 sp = getservbyname("tftp", "udp");
180 if (sp == 0) {
181 fprintf(stderr, "tftp: udp/tftp: unknown service\n");
182 exit(1);
183 }
184 f = socket(AF_INET, SOCK_DGRAM, 0);
185 if (f < 0) {
186 perror("tftp: socket");
187 exit(3);
188 }
189 bzero((char *)&sin, sizeof(sin));
190 sin.sin_family = AF_INET;
191 if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
192 perror("tftp: bind");
193 exit(1);
194 }
195 strcpy(mode, "netascii");
196 signal(SIGINT, intr);
197 if (argc > 1) {
198 if (setjmp(toplevel) != 0)
199 exit(0);
200 setpeer(argc, argv);
201 }
202 if (setjmp(toplevel) != 0)
203 (void)putchar('\n');
204 command();
205 }
206
207 char hostname[100];
208
209 void
210 setpeer(argc, argv)
211 int argc;
212 char *argv[];
213 {
214 struct hostent *host;
215
216 if (argc < 2) {
217 strcpy(line, "Connect ");
218 printf("(to) ");
219 fgets(&line[strlen(line)], BUFSIZ-strlen(line)-1, stdin);
220 makeargv();
221 argc = margc;
222 argv = margv;
223 }
224 if (argc > 3) {
225 printf("usage: %s host-name [port]\n", argv[0]);
226 return;
227 }
228 host = gethostbyname(argv[1]);
229 if (host) {
230 peeraddr.sin_family = host->h_addrtype;
231 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
232 strcpy(hostname, host->h_name);
233 } else {
234 peeraddr.sin_family = AF_INET;
235 peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
236 if (peeraddr.sin_addr.s_addr == -1) {
237 connected = 0;
238 printf("%s: unknown host\n", argv[1]);
239 return;
240 }
241 strcpy(hostname, argv[1]);
242 }
243 port = sp->s_port;
244 if (argc == 3) {
245 port = atoi(argv[2]);
246 if (port < 0) {
247 printf("%s: bad port number\n", argv[2]);
248 connected = 0;
249 return;
250 }
251 port = htons(port);
252 }
253 connected = 1;
254 }
255
256 struct modes {
257 char *m_name;
258 char *m_mode;
259 } modes[] = {
260 { "ascii", "netascii" },
261 { "netascii", "netascii" },
262 { "binary", "octet" },
263 { "image", "octet" },
264 { "octet", "octet" },
265 /* { "mail", "mail" }, */
266 { 0, 0 }
267 };
268
269 void
270 modecmd(argc, argv)
271 int argc;
272 char *argv[];
273 {
274 register struct modes *p;
275 char *sep;
276
277 if (argc < 2) {
278 printf("Using %s mode to transfer files.\n", mode);
279 return;
280 }
281 if (argc == 2) {
282 for (p = modes; p->m_name; p++)
283 if (strcmp(argv[1], p->m_name) == 0)
284 break;
285 if (p->m_name) {
286 settftpmode(p->m_mode);
287 return;
288 }
289 printf("%s: unknown mode\n", argv[1]);
290 /* drop through and print usage message */
291 }
292
293 printf("usage: %s [", argv[0]);
294 sep = " ";
295 for (p = modes; p->m_name; p++) {
296 printf("%s%s", sep, p->m_name);
297 if (*sep == ' ')
298 sep = " | ";
299 }
300 printf(" ]\n");
301 return;
302 }
303
304 void
305 setbinary(argc, argv)
306 int argc;
307 char *argv[];
308 {
309
310 settftpmode("octet");
311 }
312
313 void
314 setascii(argc, argv)
315 int argc;
316 char *argv[];
317 {
318
319 settftpmode("netascii");
320 }
321
322 static void
323 settftpmode(newmode)
324 char *newmode;
325 {
326 strcpy(mode, newmode);
327 if (verbose)
328 printf("mode set to %s\n", mode);
329 }
330
331
332 /*
333 * Send file(s).
334 */
335 void
336 put(argc, argv)
337 int argc;
338 char *argv[];
339 {
340 int fd;
341 register int n;
342 register char *cp, *targ;
343
344 if (argc < 2) {
345 strcpy(line, "send ");
346 printf("(file) ");
347 fgets(&line[strlen(line)], BUFSIZ-strlen(line)-1, stdin);
348 makeargv();
349 argc = margc;
350 argv = margv;
351 }
352 if (argc < 2) {
353 putusage(argv[0]);
354 return;
355 }
356 targ = argv[argc - 1];
357 if (index(argv[argc - 1], ':')) {
358 char *cp;
359 struct hostent *hp;
360
361 for (n = 1; n < argc - 1; n++)
362 if (index(argv[n], ':')) {
363 putusage(argv[0]);
364 return;
365 }
366 cp = argv[argc - 1];
367 targ = index(cp, ':');
368 *targ++ = 0;
369 hp = gethostbyname(cp);
370 if (hp == NULL) {
371 fprintf(stderr, "tftp: %s: ", cp);
372 herror((char *)NULL);
373 return;
374 }
375 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
376 peeraddr.sin_family = hp->h_addrtype;
377 connected = 1;
378 strcpy(hostname, hp->h_name);
379 }
380 if (!connected) {
381 printf("No target machine specified.\n");
382 return;
383 }
384 if (argc < 4) {
385 cp = argc == 2 ? tail(targ) : argv[1];
386 fd = open(cp, O_RDONLY);
387 if (fd < 0) {
388 fprintf(stderr, "tftp: "); perror(cp);
389 return;
390 }
391 if (verbose)
392 printf("putting %s to %s:%s [%s]\n",
393 cp, hostname, targ, mode);
394 peeraddr.sin_port = port;
395 tftp_sendfile(fd, targ, mode);
396 return;
397 }
398 /* this assumes the target is a directory */
399 /* on a remote unix system. hmmmm. */
400 cp = index(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 fprintf(stderr, "tftp: "); perror(argv[n]);
407 continue;
408 }
409 if (verbose)
410 printf("putting %s to %s:%s [%s]\n",
411 argv[n], hostname, targ, mode);
412 peeraddr.sin_port = port;
413 tftp_sendfile(fd, targ, mode);
414 }
415 }
416
417 static void
418 putusage(s)
419 char *s;
420 {
421 printf("usage: %s file ... host:target, or\n", s);
422 printf(" %s file ... target (when already connected)\n", s);
423 }
424
425 /*
426 * Receive file(s).
427 */
428 void
429 get(argc, argv)
430 int argc;
431 char *argv[];
432 {
433 int fd;
434 register int n;
435 register char *cp;
436 char *src;
437
438 if (argc < 2) {
439 strcpy(line, "get ");
440 printf("(files) ");
441 fgets(&line[strlen(line)], BUFSIZ-strlen(line)-1, stdin);
442 makeargv();
443 argc = margc;
444 argv = margv;
445 }
446 if (argc < 2) {
447 getusage(argv[0]);
448 return;
449 }
450 if (!connected) {
451 for (n = 1; n < argc ; n++)
452 if (index(argv[n], ':') == 0) {
453 getusage(argv[0]);
454 return;
455 }
456 }
457 for (n = 1; n < argc ; n++) {
458 src = index(argv[n], ':');
459 if (src == NULL)
460 src = argv[n];
461 else {
462 struct hostent *hp;
463
464 *src++ = 0;
465 hp = gethostbyname(argv[n]);
466 if (hp == NULL) {
467 fprintf(stderr, "tftp: %s: ", argv[n]);
468 herror((char *)NULL);
469 continue;
470 }
471 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
472 hp->h_length);
473 peeraddr.sin_family = hp->h_addrtype;
474 connected = 1;
475 strcpy(hostname, hp->h_name);
476 }
477 if (argc < 4) {
478 cp = argc == 3 ? argv[2] : tail(src);
479 fd = creat(cp, 0644);
480 if (fd < 0) {
481 fprintf(stderr, "tftp: "); perror(cp);
482 return;
483 }
484 if (verbose)
485 printf("getting from %s:%s to %s [%s]\n",
486 hostname, src, cp, mode);
487 peeraddr.sin_port = port;
488 recvfile(fd, src, mode);
489 break;
490 }
491 cp = tail(src); /* new .. jdg */
492 fd = creat(cp, 0644);
493 if (fd < 0) {
494 fprintf(stderr, "tftp: "); perror(cp);
495 continue;
496 }
497 if (verbose)
498 printf("getting from %s:%s to %s [%s]\n",
499 hostname, src, cp, mode);
500 peeraddr.sin_port = port;
501 recvfile(fd, src, mode);
502 }
503 }
504
505 static void
506 getusage(s)
507 char *s;
508 {
509 printf("usage: %s host:file host:file ... file, or\n", s);
510 printf(" %s file file ... file if connected\n", s);
511 }
512
513 int rexmtval = TIMEOUT;
514
515 void
516 setrexmt(argc, argv)
517 int argc;
518 char *argv[];
519 {
520 int t;
521
522 if (argc < 2) {
523 strcpy(line, "Rexmt-timeout ");
524 printf("(value) ");
525 fgets(&line[strlen(line)], BUFSIZ-strlen(line)-1, stdin);
526 makeargv();
527 argc = margc;
528 argv = margv;
529 }
530 if (argc != 2) {
531 printf("usage: %s value\n", argv[0]);
532 return;
533 }
534 t = atoi(argv[1]);
535 if (t < 0)
536 printf("%s: bad value\n", argv[1]);
537 else
538 rexmtval = t;
539 }
540
541 int maxtimeout = 5 * TIMEOUT;
542
543 void
544 settimeout(argc, argv)
545 int argc;
546 char *argv[];
547 {
548 int t;
549
550 if (argc < 2) {
551 strcpy(line, "Maximum-timeout ");
552 printf("(value) ");
553 fgets(&line[strlen(line)], BUFSIZ-strlen(line)-1, stdin);
554 makeargv();
555 argc = margc;
556 argv = margv;
557 }
558 if (argc != 2) {
559 printf("usage: %s value\n", argv[0]);
560 return;
561 }
562 t = atoi(argv[1]);
563 if (t < 0)
564 printf("%s: bad value\n", argv[1]);
565 else
566 maxtimeout = t;
567 }
568
569 void
570 status(argc, argv)
571 int argc;
572 char *argv[];
573 {
574 if (connected)
575 printf("Connected to %s.\n", hostname);
576 else
577 printf("Not connected.\n");
578 printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
579 verbose ? "on" : "off", trace ? "on" : "off");
580 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
581 rexmtval, maxtimeout);
582 }
583
584 void
585 intr()
586 {
587
588 signal(SIGALRM, SIG_IGN);
589 alarm(0);
590 longjmp(toplevel, -1);
591 }
592
593 char *
594 tail(filename)
595 char *filename;
596 {
597 register char *s;
598
599 while (*filename) {
600 s = rindex(filename, '/');
601 if (s == NULL)
602 break;
603 if (s[1])
604 return (s + 1);
605 *s = '\0';
606 }
607 return (filename);
608 }
609
610 /*
611 * Command parser.
612 */
613 static __dead void
614 command()
615 {
616 register struct cmd *c;
617
618 for (;;) {
619 printf("%s> ", prompt);
620 if (fgets(line, BUFSIZ-1, stdin) == 0) {
621 if (feof(stdin)) {
622 exit(0);
623 } else {
624 continue;
625 }
626 }
627 if (line[0] == 0)
628 continue;
629 makeargv();
630 if (margc == 0)
631 continue;
632 c = getcmd(margv[0]);
633 if (c == (struct cmd *)-1) {
634 printf("?Ambiguous command\n");
635 continue;
636 }
637 if (c == 0) {
638 printf("?Invalid command\n");
639 continue;
640 }
641 (*c->handler)(margc, margv);
642 }
643 }
644
645 struct cmd *
646 getcmd(name)
647 register char *name;
648 {
649 register char *p, *q;
650 register struct cmd *c, *found;
651 register int nmatches, longest;
652
653 longest = 0;
654 nmatches = 0;
655 found = 0;
656 for (c = cmdtab; (p = c->name) != NULL; c++) {
657 for (q = name; *q == *p++; q++)
658 if (*q == 0) /* exact match? */
659 return (c);
660 if (!*q) { /* the name was a prefix */
661 if (q - name > longest) {
662 longest = q - name;
663 nmatches = 1;
664 found = c;
665 } else if (q - name == longest)
666 nmatches++;
667 }
668 }
669 if (nmatches > 1)
670 return ((struct cmd *)-1);
671 return (found);
672 }
673
674 /*
675 * Slice a string up into argc/argv.
676 */
677 static void
678 makeargv()
679 {
680 register char *cp;
681 register char **argp = margv;
682
683 margc = 0;
684 for (cp = line; *cp;) {
685 while (isspace(*cp))
686 cp++;
687 if (*cp == '\0')
688 break;
689 *argp++ = cp;
690 margc += 1;
691 while (*cp != '\0' && !isspace(*cp))
692 cp++;
693 if (*cp == '\0')
694 break;
695 *cp++ = '\0';
696 }
697 *argp++ = 0;
698 }
699
700 void
701 quit(argc, argv)
702 int argc;
703 char *argv[];
704 {
705
706 exit(0);
707 }
708
709 /*
710 * Help command.
711 */
712 void
713 help(argc, argv)
714 int argc;
715 char *argv[];
716 {
717 register struct cmd *c;
718
719 if (argc == 1) {
720 printf("Commands may be abbreviated. Commands are:\n\n");
721 for (c = cmdtab; c->name; c++)
722 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
723 return;
724 }
725 while (--argc > 0) {
726 register char *arg;
727 arg = *++argv;
728 c = getcmd(arg);
729 if (c == (struct cmd *)-1)
730 printf("?Ambiguous help command %s\n", arg);
731 else if (c == (struct cmd *)0)
732 printf("?Invalid help command %s\n", arg);
733 else
734 printf("%s\n", c->help);
735 }
736 }
737
738 void
739 settrace(argc, argv)
740 int argc;
741 char **argv;
742 {
743 trace = !trace;
744 printf("Packet tracing %s.\n", trace ? "on" : "off");
745 }
746
747 void
748 setverbose(argc, argv)
749 int argc;
750 char **argv;
751 {
752 verbose = !verbose;
753 printf("Verbose mode %s.\n", verbose ? "on" : "off");
754 }