1a354385e49a2f7312fafcc19ae782556f9246fd
[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 char    *prompt = "tftp";
79 jmp_buf toplevel;
80 volatile int txrx_error;
81 void    intr();
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 *, 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(char *);
104
105 #define HELPINDENT (sizeof("connect"))
106
107 struct cmd {
108         char    *name;
109         char    *help;
110         void    (*handler)(int, char **);
111 };
112
113 char    vhelp[] = "toggle verbose mode";
114 char    thelp[] = "toggle packet tracing";
115 char    chelp[] = "connect to remote tftp";
116 char    qhelp[] = "exit tftp";
117 char    hhelp[] = "print help information";
118 char    shelp[] = "send file";
119 char    rhelp[] = "receive file";
120 char    mhelp[] = "set file transfer mode";
121 char    sthelp[] = "show current status";
122 char    xhelp[] = "set per-packet retransmission timeout";
123 char    ihelp[] = "set total retransmission timeout";
124 char    ashelp[] = "set mode to netascii";
125 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         { 0 }
142 };
143
144 struct  cmd *getcmd();
145 char    *tail();
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, char *port)
167 {
168         struct addrinfo hints, *res0, *res;
169         int error;
170         struct sockaddr_storage ss;
171         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         char *m_name;
255         char *m_mode;
256 } modes[] = {
257         { "ascii",      "netascii" },
258         { "netascii",   "netascii" },
259         { "binary",     "octet" },
260         { "image",      "octet" },
261         { "octet",     "octet" },
262 /*      { "mail",       "mail" },       */
263         { 0,            0 }
264 };
265
266 void
267 modecmd(int argc, char **argv)
268 {
269         struct modes *p;
270         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, char **argv)
301 {
302
303         settftpmode("octet");
304 }
305
306 void
307 setascii(int argc, char **argv)
308 {
309
310         settftpmode("netascii");
311 }
312
313 static void
314 settftpmode(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                 char *cp;
347
348                 for (n = 1; n < argc - 1; n++)
349                         if (strchr(argv[n], ':')) {
350                                 putusage(argv[0]);
351                                 return;
352                         }
353                 cp = argv[argc - 1];
354                 targ = strrchr(cp, ':');
355                 *targ++ = 0;
356                 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
357                         cp[strlen(cp) - 1] = '\0';
358                         cp++;
359                 }
360                 setpeer0(cp, NULL);             
361         }
362         if (!connected) {
363                 printf("No target machine specified.\n");
364                 return;
365         }
366         if (argc < 4) {
367                 cp = argc == 2 ? tail(targ) : argv[1];
368                 fd = open(cp, O_RDONLY);
369                 if (fd < 0) {
370                         warn("%s", cp);
371                         return;
372                 }
373                 if (verbose)
374                         printf("putting %s to %s:%s [%s]\n",
375                                 cp, hostname, targ, mode);
376                 xmitfile(fd, targ, mode);
377                 return;
378         }
379                                 /* this assumes the target is a directory */
380                                 /* on a remote unix system.  hmmmm.  */
381         cp = strchr(targ, '\0');
382         *cp++ = '/';
383         for (n = 1; n < argc - 1; n++) {
384                 strcpy(cp, tail(argv[n]));
385                 fd = open(argv[n], O_RDONLY);
386                 if (fd < 0) {
387                         warn("%s", argv[n]);
388                         continue;
389                 }
390                 if (verbose)
391                         printf("putting %s to %s:%s [%s]\n",
392                                 argv[n], hostname, targ, mode);
393                 xmitfile(fd, targ, mode);
394         }
395 }
396
397 static void
398 putusage(char *s)
399 {
400         printf("usage: %s file ... host:target, or\n", s);
401         printf("       %s file ... target (when already connected)\n", s);
402 }
403
404 /*
405  * Receive file(s).
406  */
407 void
408 get(int argc, char **argv)
409 {
410         int fd;
411         int n;
412         char *cp;
413         char *src;
414
415         if (argc < 2) {
416                 strcpy(line, "get ");
417                 printf("(files) ");
418                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
419                 makeargv();
420                 argc = margc;
421                 argv = margv;
422         }
423         if (argc < 2) {
424                 getusage(argv[0]);
425                 return;
426         }
427         if (!connected) {
428                 for (n = 1; n < argc ; n++)
429                         if (strrchr(argv[n], ':') == 0) {
430                                 getusage(argv[0]);
431                                 return;
432                         }
433         }
434         for (n = 1; n < argc ; n++) {
435                 src = strrchr(argv[n], ':');
436                 if (src == NULL)
437                         src = argv[n];
438                 else {
439                         char *cp;
440                         *src++ = 0;
441                         cp = argv[n];
442                         if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
443                                 cp[strlen(cp) - 1] = '\0';
444                                 cp++;
445                         }
446                         setpeer0(cp, NULL);
447                         if (!connected)
448                                 continue;
449                 }
450                 if (argc < 4) {
451                         cp = argc == 3 ? argv[2] : tail(src);
452                         fd = creat(cp, 0644);
453                         if (fd < 0) {
454                                 warn("%s", cp);
455                                 return;
456                         }
457                         if (verbose)
458                                 printf("getting from %s:%s to %s [%s]\n",
459                                         hostname, src, cp, mode);
460                         recvfile(fd, src, mode);
461                         break;
462                 }
463                 cp = tail(src);         /* new .. jdg */
464                 fd = creat(cp, 0644);
465                 if (fd < 0) {
466                         warn("%s", cp);
467                         continue;
468                 }
469                 if (verbose)
470                         printf("getting from %s:%s to %s [%s]\n",
471                                 hostname, src, cp, mode);
472                 recvfile(fd, src, mode);
473         }
474 }
475
476 static void
477 getusage(char *s)
478 {
479         printf("usage: %s host:file host:file ... file, or\n", s);
480         printf("       %s file file ... file if connected\n", s);
481 }
482
483 int     rexmtval = TIMEOUT;
484
485 void
486 setrexmt(int argc, char **argv)
487 {
488         int t;
489
490         if (argc < 2) {
491                 strcpy(line, "Rexmt-timeout ");
492                 printf("(value) ");
493                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
494                 makeargv();
495                 argc = margc;
496                 argv = margv;
497         }
498         if (argc != 2) {
499                 printf("usage: %s value\n", argv[0]);
500                 return;
501         }
502         t = atoi(argv[1]);
503         if (t < 0)
504                 printf("%s: bad value\n", argv[1]);
505         else
506                 rexmtval = t;
507 }
508
509 int     maxtimeout = 5 * TIMEOUT;
510
511 void
512 settimeout(int argc, char **argv)
513 {
514         int t;
515
516         if (argc < 2) {
517                 strcpy(line, "Maximum-timeout ");
518                 printf("(value) ");
519                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
520                 makeargv();
521                 argc = margc;
522                 argv = margv;
523         }
524         if (argc != 2) {
525                 printf("usage: %s value\n", argv[0]);
526                 return;
527         }
528         t = atoi(argv[1]);
529         if (t < 0)
530                 printf("%s: bad value\n", argv[1]);
531         else
532                 maxtimeout = t;
533 }
534
535 void
536 status(int argc, char **argv)
537 {
538         if (connected)
539                 printf("Connected to %s.\n", hostname);
540         else
541                 printf("Not connected.\n");
542         printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
543                 verbose ? "on" : "off", trace ? "on" : "off");
544         printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
545                 rexmtval, maxtimeout);
546 }
547
548 void
549 intr(void)
550 {
551
552         signal(SIGALRM, SIG_IGN);
553         alarm(0);
554         longjmp(toplevel, -1);
555 }
556
557 char *
558 tail(char *filename)
559 {
560         char *s;
561
562         while (*filename) {
563                 s = strrchr(filename, '/');
564                 if (s == NULL)
565                         break;
566                 if (s[1])
567                         return (s + 1);
568                 *s = '\0';
569         }
570         return (filename);
571 }
572
573 /*
574  * Command parser.
575  */
576 static void
577 command(void)
578 {
579         struct cmd *c;
580         char *cp;
581
582         for (;;) {
583                 printf("%s> ", prompt);
584                 if (fgets(line, sizeof line , stdin) == 0) {
585                         if (feof(stdin)) {
586                                 exit(txrx_error);
587                         } else {
588                                 continue;
589                         }
590                 }
591                 if ((cp = strchr(line, '\n')))
592                         *cp = '\0';
593                 if (line[0] == 0)
594                         continue;
595                 makeargv();
596                 if (margc == 0)
597                         continue;
598                 c = getcmd(margv[0]);
599                 if (c == (struct cmd *)-1) {
600                         printf("?Ambiguous command\n");
601                         continue;
602                 }
603                 if (c == 0) {
604                         printf("?Invalid command\n");
605                         continue;
606                 }
607                 (*c->handler)(margc, margv);
608         }
609 }
610
611 struct cmd *
612 getcmd(char *name)
613 {
614         char *p, *q;
615         struct cmd *c, *found;
616         int nmatches, longest;
617
618         longest = 0;
619         nmatches = 0;
620         found = 0;
621         for (c = cmdtab; (p = c->name) != NULL; c++) {
622                 for (q = name; *q == *p++; q++)
623                         if (*q == 0)            /* exact match? */
624                                 return (c);
625                 if (!*q) {                      /* the name was a prefix */
626                         if (q - name > longest) {
627                                 longest = q - name;
628                                 nmatches = 1;
629                                 found = c;
630                         } else if (q - name == longest)
631                                 nmatches++;
632                 }
633         }
634         if (nmatches > 1)
635                 return ((struct cmd *)-1);
636         return (found);
637 }
638
639 /*
640  * Slice a string up into argc/argv.
641  */
642 static void
643 makeargv(void)
644 {
645         char *cp;
646         char **argp = margv;
647
648         margc = 0;
649         if ((cp = strchr(line, '\n')))
650                 *cp = '\0';
651         for (cp = line; margc < MAX_MARGV -1 && *cp;) {
652                 while (isspace(*cp))
653                         cp++;
654                 if (*cp == '\0')
655                         break;
656                 *argp++ = cp;
657                 margc += 1;
658                 while (*cp != '\0' && !isspace(*cp))
659                         cp++;
660                 if (*cp == '\0')
661                         break;
662                 *cp++ = '\0';
663         }
664         *argp++ = 0;
665 }
666
667 void
668 quit(int argc, char **argv)
669 {
670
671         exit(txrx_error);
672 }
673
674 /*
675  * Help command.
676  */
677 void
678 help(int argc, char **argv)
679 {
680         struct cmd *c;
681
682         if (argc == 1) {
683                 printf("Commands may be abbreviated.  Commands are:\n\n");
684                 for (c = cmdtab; c->name; c++)
685                         printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
686                 return;
687         }
688         while (--argc > 0) {
689                 char *arg;
690                 arg = *++argv;
691                 c = getcmd(arg);
692                 if (c == (struct cmd *)-1)
693                         printf("?Ambiguous help command %s\n", arg);
694                 else if (c == (struct cmd *)0)
695                         printf("?Invalid help command %s\n", arg);
696                 else
697                         printf("%s\n", c->help);
698         }
699 }
700
701 void
702 settrace(int argc, char **argv)
703 {
704         trace = !trace;
705         printf("Packet tracing %s.\n", trace ? "on" : "off");
706 }
707
708 void
709 setverbose(int argc, char **argv)
710 {
711         verbose = !verbose;
712         printf("Verbose mode %s.\n", verbose ? "on" : "off");
713 }