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