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
main(int argc,char * argv[])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
urihandling(char * URI)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
setpeer0(char * host,const char * lport)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
setpeer(int argc,char * argv[])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
modecmd(int argc,char * argv[])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
setbinary(int argc __unused,char * argv[]__unused)402 setbinary(int argc __unused, char *argv[] __unused)
403 {
404
405 settftpmode("octet");
406 }
407
408 static void
setascii(int argc __unused,char * argv[]__unused)409 setascii(int argc __unused, char *argv[] __unused)
410 {
411
412 settftpmode("netascii");
413 }
414
415 static void
settftpmode(const char * newmode)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
put(int argc,char * argv[])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
putusage(char * s)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
get(int argc,char * argv[])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
getusage(char * s)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
settimeoutpacket(int argc,char * argv[])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
settimeoutnetwork(int argc,char * argv[])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
showstatus(int argc __unused,char * argv[]__unused)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
intr(int dummy __unused)698 intr(int dummy __unused)
699 {
700
701 signal(SIGALRM, SIG_IGN);
702 alarm(0);
703 longjmp(toplevel, -1);
704 }
705
706 static char *
tail(char * filename)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 *
command_prompt(void)723 command_prompt(void)
724 {
725
726 return ("tftp> ");
727 }
728
729 /*
730 * Command parser.
731 */
732 static void
command(bool interactive,EditLine * el,History * hist,HistEvent * hep)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 *
getcmd(const char * name)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
makeargv(char * line)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
quit(int argc __unused,char * argv[]__unused)837 quit(int argc __unused, char *argv[] __unused)
838 {
839
840 exit(txrx_error);
841 }
842
843 /*
844 * Help command.
845 */
846 static void
help(int argc,char * argv[])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
setverbose(int argc __unused,char * argv[]__unused)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
setoptions(int argc,char * argv[])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
setrollover(int argc,char * argv[])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
setdebug(int argc,char * argv[])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
setblocksize(int argc,char * argv[])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
setblocksize2(int argc,char * argv[])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
setpacketdrop(int argc,char * argv[])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
setwindowsize(int argc,char * argv[])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 }
1088