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