Initial import from FreeBSD RELENG_4:
[games.git] / usr.bin / tftp / main.c
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 const 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 #if 0
42 static char sccsid[] = "@(#)main.c      8.1 (Berkeley) 6/6/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD: src/usr.bin/tftp/main.c,v 1.8.2.3 2002/05/14 22:08:07 bsd Exp $";
46 #endif /* not lint */
47
48 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
49
50 /*
51  * TFTP User Program -- Command Interface.
52  */
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/socket.h>
56 #include <sys/file.h>
57 #include <sys/param.h>
58
59 #include <netinet/in.h>
60
61 #include <arpa/inet.h>
62
63 #include <ctype.h>
64 #include <err.h>
65 #include <netdb.h>
66 #include <setjmp.h>
67 #include <signal.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <unistd.h>
72
73 #include "extern.h"
74
75 #define TIMEOUT         5               /* secs between rexmt's */
76
77 struct  sockaddr_storage peeraddr;
78 int     f;
79 int     trace;
80 int     verbose;
81 int     connected;
82 char    mode[32];
83 char    line[200];
84 int     margc;
85 char    *margv[20];
86 char    *prompt = "tftp";
87 jmp_buf toplevel;
88 volatile int txrx_error;
89 void    intr();
90
91 void    get __P((int, char **));
92 void    help __P((int, char **));
93 void    modecmd __P((int, char **));
94 void    put __P((int, char **));
95 void    quit __P((int, char **));
96 void    setascii __P((int, char **));
97 void    setbinary __P((int, char **));
98 void    setpeer0 __P((char *, char *));
99 void    setpeer __P((int, char **));
100 void    setrexmt __P((int, char **));
101 void    settimeout __P((int, char **));
102 void    settrace __P((int, char **));
103 void    setverbose __P((int, char **));
104 void    status __P((int, char **));
105
106 static void command __P((void)) __dead2;
107
108 static void getusage __P((char *));
109 static void makeargv __P((void));
110 static void putusage __P((char *));
111 static void settftpmode __P((char *));
112
113 #define HELPINDENT (sizeof("connect"))
114
115 struct cmd {
116         char    *name;
117         char    *help;
118         void    (*handler) __P((int, char **));
119 };
120
121 char    vhelp[] = "toggle verbose mode";
122 char    thelp[] = "toggle packet tracing";
123 char    chelp[] = "connect to remote tftp";
124 char    qhelp[] = "exit tftp";
125 char    hhelp[] = "print help information";
126 char    shelp[] = "send file";
127 char    rhelp[] = "receive file";
128 char    mhelp[] = "set file transfer mode";
129 char    sthelp[] = "show current status";
130 char    xhelp[] = "set per-packet retransmission timeout";
131 char    ihelp[] = "set total retransmission timeout";
132 char    ashelp[] = "set mode to netascii";
133 char    bnhelp[] = "set mode to octet";
134
135 struct cmd cmdtab[] = {
136         { "connect",    chelp,          setpeer },
137         { "mode",       mhelp,          modecmd },
138         { "put",        shelp,          put },
139         { "get",        rhelp,          get },
140         { "quit",       qhelp,          quit },
141         { "verbose",    vhelp,          setverbose },
142         { "trace",      thelp,          settrace },
143         { "status",     sthelp,         status },
144         { "binary",     bnhelp,         setbinary },
145         { "ascii",      ashelp,         setascii },
146         { "rexmt",      xhelp,          setrexmt },
147         { "timeout",    ihelp,          settimeout },
148         { "?",          hhelp,          help },
149         { 0 }
150 };
151
152 struct  cmd *getcmd();
153 char    *tail();
154
155 int
156 main(argc, argv)
157         int argc;
158         char *argv[];
159 {
160         f = -1;
161         strcpy(mode, "netascii");
162         signal(SIGINT, intr);
163         if (argc > 1) {
164                 if (setjmp(toplevel) != 0)
165                         exit(txrx_error);
166                 setpeer(argc, argv);
167         }
168         if (setjmp(toplevel) != 0)
169                 (void)putchar('\n');
170         command();
171 }
172
173 char    hostname[MAXHOSTNAMELEN];
174
175 void
176 setpeer0(host, port)
177         char *host;
178         char *port;
179 {
180         struct addrinfo hints, *res0, *res;
181         int error;
182         struct sockaddr_storage ss;
183         char *cause = "unknown";
184
185         if (connected) {
186                 close(f);
187                 f = -1;
188         }
189         connected = 0;
190
191         memset(&hints, 0, sizeof(hints));
192         hints.ai_family = PF_UNSPEC;
193         hints.ai_socktype = SOCK_DGRAM;
194         hints.ai_protocol = IPPROTO_UDP;
195         hints.ai_flags = AI_CANONNAME;
196         if (!port)
197                 port = "tftp";
198         error = getaddrinfo(host, port, &hints, &res0);
199         if (error) {
200                 warnx("%s", gai_strerror(error));
201                 return;
202         }
203
204         for (res = res0; res; res = res->ai_next) {
205                 if (res->ai_addrlen > sizeof(peeraddr))
206                         continue;
207                 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
208                 if (f < 0) {
209                         cause = "socket";
210                         continue;
211                 }
212
213                 memset(&ss, 0, sizeof(ss));
214                 ss.ss_family = res->ai_family;
215                 ss.ss_len = res->ai_addrlen;
216                 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
217                         cause = "bind";
218                         close(f);
219                         f = -1;
220                         continue;
221                 }
222
223                 break;
224         }
225
226         if (f < 0)
227                 warn("%s", cause);
228         else {
229                 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
230                 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
231                 if (res->ai_canonname) {
232                         (void) strncpy(hostname, res->ai_canonname,
233                                 sizeof(hostname));
234                 } else
235                         (void) strncpy(hostname, host, sizeof(hostname));
236                 hostname[sizeof(hostname)-1] = 0;
237                 connected = 1;
238         }
239
240         freeaddrinfo(res0);
241 }
242
243 void
244 setpeer(argc, argv)
245         int argc;
246         char *argv[];
247 {
248
249         if (argc < 2) {
250                 strcpy(line, "Connect ");
251                 printf("(to) ");
252                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
253                 makeargv();
254                 argc = margc;
255                 argv = margv;
256         }
257         if ((argc < 2) || (argc > 3)) {
258                 printf("usage: %s host-name [port]\n", argv[0]);
259                 return;
260         }
261         if (argc == 3)
262                 setpeer0(argv[1], NULL);
263         else
264                 setpeer0(argv[1], argv[2]);
265 }
266
267 struct  modes {
268         char *m_name;
269         char *m_mode;
270 } modes[] = {
271         { "ascii",      "netascii" },
272         { "netascii",   "netascii" },
273         { "binary",     "octet" },
274         { "image",      "octet" },
275         { "octet",     "octet" },
276 /*      { "mail",       "mail" },       */
277         { 0,            0 }
278 };
279
280 void
281 modecmd(argc, argv)
282         int argc;
283         char *argv[];
284 {
285         register struct modes *p;
286         char *sep;
287
288         if (argc < 2) {
289                 printf("Using %s mode to transfer files.\n", mode);
290                 return;
291         }
292         if (argc == 2) {
293                 for (p = modes; p->m_name; p++)
294                         if (strcmp(argv[1], p->m_name) == 0)
295                                 break;
296                 if (p->m_name) {
297                         settftpmode(p->m_mode);
298                         return;
299                 }
300                 printf("%s: unknown mode\n", argv[1]);
301                 /* drop through and print usage message */
302         }
303
304         printf("usage: %s [", argv[0]);
305         sep = " ";
306         for (p = modes; p->m_name; p++) {
307                 printf("%s%s", sep, p->m_name);
308                 if (*sep == ' ')
309                         sep = " | ";
310         }
311         printf(" ]\n");
312         return;
313 }
314
315 void
316 setbinary(argc, argv)
317         int argc;
318         char *argv[];
319 {
320
321         settftpmode("octet");
322 }
323
324 void
325 setascii(argc, argv)
326         int argc;
327         char *argv[];
328 {
329
330         settftpmode("netascii");
331 }
332
333 static void
334 settftpmode(newmode)
335         char *newmode;
336 {
337         strcpy(mode, newmode);
338         if (verbose)
339                 printf("mode set to %s\n", mode);
340 }
341
342
343 /*
344  * Send file(s).
345  */
346 void
347 put(argc, argv)
348         int argc;
349         char *argv[];
350 {
351         int fd;
352         register int n;
353         register char *cp, *targ;
354
355         if (argc < 2) {
356                 strcpy(line, "send ");
357                 printf("(file) ");
358                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
359                 makeargv();
360                 argc = margc;
361                 argv = margv;
362         }
363         if (argc < 2) {
364                 putusage(argv[0]);
365                 return;
366         }
367         targ = argv[argc - 1];
368         if (rindex(argv[argc - 1], ':')) {
369                 char *cp;
370
371                 for (n = 1; n < argc - 1; n++)
372                         if (index(argv[n], ':')) {
373                                 putusage(argv[0]);
374                                 return;
375                         }
376                 cp = argv[argc - 1];
377                 targ = rindex(cp, ':');
378                 *targ++ = 0;
379                 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
380                         cp[strlen(cp) - 1] = '\0';
381                         cp++;
382                 }
383                 setpeer0(cp, NULL);             
384         }
385         if (!connected) {
386                 printf("No target machine specified.\n");
387                 return;
388         }
389         if (argc < 4) {
390                 cp = argc == 2 ? tail(targ) : argv[1];
391                 fd = open(cp, O_RDONLY);
392                 if (fd < 0) {
393                         warn("%s", cp);
394                         return;
395                 }
396                 if (verbose)
397                         printf("putting %s to %s:%s [%s]\n",
398                                 cp, hostname, targ, mode);
399                 xmitfile(fd, targ, mode);
400                 return;
401         }
402                                 /* this assumes the target is a directory */
403                                 /* on a remote unix system.  hmmmm.  */
404         cp = index(targ, '\0');
405         *cp++ = '/';
406         for (n = 1; n < argc - 1; n++) {
407                 strcpy(cp, tail(argv[n]));
408                 fd = open(argv[n], O_RDONLY);
409                 if (fd < 0) {
410                         warn("%s", argv[n]);
411                         continue;
412                 }
413                 if (verbose)
414                         printf("putting %s to %s:%s [%s]\n",
415                                 argv[n], hostname, targ, mode);
416                 xmitfile(fd, targ, mode);
417         }
418 }
419
420 static void
421 putusage(s)
422         char *s;
423 {
424         printf("usage: %s file ... host:target, or\n", s);
425         printf("       %s file ... target (when already connected)\n", s);
426 }
427
428 /*
429  * Receive file(s).
430  */
431 void
432 get(argc, argv)
433         int argc;
434         char *argv[];
435 {
436         int fd;
437         register int n;
438         register char *cp;
439         char *src;
440
441         if (argc < 2) {
442                 strcpy(line, "get ");
443                 printf("(files) ");
444                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
445                 makeargv();
446                 argc = margc;
447                 argv = margv;
448         }
449         if (argc < 2) {
450                 getusage(argv[0]);
451                 return;
452         }
453         if (!connected) {
454                 for (n = 1; n < argc ; n++)
455                         if (rindex(argv[n], ':') == 0) {
456                                 getusage(argv[0]);
457                                 return;
458                         }
459         }
460         for (n = 1; n < argc ; n++) {
461                 src = rindex(argv[n], ':');
462                 if (src == NULL)
463                         src = argv[n];
464                 else {
465                         char *cp;
466                         *src++ = 0;
467                         cp = argv[n];
468                         if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
469                                 cp[strlen(cp) - 1] = '\0';
470                                 cp++;
471                         }
472                         setpeer0(cp, NULL);
473                         if (!connected)
474                                 continue;
475                 }
476                 if (argc < 4) {
477                         cp = argc == 3 ? argv[2] : tail(src);
478                         fd = creat(cp, 0644);
479                         if (fd < 0) {
480                                 warn("%s", cp);
481                                 return;
482                         }
483                         if (verbose)
484                                 printf("getting from %s:%s to %s [%s]\n",
485                                         hostname, src, cp, mode);
486                         recvfile(fd, src, mode);
487                         break;
488                 }
489                 cp = tail(src);         /* new .. jdg */
490                 fd = creat(cp, 0644);
491                 if (fd < 0) {
492                         warn("%s", cp);
493                         continue;
494                 }
495                 if (verbose)
496                         printf("getting from %s:%s to %s [%s]\n",
497                                 hostname, src, cp, mode);
498                 recvfile(fd, src, mode);
499         }
500 }
501
502 static void
503 getusage(s)
504         char *s;
505 {
506         printf("usage: %s host:file host:file ... file, or\n", s);
507         printf("       %s file file ... file if connected\n", s);
508 }
509
510 int     rexmtval = TIMEOUT;
511
512 void
513 setrexmt(argc, argv)
514         int argc;
515         char *argv[];
516 {
517         int t;
518
519         if (argc < 2) {
520                 strcpy(line, "Rexmt-timeout ");
521                 printf("(value) ");
522                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
523                 makeargv();
524                 argc = margc;
525                 argv = margv;
526         }
527         if (argc != 2) {
528                 printf("usage: %s value\n", argv[0]);
529                 return;
530         }
531         t = atoi(argv[1]);
532         if (t < 0)
533                 printf("%s: bad value\n", argv[1]);
534         else
535                 rexmtval = t;
536 }
537
538 int     maxtimeout = 5 * TIMEOUT;
539
540 void
541 settimeout(argc, argv)
542         int argc;
543         char *argv[];
544 {
545         int t;
546
547         if (argc < 2) {
548                 strcpy(line, "Maximum-timeout ");
549                 printf("(value) ");
550                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
551                 makeargv();
552                 argc = margc;
553                 argv = margv;
554         }
555         if (argc != 2) {
556                 printf("usage: %s value\n", argv[0]);
557                 return;
558         }
559         t = atoi(argv[1]);
560         if (t < 0)
561                 printf("%s: bad value\n", argv[1]);
562         else
563                 maxtimeout = t;
564 }
565
566 void
567 status(argc, argv)
568         int argc;
569         char *argv[];
570 {
571         if (connected)
572                 printf("Connected to %s.\n", hostname);
573         else
574                 printf("Not connected.\n");
575         printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
576                 verbose ? "on" : "off", trace ? "on" : "off");
577         printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
578                 rexmtval, maxtimeout);
579 }
580
581 void
582 intr()
583 {
584
585         signal(SIGALRM, SIG_IGN);
586         alarm(0);
587         longjmp(toplevel, -1);
588 }
589
590 char *
591 tail(filename)
592         char *filename;
593 {
594         register char *s;
595
596         while (*filename) {
597                 s = rindex(filename, '/');
598                 if (s == NULL)
599                         break;
600                 if (s[1])
601                         return (s + 1);
602                 *s = '\0';
603         }
604         return (filename);
605 }
606
607 /*
608  * Command parser.
609  */
610 static void
611 command()
612 {
613         register struct cmd *c;
614         char *cp;
615
616         for (;;) {
617                 printf("%s> ", prompt);
618                 if (fgets(line, sizeof line , stdin) == 0) {
619                         if (feof(stdin)) {
620                                 exit(txrx_error);
621                         } else {
622                                 continue;
623                         }
624                 }
625                 if ((cp = strchr(line, '\n')))
626                         *cp = '\0';
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         if ((cp = strchr(line, '\n')))
685                 *cp = '\0';
686         for (cp = line; *cp;) {
687                 while (isspace(*cp))
688                         cp++;
689                 if (*cp == '\0')
690                         break;
691                 *argp++ = cp;
692                 margc += 1;
693                 while (*cp != '\0' && !isspace(*cp))
694                         cp++;
695                 if (*cp == '\0')
696                         break;
697                 *cp++ = '\0';
698         }
699         *argp++ = 0;
700 }
701
702 void
703 quit(argc, argv)
704         int argc;
705         char *argv[];
706 {
707
708         exit(txrx_error);
709 }
710
711 /*
712  * Help command.
713  */
714 void
715 help(argc, argv)
716         int argc;
717         char *argv[];
718 {
719         register struct cmd *c;
720
721         if (argc == 1) {
722                 printf("Commands may be abbreviated.  Commands are:\n\n");
723                 for (c = cmdtab; c->name; c++)
724                         printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
725                 return;
726         }
727         while (--argc > 0) {
728                 register char *arg;
729                 arg = *++argv;
730                 c = getcmd(arg);
731                 if (c == (struct cmd *)-1)
732                         printf("?Ambiguous help command %s\n", arg);
733                 else if (c == (struct cmd *)0)
734                         printf("?Invalid help command %s\n", arg);
735                 else
736                         printf("%s\n", c->help);
737         }
738 }
739
740 void
741 settrace(argc, argv)
742         int argc;
743         char **argv;
744 {
745         trace = !trace;
746         printf("Packet tracing %s.\n", trace ? "on" : "off");
747 }
748
749 void
750 setverbose(argc, argv)
751         int argc;
752         char **argv;
753 {
754         verbose = !verbose;
755         printf("Verbose mode %s.\n", verbose ? "on" : "off");
756 }