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