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