]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.bin/tftp/main.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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(char *);
113 static void makeargv(void);
114 static void putusage(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(argc, argv)
161         int argc;
162         char *argv[];
163 {
164         f = -1;
165         strcpy(mode, "netascii");
166         signal(SIGINT, intr);
167         if (argc > 1) {
168                 if (setjmp(toplevel) != 0)
169                         exit(txrx_error);
170                 setpeer(argc, argv);
171         }
172         if (setjmp(toplevel) != 0)
173                 (void)putchar('\n');
174         command();
175 }
176
177 char    hostname[MAXHOSTNAMELEN];
178
179 void
180 setpeer0(host, port)
181         char *host;
182         const char *port;
183 {
184         struct addrinfo hints, *res0, *res;
185         int error;
186         struct sockaddr_storage ss;
187         const char *cause = "unknown";
188
189         if (connected) {
190                 close(f);
191                 f = -1;
192         }
193         connected = 0;
194
195         memset(&hints, 0, sizeof(hints));
196         hints.ai_family = PF_UNSPEC;
197         hints.ai_socktype = SOCK_DGRAM;
198         hints.ai_protocol = IPPROTO_UDP;
199         hints.ai_flags = AI_CANONNAME;
200         if (!port)
201                 port = "tftp";
202         error = getaddrinfo(host, port, &hints, &res0);
203         if (error) {
204                 warnx("%s", gai_strerror(error));
205                 return;
206         }
207
208         for (res = res0; res; res = res->ai_next) {
209                 if (res->ai_addrlen > sizeof(peeraddr))
210                         continue;
211                 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
212                 if (f < 0) {
213                         cause = "socket";
214                         continue;
215                 }
216
217                 memset(&ss, 0, sizeof(ss));
218                 ss.ss_family = res->ai_family;
219                 ss.ss_len = res->ai_addrlen;
220                 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
221                         cause = "bind";
222                         close(f);
223                         f = -1;
224                         continue;
225                 }
226
227                 break;
228         }
229
230         if (f < 0)
231                 warn("%s", cause);
232         else {
233                 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
234                 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
235                 if (res->ai_canonname) {
236                         (void) strncpy(hostname, res->ai_canonname,
237                                 sizeof(hostname));
238                 } else
239                         (void) strncpy(hostname, host, sizeof(hostname));
240                 hostname[sizeof(hostname)-1] = 0;
241                 connected = 1;
242         }
243
244         freeaddrinfo(res0);
245 }
246
247 void
248 setpeer(argc, argv)
249         int argc;
250         char *argv[];
251 {
252
253         if (argc < 2) {
254                 strcpy(line, "Connect ");
255                 printf("(to) ");
256                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
257                 makeargv();
258                 argc = margc;
259                 argv = margv;
260         }
261         if ((argc < 2) || (argc > 3)) {
262                 printf("usage: %s [host [port]]\n", argv[0]);
263                 return;
264         }
265         if (argc == 3)
266                 setpeer0(argv[1], argv[2]);
267         else
268                 setpeer0(argv[1], NULL);
269 }
270
271 struct  modes {
272         const char *m_name;
273         const char *m_mode;
274 } modes[] = {
275         { "ascii",      "netascii" },
276         { "netascii",   "netascii" },
277         { "binary",     "octet" },
278         { "image",      "octet" },
279         { "octet",     "octet" },
280 /*      { "mail",       "mail" },       */
281         { 0,            0 }
282 };
283
284 void
285 modecmd(argc, argv)
286         int argc;
287         char *argv[];
288 {
289         struct modes *p;
290         const char *sep;
291
292         if (argc < 2) {
293                 printf("Using %s mode to transfer files.\n", mode);
294                 return;
295         }
296         if (argc == 2) {
297                 for (p = modes; p->m_name; p++)
298                         if (strcmp(argv[1], p->m_name) == 0)
299                                 break;
300                 if (p->m_name) {
301                         settftpmode(p->m_mode);
302                         return;
303                 }
304                 printf("%s: unknown mode\n", argv[1]);
305                 /* drop through and print usage message */
306         }
307
308         printf("usage: %s [", argv[0]);
309         sep = " ";
310         for (p = modes; p->m_name; p++) {
311                 printf("%s%s", sep, p->m_name);
312                 if (*sep == ' ')
313                         sep = " | ";
314         }
315         printf(" ]\n");
316         return;
317 }
318
319 void
320 setbinary(argc, argv)
321         int argc __unused;
322         char *argv[] __unused;
323 {
324
325         settftpmode("octet");
326 }
327
328 void
329 setascii(argc, argv)
330         int argc __unused;
331         char *argv[] __unused;
332 {
333
334         settftpmode("netascii");
335 }
336
337 static void
338 settftpmode(newmode)
339         const char *newmode;
340 {
341         strcpy(mode, newmode);
342         if (verbose)
343                 printf("mode set to %s\n", mode);
344 }
345
346
347 /*
348  * Send file(s).
349  */
350 void
351 put(argc, argv)
352         int argc;
353         char *argv[];
354 {
355         int fd;
356         int n;
357         char *cp, *targ;
358
359         if (argc < 2) {
360                 strcpy(line, "send ");
361                 printf("(file) ");
362                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
363                 makeargv();
364                 argc = margc;
365                 argv = margv;
366         }
367         if (argc < 2) {
368                 putusage(argv[0]);
369                 return;
370         }
371         targ = argv[argc - 1];
372         if (rindex(argv[argc - 1], ':')) {
373                 char *lcp;
374
375                 for (n = 1; n < argc - 1; n++)
376                         if (index(argv[n], ':')) {
377                                 putusage(argv[0]);
378                                 return;
379                         }
380                 lcp = argv[argc - 1];
381                 targ = rindex(lcp, ':');
382                 *targ++ = 0;
383                 if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
384                         lcp[strlen(lcp) - 1] = '\0';
385                         lcp++;
386                 }
387                 setpeer0(lcp, NULL);            
388         }
389         if (!connected) {
390                 printf("No target machine specified.\n");
391                 return;
392         }
393         if (argc < 4) {
394                 cp = argc == 2 ? tail(targ) : argv[1];
395                 fd = open(cp, O_RDONLY);
396                 if (fd < 0) {
397                         warn("%s", cp);
398                         return;
399                 }
400                 if (verbose)
401                         printf("putting %s to %s:%s [%s]\n",
402                                 cp, hostname, targ, mode);
403                 xmitfile(fd, targ, mode);
404                 return;
405         }
406                                 /* this assumes the target is a directory */
407                                 /* on a remote unix system.  hmmmm.  */
408         cp = index(targ, '\0');
409         *cp++ = '/';
410         for (n = 1; n < argc - 1; n++) {
411                 strcpy(cp, tail(argv[n]));
412                 fd = open(argv[n], O_RDONLY);
413                 if (fd < 0) {
414                         warn("%s", argv[n]);
415                         continue;
416                 }
417                 if (verbose)
418                         printf("putting %s to %s:%s [%s]\n",
419                                 argv[n], hostname, targ, mode);
420                 xmitfile(fd, targ, mode);
421         }
422 }
423
424 static void
425 putusage(s)
426         char *s;
427 {
428         printf("usage: %s file [[host:]remotename]\n", s);
429         printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
430 }
431
432 /*
433  * Receive file(s).
434  */
435 void
436 get(argc, argv)
437         int argc;
438         char *argv[];
439 {
440         int fd;
441         int n;
442         char *cp;
443         char *src;
444
445         if (argc < 2) {
446                 strcpy(line, "get ");
447                 printf("(files) ");
448                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
449                 makeargv();
450                 argc = margc;
451                 argv = margv;
452         }
453         if (argc < 2) {
454                 getusage(argv[0]);
455                 return;
456         }
457         if (!connected) {
458                 for (n = 1; n < argc ; n++)
459                         if (rindex(argv[n], ':') == 0) {
460                                 getusage(argv[0]);
461                                 return;
462                         }
463         }
464         for (n = 1; n < argc ; n++) {
465                 src = rindex(argv[n], ':');
466                 if (src == NULL)
467                         src = argv[n];
468                 else {
469                         char *lcp;
470
471                         *src++ = 0;
472                         lcp = argv[n];
473                         if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
474                                 lcp[strlen(lcp) - 1] = '\0';
475                                 lcp++;
476                         }
477                         setpeer0(lcp, NULL);
478                         if (!connected)
479                                 continue;
480                 }
481                 if (argc < 4) {
482                         cp = argc == 3 ? argv[2] : tail(src);
483                         fd = creat(cp, 0644);
484                         if (fd < 0) {
485                                 warn("%s", cp);
486                                 return;
487                         }
488                         if (verbose)
489                                 printf("getting from %s:%s to %s [%s]\n",
490                                         hostname, src, cp, mode);
491                         recvfile(fd, src, mode);
492                         break;
493                 }
494                 cp = tail(src);         /* new .. jdg */
495                 fd = creat(cp, 0644);
496                 if (fd < 0) {
497                         warn("%s", cp);
498                         continue;
499                 }
500                 if (verbose)
501                         printf("getting from %s:%s to %s [%s]\n",
502                                 hostname, src, cp, mode);
503                 recvfile(fd, src, mode);
504         }
505 }
506
507 static void
508 getusage(s)
509         char *s;
510 {
511         printf("usage: %s [host:]file [localname]\n", s);
512         printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
513 }
514
515 int     rexmtval = TIMEOUT;
516
517 void
518 setrexmt(argc, argv)
519         int argc;
520         char *argv[];
521 {
522         int t;
523
524         if (argc < 2) {
525                 strcpy(line, "Rexmt-timeout ");
526                 printf("(value) ");
527                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
528                 makeargv();
529                 argc = margc;
530                 argv = margv;
531         }
532         if (argc != 2) {
533                 printf("usage: %s value\n", argv[0]);
534                 return;
535         }
536         t = atoi(argv[1]);
537         if (t < 0)
538                 printf("%s: bad value\n", argv[1]);
539         else
540                 rexmtval = t;
541 }
542
543 int     maxtimeout = 5 * TIMEOUT;
544
545 void
546 settimeout(argc, argv)
547         int argc;
548         char *argv[];
549 {
550         int t;
551
552         if (argc < 2) {
553                 strcpy(line, "Maximum-timeout ");
554                 printf("(value) ");
555                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
556                 makeargv();
557                 argc = margc;
558                 argv = margv;
559         }
560         if (argc != 2) {
561                 printf("usage: %s value\n", argv[0]);
562                 return;
563         }
564         t = atoi(argv[1]);
565         if (t < 0)
566                 printf("%s: bad value\n", argv[1]);
567         else
568                 maxtimeout = t;
569 }
570
571 void
572 status(argc, argv)
573         int argc __unused;
574         char *argv[] __unused;
575 {
576         if (connected)
577                 printf("Connected to %s.\n", hostname);
578         else
579                 printf("Not connected.\n");
580         printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
581                 verbose ? "on" : "off", trace ? "on" : "off");
582         printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
583                 rexmtval, maxtimeout);
584 }
585
586 void
587 intr(dummy)
588         int dummy __unused;
589 {
590
591         signal(SIGALRM, SIG_IGN);
592         alarm(0);
593         longjmp(toplevel, -1);
594 }
595
596 char *
597 tail(filename)
598         char *filename;
599 {
600         char *s;
601
602         while (*filename) {
603                 s = rindex(filename, '/');
604                 if (s == NULL)
605                         break;
606                 if (s[1])
607                         return (s + 1);
608                 *s = '\0';
609         }
610         return (filename);
611 }
612
613 static const char *
614 command_prompt()
615 {
616
617         return ("tftp> ");
618 }
619
620 /*
621  * Command parser.
622  */
623 static void
624 command()
625 {
626         HistEvent he;
627         struct cmd *c;
628         static EditLine *el;
629         static History *hist;
630         const char *bp;
631         char *cp;
632         int len, num, vrbose;
633
634         vrbose = isatty(0);
635         if (vrbose) {
636                 el = el_init("tftp", stdin, stdout, stderr);
637                 hist = history_init();
638                 history(hist, &he, H_SETSIZE, 100);
639                 el_set(el, EL_HIST, history, hist);
640                 el_set(el, EL_EDITOR, "emacs");
641                 el_set(el, EL_PROMPT, command_prompt);
642                 el_set(el, EL_SIGNAL, 1);
643                 el_source(el, NULL);
644         }
645         for (;;) {
646                 if (vrbose) {
647                         if ((bp = el_gets(el, &num)) == NULL || num == 0)
648                                 exit(0);
649                         len = (num > MAXLINE) ? MAXLINE : num;
650                         memcpy(line, bp, len);
651                         line[len] = '\0';
652                         history(hist, &he, H_ENTER, bp);
653                 } else {
654                         if (fgets(line, sizeof line , stdin) == 0) {
655                                 if (feof(stdin)) {
656                                         exit(txrx_error);
657                                 } else {
658                                         continue;
659                                 }
660                         }
661                 }
662                 if ((cp = strchr(line, '\n')))
663                         *cp = '\0';
664                 if (line[0] == 0)
665                         continue;
666                 makeargv();
667                 if (margc == 0)
668                         continue;
669                 c = getcmd(margv[0]);
670                 if (c == (struct cmd *)-1) {
671                         printf("?Ambiguous command\n");
672                         continue;
673                 }
674                 if (c == 0) {
675                         printf("?Invalid command\n");
676                         continue;
677                 }
678                 (*c->handler)(margc, margv);
679         }
680 }
681
682 struct cmd *
683 getcmd(name)
684         char *name;
685 {
686         const char *p, *q;
687         struct cmd *c, *found;
688         int nmatches, longest;
689
690         longest = 0;
691         nmatches = 0;
692         found = 0;
693         for (c = cmdtab; (p = c->name) != NULL; c++) {
694                 for (q = name; *q == *p++; q++)
695                         if (*q == 0)            /* exact match? */
696                                 return (c);
697                 if (!*q) {                      /* the name was a prefix */
698                         if (q - name > longest) {
699                                 longest = q - name;
700                                 nmatches = 1;
701                                 found = c;
702                         } else if (q - name == longest)
703                                 nmatches++;
704                 }
705         }
706         if (nmatches > 1)
707                 return ((struct cmd *)-1);
708         return (found);
709 }
710
711 /*
712  * Slice a string up into argc/argv.
713  */
714 static void
715 makeargv()
716 {
717         char *cp;
718         char **argp = margv;
719
720         margc = 0;
721         if ((cp = strchr(line, '\n')))
722                 *cp = '\0';
723         for (cp = line; margc < MAX_MARGV - 1 && *cp;) {
724                 while (isspace(*cp))
725                         cp++;
726                 if (*cp == '\0')
727                         break;
728                 *argp++ = cp;
729                 margc += 1;
730                 while (*cp != '\0' && !isspace(*cp))
731                         cp++;
732                 if (*cp == '\0')
733                         break;
734                 *cp++ = '\0';
735         }
736         *argp++ = 0;
737 }
738
739 void
740 quit(argc, argv)
741         int argc __unused;
742         char *argv[] __unused;
743 {
744         exit(txrx_error);
745 }
746
747 /*
748  * Help command.
749  */
750 void
751 help(argc, argv)
752         int argc;
753         char *argv[];
754 {
755         struct cmd *c;
756
757         if (argc == 1) {
758                 printf("Commands may be abbreviated.  Commands are:\n\n");
759                 for (c = cmdtab; c->name; c++)
760                         printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
761                 return;
762         }
763         while (--argc > 0) {
764                 char *arg;
765                 arg = *++argv;
766                 c = getcmd(arg);
767                 if (c == (struct cmd *)-1)
768                         printf("?Ambiguous help command %s\n", arg);
769                 else if (c == (struct cmd *)0)
770                         printf("?Invalid help command %s\n", arg);
771                 else
772                         printf("%s\n", c->help);
773         }
774 }
775
776 void
777 settrace(argc, argv)
778         int argc __unused;
779         char **argv __unused;
780 {
781         trace = !trace;
782         printf("Packet tracing %s.\n", trace ? "on" : "off");
783 }
784
785 void
786 setverbose(argc, argv)
787         int argc __unused;
788         char **argv __unused;
789 {
790         verbose = !verbose;
791         printf("Verbose mode %s.\n", verbose ? "on" : "off");
792 }