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