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