]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/tftp/main.c
stat(1): Fix grammar error in stat.c
[FreeBSD/FreeBSD.git] / usr.bin / tftp / main.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
33
34 /*
35  * TFTP User Program -- Command Interface.
36  */
37 #include <sys/param.h>
38 #include <sys/file.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <arpa/tftp.h>
46
47 #include <ctype.h>
48 #include <err.h>
49 #include <histedit.h>
50 #include <netdb.h>
51 #include <setjmp.h>
52 #include <signal.h>
53 #include <stdbool.h>
54 #include <stddef.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #include "tftp-utils.h"
61 #include "tftp-io.h"
62 #include "tftp-options.h"
63 #include "tftp.h"
64
65 #define MAXLINE         (2 * MAXPATHLEN)
66 #define TIMEOUT         5               /* secs between rexmt's */
67
68 typedef struct  sockaddr_storage peeraddr;
69 static int      connected;
70 static char     mode[32];
71 static jmp_buf  toplevel;
72 static int      txrx_error;
73 static int      peer;
74
75 #define MAX_MARGV       20
76 static int      margc;
77 static char     *margv[MAX_MARGV];
78
79 int             verbose;
80 static char     *port = NULL;
81
82 static void     get(int, char **);
83 static void     help(int, char **);
84 static void     intr(int);
85 static void     modecmd(int, char **);
86 static void     put(int, char **);
87 static void     quit(int, char **);
88 static void     setascii(int, char **);
89 static void     setbinary(int, char **);
90 static void     setpeer0(char *, const char *);
91 static void     setpeer(int, char **);
92 static void     settimeoutpacket(int, char **);
93 static void     settimeoutnetwork(int, char **);
94 static void     setdebug(int, char **);
95 static void     setverbose(int, char **);
96 static void     showstatus(int, char **);
97 static void     setblocksize(int, char **);
98 static void     setblocksize2(int, char **);
99 static void     setoptions(int, char **);
100 static void     setrollover(int, char **);
101 static void     setpacketdrop(int, char **);
102 static void     setwindowsize(int, char **);
103
104 static void command(bool, EditLine *, History *, HistEvent *) __dead2;
105 static const char *command_prompt(void);
106
107 static void urihandling(char *URI);
108 static void getusage(char *);
109 static void makeargv(char *line);
110 static void putusage(char *);
111 static void settftpmode(const char *);
112
113 static char     *tail(char *);
114 static const struct cmd *getcmd(const char *);
115
116 #define HELPINDENT (sizeof("connect"))
117
118 struct cmd {
119         const char      *name;
120         void    (*handler)(int, char **);
121         const char      *help;
122 };
123
124 static struct cmd cmdtab[] = {
125         { "connect",    setpeer,        "connect to remote tftp"        },
126         { "mode",       modecmd,        "set file transfer mode"        },
127         { "put",        put,            "send file"                     },
128         { "get",        get,            "receive file"                  },
129         { "quit",       quit,           "exit tftp"                     },
130         { "verbose",    setverbose,     "toggle verbose mode"           },
131         { "status",     showstatus,     "show current status"           },
132         { "binary",     setbinary,      "set mode to octet"             },
133         { "ascii",      setascii,       "set mode to netascii"          },
134         { "rexmt",      settimeoutpacket,
135           "set per-packet retransmission timeout[-]" },
136         { "timeout",    settimeoutnetwork,
137           "set total retransmission timeout" },
138         { "trace",      setdebug,       "enable 'debug packet'[-]"      },
139         { "debug",      setdebug,       "enable verbose output"         },
140         { "blocksize",  setblocksize,   "set blocksize[*]"              },
141         { "blocksize2", setblocksize2,  "set blocksize as a power of 2[**]" },
142         { "rollover",   setrollover,    "rollover after 64K packets[**]" },
143         { "options",    setoptions,
144           "enable or disable RFC2347 style options" },
145         { "help",       help,           "print help information"        },
146         { "packetdrop", setpacketdrop,  "artificial packetloss feature" },
147         { "windowsize", setwindowsize,  "set windowsize[*]"             },
148         { "?",          help,           "print help information"        },
149         { NULL,         NULL,           NULL                            }
150 };
151
152 static struct   modes {
153         const char *m_name;
154         const char *m_mode;
155 } modes[] = {
156         { "ascii",      "netascii" },
157         { "netascii",   "netascii" },
158         { "binary",     "octet" },
159         { "image",      "octet" },
160         { "octet",      "octet" },
161         { NULL,         NULL }
162 };
163
164 int
165 main(int argc, char *argv[])
166 {
167         HistEvent he;
168         static EditLine *el;
169         static History *hist;
170         bool interactive;
171
172         acting_as_client = 1;
173         peer = -1;
174         strcpy(mode, "octet");
175         signal(SIGINT, intr);
176
177         interactive = isatty(STDIN_FILENO);
178         if (interactive) {
179                 el = el_init("tftp", stdin, stdout, stderr);
180                 hist = history_init();
181                 history(hist, &he, H_SETSIZE, 100);
182                 el_set(el, EL_HIST, history, hist);
183                 el_set(el, EL_EDITOR, "emacs");
184                 el_set(el, EL_PROMPT, command_prompt);
185                 el_set(el, EL_SIGNAL, 1);
186                 el_source(el, NULL);
187         }
188
189         if (argc > 1) {
190                 if (setjmp(toplevel) != 0)
191                         exit(txrx_error);
192
193                 if (strncmp(argv[1], "tftp://", 7) == 0) {
194                         urihandling(argv[1]);
195                         exit(txrx_error);
196                 }
197
198                 setpeer(argc, argv);
199         }
200
201         if (setjmp(toplevel) != 0) {
202                 if (interactive)
203                         el_reset(el);
204                 (void)putchar('\n');
205         }
206
207         init_options();
208         command(interactive, el, hist, &he);
209 }
210
211 /*
212  * RFC3617 handling of TFTP URIs:
213  *
214  *    tftpURI         = "tftp://" host "/" file [ mode ]
215  *    mode            = ";"  "mode=" ( "netascii" / "octet" )
216  *    file            = *( unreserved / escaped )
217  *    host            = <as specified by RFC 2732>
218  *    unreserved      = <as specified in RFC 2396>
219  *    escaped         = <as specified in RFC 2396>
220  *
221  * We are cheating a little bit by allowing any mode as specified in the
222  * modes table defined earlier on in this file and mapping it on the real
223  * mode.
224  */
225 static void
226 urihandling(char *URI)
227 {
228         char    uri[ARG_MAX];
229         char    *host = NULL;
230         char    *path = NULL;
231         char    *opts = NULL;
232         const char *tmode = "octet";
233         char    *s;
234         char    line[MAXLINE];
235         int     i;
236
237         strlcpy(uri, URI, ARG_MAX);
238         host = uri + 7;
239
240         if ((s = strchr(host, '/')) == NULL) {
241                 fprintf(stderr,
242                     "Invalid URI: Couldn't find / after hostname\n");
243                 exit(1);
244         }
245         *s = '\0';
246         path = s + 1;
247
248         if ((s = strchr(path, ';')) != NULL) {
249                 *s = '\0';
250                 opts = s + 1;
251
252                 if (strncmp(opts, "mode=", 5) == 0) {
253                         tmode = opts;
254                         tmode += 5;
255
256                         for (i = 0; modes[i].m_name != NULL; i++) {
257                                 if (strcmp(modes[i].m_name, tmode) == 0)
258                                         break;
259                         }
260                         if (modes[i].m_name == NULL) {
261                                 fprintf(stderr, "Invalid mode: '%s'\n", mode);
262                                 exit(1);
263                         }
264                         settftpmode(modes[i].m_mode);
265                 }
266         } else {
267                 settftpmode("octet");
268         }
269
270         setpeer0(host, NULL);
271
272         sprintf(line, "get %s", path);
273         makeargv(line);
274         get(margc, margv);
275 }
276
277 static char    hostname[MAXHOSTNAMELEN];
278
279 static void
280 setpeer0(char *host, const char *lport)
281 {
282         struct addrinfo hints, *res0, *res;
283         int error;
284         const char *cause = "unknown";
285
286         if (connected) {
287                 close(peer);
288                 peer = -1;
289         }
290         connected = 0;
291
292         memset(&hints, 0, sizeof(hints));
293         hints.ai_family = PF_UNSPEC;
294         hints.ai_socktype = SOCK_DGRAM;
295         hints.ai_protocol = IPPROTO_UDP;
296         hints.ai_flags = AI_CANONNAME;
297         if (!lport)
298                 lport = "tftp";
299         error = getaddrinfo(host, lport, &hints, &res0);
300         if (error) {
301                 warnx("%s", gai_strerror(error));
302                 return;
303         }
304
305         for (res = res0; res; res = res->ai_next) {
306                 if (res->ai_addrlen > sizeof(peeraddr))
307                         continue;
308                 peer = socket(res->ai_family, res->ai_socktype,
309                         res->ai_protocol);
310                 if (peer < 0) {
311                         cause = "socket";
312                         continue;
313                 }
314
315                 memset(&peer_sock, 0, sizeof(peer_sock));
316                 peer_sock.ss_family = res->ai_family;
317                 peer_sock.ss_len = res->ai_addrlen;
318                 if (bind(peer, (struct sockaddr *)&peer_sock, peer_sock.ss_len) < 0) {
319                         cause = "bind";
320                         close(peer);
321                         peer = -1;
322                         continue;
323                 }
324
325                 break;
326         }
327
328         if (peer < 0)
329                 warn("%s", cause);
330         else {
331                 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */
332                 memcpy(&peer_sock, res->ai_addr, res->ai_addrlen);
333                 if (res->ai_canonname) {
334                         (void) strlcpy(hostname, res->ai_canonname,
335                                 sizeof(hostname));
336                 } else
337                         (void) strlcpy(hostname, host, sizeof(hostname));
338                 connected = 1;
339         }
340
341         freeaddrinfo(res0);
342 }
343
344 static void
345 setpeer(int argc, char *argv[])
346 {
347         char    line[MAXLINE];
348
349         if (argc < 2) {
350                 strcpy(line, "Connect ");
351                 printf("(to) ");
352                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
353                 makeargv(line);
354                 argc = margc;
355                 argv = margv;
356         }
357         if ((argc < 2) || (argc > 3)) {
358                 printf("usage: %s [host [port]]\n", argv[0]);
359                 return;
360         }
361         if (argc == 3) {
362                 port = argv[2];
363                 setpeer0(argv[1], argv[2]);
364         } else
365                 setpeer0(argv[1], NULL);
366 }
367
368 static void
369 modecmd(int argc, char *argv[])
370 {
371         struct modes *p;
372         const char *sep;
373
374         if (argc < 2) {
375                 printf("Using %s mode to transfer files.\n", mode);
376                 return;
377         }
378         if (argc == 2) {
379                 for (p = modes; p->m_name; p++)
380                         if (strcmp(argv[1], p->m_name) == 0)
381                                 break;
382                 if (p->m_name) {
383                         settftpmode(p->m_mode);
384                         return;
385                 }
386                 printf("%s: unknown mode\n", argv[1]);
387                 /* drop through and print usage message */
388         }
389
390         printf("usage: %s [", argv[0]);
391         sep = " ";
392         for (p = modes; p->m_name != NULL; p++) {
393                 printf("%s%s", sep, p->m_name);
394                 if (*sep == ' ')
395                         sep = " | ";
396         }
397         printf(" ]\n");
398         return;
399 }
400
401 static void
402 setbinary(int argc __unused, char *argv[] __unused)
403 {
404
405         settftpmode("octet");
406 }
407
408 static void
409 setascii(int argc __unused, char *argv[] __unused)
410 {
411
412         settftpmode("netascii");
413 }
414
415 static void
416 settftpmode(const char *newmode)
417 {
418
419         strlcpy(mode, newmode, sizeof(mode));
420         if (verbose)
421                 printf("mode set to %s\n", mode);
422 }
423
424
425 /*
426  * Send file(s).
427  */
428 static void
429 put(int argc, char *argv[])
430 {
431         int     fd;
432         int     n;
433         char    *cp, *targ, *path;
434         char    line[MAXLINE];
435         struct stat sb;
436
437         if (argc < 2) {
438                 strcpy(line, "send ");
439                 printf("(file) ");
440                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
441                 makeargv(line);
442                 argc = margc;
443                 argv = margv;
444         }
445         if (argc < 2) {
446                 putusage(argv[0]);
447                 return;
448         }
449         targ = argv[argc - 1];
450         if (strrchr(argv[argc - 1], ':')) {
451                 char *lcp;
452
453                 for (n = 1; n < argc - 1; n++)
454                         if (strchr(argv[n], ':')) {
455                                 putusage(argv[0]);
456                                 return;
457                         }
458                 lcp = argv[argc - 1];
459                 targ = strrchr(lcp, ':');
460                 *targ++ = 0;
461                 if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
462                         lcp[strlen(lcp) - 1] = '\0';
463                         lcp++;
464                 }
465                 setpeer0(lcp, NULL);
466         }
467         if (!connected) {
468                 printf("No target machine specified.\n");
469                 return;
470         }
471         if (argc < 4) {
472                 cp = argc == 2 ? tail(targ) : argv[1];
473                 fd = open(cp, O_RDONLY);
474                 if (fd < 0) {
475                         warn("%s", cp);
476                         return;
477                 }
478
479                 if (fstat(fd, &sb) < 0) {
480                         warn("%s", cp);
481                         close(fd);
482                         return;
483                 }
484                 options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size);
485
486                 if (verbose)
487                         printf("putting %s to %s:%s [%s]\n",
488                             cp, hostname, targ, mode);
489                 if (xmitfile(peer, port, fd, targ, mode))
490                         txrx_error = 1;
491                 close(fd);
492                 return;
493         }
494                                 /* this assumes the target is a directory */
495                                 /* on a remote unix system.  hmmmm.  */
496         for (n = 1; n < argc - 1; n++) {
497                 if (asprintf(&path, "%s/%s", targ, tail(argv[n])) < 0)
498                         err(1, "malloc");
499
500                 fd = open(argv[n], O_RDONLY);
501                 if (fd < 0) {
502                         warn("%s", argv[n]);
503                         free(path);
504                         continue;
505                 }
506
507                 if (fstat(fd, &sb) < 0) {
508                         warn("%s", argv[n]);
509                         close(fd);
510                         free(path);
511                         continue;
512                 }
513                 options_set_request(OPT_TSIZE, "%ju", (uintmax_t)sb.st_size);
514
515                 if (verbose)
516                         printf("putting %s to %s:%s [%s]\n",
517                             argv[n], hostname, path, mode);
518                 if (xmitfile(peer, port, fd, path, mode) != 0)
519                         txrx_error = 1;
520                 close(fd);
521
522                 free(path);
523         }
524 }
525
526 static void
527 putusage(char *s)
528 {
529
530         printf("usage: %s file [remotename]\n", s);
531         printf("       %s file host:remotename\n", s);
532         printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n", s);
533 }
534
535 /*
536  * Receive file(s).
537  */
538 static void
539 get(int argc, char *argv[])
540 {
541         int fd;
542         int n;
543         char *cp;
544         char *src;
545         char    line[MAXLINE];
546
547         if (argc < 2) {
548                 strcpy(line, "get ");
549                 printf("(files) ");
550                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
551                 makeargv(line);
552                 argc = margc;
553                 argv = margv;
554         }
555         if (argc < 2) {
556                 getusage(argv[0]);
557                 return;
558         }
559         if (!connected) {
560                 for (n = 1; n < argc ; n++)
561                         if (strrchr(argv[n], ':') == 0) {
562                                 printf("No remote host specified and "
563                                     "no host given for file '%s'\n", argv[n]);
564                                 getusage(argv[0]);
565                                 return;
566                         }
567         }
568         for (n = 1; n < argc ; n++) {
569                 src = strrchr(argv[n], ':');
570                 if (src == NULL)
571                         src = argv[n];
572                 else {
573                         char *lcp;
574
575                         *src++ = 0;
576                         lcp = argv[n];
577                         if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') {
578                                 lcp[strlen(lcp) - 1] = '\0';
579                                 lcp++;
580                         }
581                         setpeer0(lcp, NULL);
582                         if (!connected)
583                                 continue;
584                 }
585                 if (argc < 4) {
586                         cp = argc == 3 ? argv[2] : tail(src);
587                         fd = creat(cp, 0644);
588                         if (fd < 0) {
589                                 warn("%s", cp);
590                                 return;
591                         }
592                         if (verbose)
593                                 printf("getting from %s:%s to %s [%s]\n",
594                                     hostname, src, cp, mode);
595                         if (recvfile(peer, port, fd, src, mode) != 0)
596                                 txrx_error = 1;
597                         break;
598                 }
599                 cp = tail(src);         /* new .. jdg */
600                 fd = creat(cp, 0644);
601                 if (fd < 0) {
602                         warn("%s", cp);
603                         continue;
604                 }
605                 if (verbose)
606                         printf("getting from %s:%s to %s [%s]\n",
607                             hostname, src, cp, mode);
608                 if (recvfile(peer, port, fd, src, mode) != 0)
609                         txrx_error = 1;
610         }
611 }
612
613 static void
614 getusage(char *s)
615 {
616
617         printf("usage: %s file [localname]\n", s);
618         printf("       %s [host:]file [localname]\n", s);
619         printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
620 }
621
622 static void
623 settimeoutpacket(int argc, char *argv[])
624 {
625         int t;
626         char    line[MAXLINE];
627
628         if (argc < 2) {
629                 strcpy(line, "Packet timeout ");
630                 printf("(value) ");
631                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
632                 makeargv(line);
633                 argc = margc;
634                 argv = margv;
635         }
636         if (argc != 2) {
637                 printf("usage: %s value\n", argv[0]);
638                 return;
639         }
640         t = atoi(argv[1]);
641         if (t < 0) {
642                 printf("%s: bad value\n", argv[1]);
643                 return;
644         }
645
646         settimeouts(t, timeoutnetwork, maxtimeouts);
647 }
648
649 static void
650 settimeoutnetwork(int argc, char *argv[])
651 {
652         int t;
653         char    line[MAXLINE];
654
655         if (argc < 2) {
656                 strcpy(line, "Network timeout ");
657                 printf("(value) ");
658                 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
659                 makeargv(line);
660                 argc = margc;
661                 argv = margv;
662         }
663         if (argc != 2) {
664                 printf("usage: %s value\n", argv[0]);
665                 return;
666         }
667         t = atoi(argv[1]);
668         if (t < 0) {
669                 printf("%s: bad value\n", argv[1]);
670                 return;
671         }
672
673         settimeouts(timeoutpacket, t, maxtimeouts);
674 }
675
676 static void
677 showstatus(int argc __unused, char *argv[] __unused)
678 {
679
680         printf("Remote host: %s\n",
681             connected ? hostname : "none specified yet");
682         printf("RFC2347 Options support: %s\n",
683             options_rfc_enabled ? "enabled" : "disabled");
684         printf("Non-RFC defined options support: %s\n",
685             options_extra_enabled ? "enabled" : "disabled");
686         printf("Mode: %s\n", mode);
687         printf("Verbose: %s\n", verbose ? "on" : "off");
688         printf("Debug: %s\n", debug_show(debug));
689         printf("Artificial packetloss: %d in 100 packets\n",
690             packetdroppercentage);
691         printf("Segment size: %d bytes\n", segsize);
692         printf("Network timeout: %d seconds\n", timeoutpacket);
693         printf("Maximum network timeout: %d seconds\n", timeoutnetwork);
694         printf("Maximum timeouts: %d \n", maxtimeouts);
695 }
696
697 static void
698 intr(int dummy __unused)
699 {
700
701         signal(SIGALRM, SIG_IGN);
702         alarm(0);
703         longjmp(toplevel, -1);
704 }
705
706 static char *
707 tail(char *filename)
708 {
709         char *s;
710
711         while (*filename) {
712                 s = strrchr(filename, '/');
713                 if (s == NULL)
714                         break;
715                 if (s[1])
716                         return (s + 1);
717                 *s = '\0';
718         }
719         return (filename);
720 }
721
722 static const char *
723 command_prompt(void)
724 {
725
726         return ("tftp> ");
727 }
728
729 /*
730  * Command parser.
731  */
732 static void
733 command(bool interactive, EditLine *el, History *hist, HistEvent *hep)
734 {
735         const struct cmd *c;
736         const char *bp;
737         char *cp;
738         int len, num;
739         char    line[MAXLINE];
740
741         for (;;) {
742                 if (interactive) {
743                         if ((bp = el_gets(el, &num)) == NULL || num == 0)
744                                 exit(0);
745                         len = MIN(MAXLINE, num);
746                         memcpy(line, bp, len);
747                         line[len - 1] = '\0';
748                         history(hist, hep, H_ENTER, bp);
749                 } else {
750                         line[0] = 0;
751                         if (fgets(line, sizeof line , stdin) == NULL) {
752                                 if (feof(stdin)) {
753                                         exit(txrx_error);
754                                 } else {
755                                         continue;
756                                 }
757                         }
758                 }
759                 if ((cp = strchr(line, '\n')))
760                         *cp = '\0';
761                 if (line[0] == 0)
762                         continue;
763                 makeargv(line);
764                 if (margc == 0)
765                         continue;
766                 c = getcmd(margv[0]);
767                 if (c == (struct cmd *)-1) {
768                         printf("?Ambiguous command\n");
769                         continue;
770                 }
771                 if (c == NULL) {
772                         printf("?Invalid command\n");
773                         continue;
774                 }
775                 (*c->handler)(margc, margv);
776         }
777 }
778
779 static const struct cmd *
780 getcmd(const char *name)
781 {
782         const char *p, *q;
783         const struct cmd *c, *found;
784         ptrdiff_t longest;
785         int nmatches;
786
787         longest = 0;
788         nmatches = 0;
789         found = 0;
790         for (c = cmdtab; (p = c->name) != NULL; c++) {
791                 for (q = name; *q == *p++; q++)
792                         if (*q == '\0')         /* exact match? */
793                                 return (c);
794                 if (*q == '\0') {               /* the name was a prefix */
795                         if (q - name > longest) {
796                                 longest = q - name;
797                                 nmatches = 1;
798                                 found = c;
799                         } else if (q - name == longest)
800                                 nmatches++;
801                 }
802         }
803         if (nmatches > 1)
804                 return ((struct cmd *)-1);
805         return (found);
806 }
807
808 /*
809  * Slice a string up into argc/argv.
810  */
811 static void
812 makeargv(char *line)
813 {
814         char *cp;
815         char **argp = margv;
816
817         margc = 0;
818         if ((cp = strchr(line, '\n')) != NULL)
819                 *cp = '\0';
820         for (cp = line; margc < MAX_MARGV - 1 && *cp != '\0';) {
821                 while (isspace(*cp))
822                         cp++;
823                 if (*cp == '\0')
824                         break;
825                 *argp++ = cp;
826                 margc += 1;
827                 while (*cp != '\0' && !isspace(*cp))
828                         cp++;
829                 if (*cp == '\0')
830                         break;
831                 *cp++ = '\0';
832         }
833         *argp++ = 0;
834 }
835
836 static void
837 quit(int argc __unused, char *argv[] __unused)
838 {
839
840         exit(txrx_error);
841 }
842
843 /*
844  * Help command.
845  */
846 static void
847 help(int argc, char *argv[])
848 {
849         const struct cmd *c;
850
851         if (argc == 1) {
852                 printf("Commands may be abbreviated.  Commands are:\n\n");
853                 for (c = cmdtab; c->name; c++)
854                         printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
855
856                 printf("\n[-] : You shouldn't use these ones anymore.\n");
857                 printf("[*] : RFC2347 options support required.\n");
858                 printf("[**] : Non-standard RFC2347 option.\n");
859                 return;
860         }
861         while (--argc > 0) {
862                 char *arg;
863                 arg = *++argv;
864                 c = getcmd(arg);
865                 if (c == (struct cmd *)-1)
866                         printf("?Ambiguous help command: %s\n", arg);
867                 else if (c == (struct cmd *)0)
868                         printf("?Invalid help command: %s\n", arg);
869                 else
870                         printf("%s\n", c->help);
871         }
872 }
873
874 static void
875 setverbose(int argc __unused, char *argv[] __unused)
876 {
877
878         verbose = !verbose;
879         printf("Verbose mode %s.\n", verbose ? "on" : "off");
880 }
881
882 static void
883 setoptions(int argc, char *argv[])
884 {
885
886         if (argc == 2) {
887                 if (strcasecmp(argv[1], "enable") == 0 ||
888                     strcasecmp(argv[1], "on") == 0) {
889                         options_extra_enabled = 1;
890                         options_rfc_enabled = 1;
891                 }
892                 if (strcasecmp(argv[1], "disable") == 0 ||
893                     strcasecmp(argv[1], "off") == 0) {
894                         options_extra_enabled = 0;
895                         options_rfc_enabled = 0;
896                 }
897                 if (strcasecmp(argv[1], "extra") == 0)
898                         options_extra_enabled = !options_extra_enabled;
899         }
900         printf("Support for RFC2347 style options are now %s.\n",
901             options_rfc_enabled ? "enabled" : "disabled");
902         printf("Support for non-RFC defined options are now %s.\n",
903             options_extra_enabled ? "enabled" : "disabled");
904
905         printf("\nThe following options are available:\n"
906             "\toptions on       : enable support for RFC2347 style options\n"
907             "\toptions off      : disable support for RFC2347 style options\n"
908             "\toptions extra    : toggle support for non-RFC defined options\n"
909         );
910 }
911
912 static void
913 setrollover(int argc, char *argv[])
914 {
915
916         if (argc == 2) {
917                 if (strcasecmp(argv[1], "never") == 0 ||
918                     strcasecmp(argv[1], "none") == 0) {
919                         options_set_request(OPT_ROLLOVER, NULL);
920                 }
921                 if (strcasecmp(argv[1], "1") == 0) {
922                         options_set_request(OPT_ROLLOVER, "1");
923                 }
924                 if (strcasecmp(argv[1], "0") == 0) {
925                         options_set_request(OPT_ROLLOVER, "0");
926                 }
927         }
928         printf("Support for the rollover options is %s.\n",
929             options[OPT_ROLLOVER].o_request != NULL ? "enabled" : "disabled");
930         if (options[OPT_ROLLOVER].o_request != NULL)
931                 printf("Block rollover will be to block %s.\n",
932                     options[OPT_ROLLOVER].o_request);
933
934
935         printf("\nThe following rollover options are available:\n"
936             "\trollover 0       : rollover to block zero (default)\n"
937             "\trollover 1       : rollover to block one\n"
938             "\trollover never   : do not support the rollover option\n"
939             "\trollover none    : do not support the rollover option\n"
940         );
941 }
942
943 static void
944 setdebug(int argc, char *argv[])
945 {
946         int i;
947
948         if (argc != 1) {
949                 i = 1;
950                 while (i < argc)
951                         debug ^= debug_find(argv[i++]);
952         }
953         printf("The following debugging is enabled: %s\n", debug_show(debug));
954
955         printf("\nThe following debugs are available:\n");
956         i = 0;
957         while (debugs[i].name != NULL) {
958                 printf("\t%s\t%s\n", debugs[i].name, debugs[i].desc);
959                 i++;
960         }
961 }
962
963 static void
964 setblocksize(int argc, char *argv[])
965 {
966
967         if (!options_rfc_enabled)
968                 printf("RFC2347 style options are not enabled "
969                     "(but proceeding anyway)\n");
970
971         if (argc != 1) {
972                 int size = atoi(argv[1]);
973                 size_t max;
974                 u_long maxdgram;
975
976                 max = sizeof(maxdgram);
977                 if (sysctlbyname("net.inet.udp.maxdgram",
978                         &maxdgram, &max, NULL, 0) < 0) {
979                         perror("sysctl: net.inet.udp.maxdgram");
980                         return;
981                 }
982
983                 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
984                         printf("Blocksize should be between %d and %d bytes.\n",
985                                 BLKSIZE_MIN, BLKSIZE_MAX);
986                         return;
987                 } else if (size > (int)maxdgram - 4) {
988                         printf("Blocksize can't be bigger than %ld bytes due "
989                             "to the net.inet.udp.maxdgram sysctl limitation.\n",
990                             maxdgram - 4);
991                         options_set_request(OPT_BLKSIZE, "%ld", maxdgram - 4);
992                 } else {
993                         options_set_request(OPT_BLKSIZE, "%d", size);
994                 }
995         }
996         printf("Blocksize is now %s bytes.\n", options[OPT_BLKSIZE].o_request);
997 }
998
999 static void
1000 setblocksize2(int argc, char *argv[])
1001 {
1002
1003         if (!options_rfc_enabled || !options_extra_enabled)
1004                 printf(
1005                     "RFC2347 style or non-RFC defined options are not enabled "
1006                     "(but proceeding anyway)\n");
1007
1008         if (argc != 1) {
1009                 int size = atoi(argv[1]);
1010                 int i;
1011                 size_t max;
1012                 u_long maxdgram;
1013
1014                 int sizes[] = {
1015                         8, 16, 32, 64, 128, 256, 512, 1024,
1016                         2048, 4096, 8192, 16384, 32768, 0
1017                 };
1018
1019                 max = sizeof(maxdgram);
1020                 if (sysctlbyname("net.inet.udp.maxdgram",
1021                         &maxdgram, &max, NULL, 0) < 0) {
1022                         perror("sysctl: net.inet.udp.maxdgram");
1023                         return;
1024                 }
1025
1026                 for (i = 0; sizes[i] != 0; i++) {
1027                         if (sizes[i] == size) break;
1028                 }
1029                 if (sizes[i] == 0) {
1030                         printf("Blocksize2 should be a power of two between "
1031                             "8 and 32768.\n");
1032                         return;
1033                 }
1034
1035                 if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
1036                         printf("Blocksize2 should be between "
1037                             "%d and %d bytes.\n", BLKSIZE_MIN, BLKSIZE_MAX);
1038                         return;
1039                 } else if (size > (int)maxdgram - 4) {
1040                         printf("Blocksize2 can't be bigger than %ld bytes due "
1041                             "to the net.inet.udp.maxdgram sysctl limitation.\n",
1042                             maxdgram - 4);
1043                         for (i = 0; sizes[i+1] != 0; i++) {
1044                                 if ((int)maxdgram < sizes[i+1]) break;
1045                         }
1046                         options_set_request(OPT_BLKSIZE2, "%d", sizes[i]);
1047                 } else {
1048                         options_set_request(OPT_BLKSIZE2, "%d", size);
1049                 }
1050         }
1051         printf("Blocksize2 is now %s bytes.\n",
1052             options[OPT_BLKSIZE2].o_request);
1053 }
1054
1055 static void
1056 setpacketdrop(int argc, char *argv[])
1057 {
1058
1059         if (argc != 1)
1060                 packetdroppercentage = atoi(argv[1]);
1061
1062         printf("Randomly %d in 100 packets will be dropped\n",
1063             packetdroppercentage);
1064 }
1065
1066 static void
1067 setwindowsize(int argc, char *argv[])
1068 {
1069
1070         if (!options_rfc_enabled)
1071                 printf("RFC2347 style options are not enabled "
1072                     "(but proceeding anyway)\n");
1073
1074         if (argc != 1) {
1075                 int size = atoi(argv[1]);
1076
1077                 if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) {
1078                         printf("Windowsize should be between %d and %d "
1079                             "blocks.\n", WINDOWSIZE_MIN, WINDOWSIZE_MAX);
1080                         return;
1081                 } else {
1082                         options_set_request(OPT_WINDOWSIZE, "%d", size);
1083                 }
1084         }
1085         printf("Windowsize is now %s blocks.\n",
1086             options[OPT_WINDOWSIZE].o_request);
1087 }