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