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 } 1088