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