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