1 /* $OpenBSD: main.c,v 1.45 2022/10/04 08:03:26 kn Exp $ */ 2 /* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * TFTP User Program -- Command Interface 35 * 36 * This version includes many modifications by Jim Guyton <guyton@rand-unix> 37 */ 38 39 #include <sys/socket.h> 40 41 #include <netinet/in.h> 42 #include <arpa/inet.h> 43 #include <arpa/tftp.h> 44 45 #include <ctype.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <netdb.h> 50 #include <poll.h> 51 #include <signal.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include <limits.h> 57 58 #include "extern.h" 59 60 #define LBUFLEN 200 /* size of input buffer */ 61 #define MAXARGV 20 62 #define HELPINDENT (sizeof("connect")) 63 64 void get(int, char **); 65 void help(int, char **); 66 void modecmd(int, char **); 67 void put(int, char **); 68 void quit(int, char **); 69 void setascii(int, char **); 70 void setbinary(int, char **); 71 void setpeer(char *, char *); 72 void parsearg(int, char **); 73 void setrexmt(int, char **); 74 void settimeout(int, char **); 75 void settrace(int, char **); 76 void setverbose(int, char **); 77 void settsize(int, char **); 78 void settout(int, char **); 79 void setblksize(int, char **); 80 void status(int, char **); 81 int readcmd(char *, int, FILE *); 82 static void getusage(char *); 83 static int makeargv(void); 84 static void putusage(char *); 85 static void settftpmode(char *); 86 static __dead void command(void); 87 struct cmd *getcmd(char *); 88 char *tail(char *); 89 90 struct sockaddr_storage peeraddr; 91 int f; 92 int trace; 93 int verbose; 94 int connected; 95 char mode[32]; 96 char line[LBUFLEN]; 97 int margc; 98 char *margv[MAXARGV+1]; 99 char *prompt = "tftp"; 100 void intr(int); 101 int rexmtval = TIMEOUT; 102 int maxtimeout = 5 * TIMEOUT; 103 char hostname[HOST_NAME_MAX+1]; 104 FILE *file = NULL; 105 volatile sig_atomic_t intrflag = 0; 106 char *ackbuf; 107 int has_options = 0; 108 int opt_tsize = 0; 109 int opt_tout = 0; 110 int opt_blksize = 0; 111 112 struct cmd { 113 char *name; 114 char *help; 115 void (*handler)(int, char **); 116 }; 117 118 struct cmd cmdtab[] = { 119 { "?", "print help information", help }, 120 { "ascii", "set mode to netascii", setascii }, 121 { "binary", "set mode to octet", setbinary }, 122 { "blksize", "set alternative blksize option", setblksize }, 123 { "connect", "connect to remote tftp", parsearg }, 124 { "get", "receive file", get }, 125 { "help", "print help information", help }, 126 { "mode", "set file transfer mode", modecmd }, 127 { "put", "send file", put }, 128 { "quit", "exit tftp", quit }, 129 { "rexmt", "set per-packet retransmission timeout", setrexmt }, 130 { "status", "show current status", status }, 131 { "timeout", "set total retransmission timeout", settimeout }, 132 { "tout", "toggle timeout option", settout }, 133 { "trace", "toggle packet tracing", settrace }, 134 { "tsize", "toggle tsize option", settsize }, 135 { "verbose", "toggle verbose mode", setverbose }, 136 { NULL, NULL, NULL } 137 }; 138 139 struct modes { 140 char *m_name; 141 char *m_mode; 142 } modes[] = { 143 { "ascii", "netascii" }, 144 { "netascii", "netascii" }, 145 { "binary", "octet" }, 146 { "image", "octet" }, 147 { "octet", "octet" }, 148 /* { "mail", "mail" }, */ 149 { NULL, NULL } 150 }; 151 152 int 153 main(int argc, char *argv[]) 154 { 155 f = -1; 156 157 if (pledge("stdio rpath wpath cpath dns inet", NULL) == -1) 158 err(1, "pledge"); 159 160 /* set default transfer mode */ 161 strlcpy(mode, "netascii", sizeof(mode)); 162 163 /* set peer if given */ 164 if (argc > 1) 165 parsearg(argc, argv); 166 167 /* catch SIGINT */ 168 signal(SIGINT, intr); 169 170 /* allocate memory for packets */ 171 if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL) 172 err(1, "malloc"); 173 174 /* command prompt */ 175 command(); 176 177 return (0); 178 } 179 180 void 181 setpeer(char *host, char *port) 182 { 183 struct addrinfo hints, *res0, *res; 184 int error; 185 struct sockaddr_storage ss; 186 char *cause = "unknown"; 187 188 if (connected) { 189 close(f); 190 f = -1; 191 } 192 connected = 0; 193 194 memset(&hints, 0, sizeof(hints)); 195 hints.ai_family = PF_UNSPEC; 196 hints.ai_socktype = SOCK_DGRAM; 197 hints.ai_protocol = IPPROTO_UDP; 198 hints.ai_flags = AI_CANONNAME; 199 if (!port) 200 port = "tftp"; 201 error = getaddrinfo(host, port, &hints, &res0); 202 if (error) { 203 warnx("%s", gai_strerror(error)); 204 return; 205 } 206 207 for (res = res0; res; res = res->ai_next) { 208 if (res->ai_addrlen > sizeof(peeraddr)) 209 continue; 210 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 211 if (f < 0) { 212 cause = "socket"; 213 continue; 214 } 215 216 memset(&ss, 0, sizeof(ss)); 217 ss.ss_family = res->ai_family; 218 if (bind(f, (struct sockaddr *)&ss, res->ai_addrlen) < 0) { 219 cause = "bind"; 220 close(f); 221 f = -1; 222 continue; 223 } 224 225 break; 226 } 227 228 if (f < 0) 229 warn("%s", cause); 230 else { 231 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 232 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 233 if (res->ai_canonname) { 234 (void)strlcpy(hostname, res->ai_canonname, 235 sizeof(hostname)); 236 } else 237 (void)strlcpy(hostname, host, sizeof(hostname)); 238 connected = 1; 239 } 240 freeaddrinfo(res0); 241 } 242 243 void 244 parsearg(int argc, char *argv[]) 245 { 246 if (argc < 2) { 247 strlcpy(line, "Connect ", sizeof(line)); 248 printf("(to) "); 249 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 250 if (makeargv()) 251 return; 252 argc = margc; 253 argv = margv; 254 } 255 if ((argc < 2) || (argc > 3)) { 256 printf("usage: %s [host [port]]\n", argv[0]); 257 return; 258 } 259 if (argc == 2) 260 setpeer(argv[1], NULL); 261 else 262 setpeer(argv[1], argv[2]); 263 } 264 265 void 266 modecmd(int argc, char *argv[]) 267 { 268 struct modes *p; 269 char *sep; 270 271 if (argc < 2) { 272 printf("Using %s mode to transfer files.\n", mode); 273 return; 274 } 275 if (argc == 2) { 276 for (p = modes; p->m_name != NULL; p++) 277 if (strcmp(argv[1], p->m_name) == 0) 278 break; 279 if (p->m_name) { 280 settftpmode(p->m_mode); 281 return; 282 } 283 printf("%s: unknown mode\n", argv[1]); 284 /* drop through and print usage message */ 285 } 286 287 printf("usage: %s [", argv[0]); 288 sep = " "; 289 for (p = modes; p->m_name != NULL; p++) { 290 printf("%s%s", sep, p->m_name); 291 if (*sep == ' ') 292 sep = " | "; 293 } 294 printf(" ]\n"); 295 296 return; 297 } 298 299 /* ARGSUSED */ 300 void 301 setbinary(int argc, char *argv[]) 302 { 303 settftpmode("octet"); 304 } 305 306 /* ARGSUSED */ 307 void 308 setascii(int argc, char *argv[]) 309 { 310 settftpmode("netascii"); 311 } 312 313 static void 314 settftpmode(char *newmode) 315 { 316 strlcpy(mode, newmode, sizeof(mode)); 317 if (verbose) 318 printf("mode set to %s\n", mode); 319 } 320 321 /* 322 * Send file(s). 323 */ 324 void 325 put(int argc, char *argv[]) 326 { 327 int fd; 328 int n; 329 char *cp, *targ; 330 331 if (argc < 2) { 332 strlcpy(line, "put ", sizeof(line)); 333 printf("(file) "); 334 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 335 if (makeargv()) 336 return; 337 argc = margc; 338 argv = margv; 339 } 340 if (argc < 2) { 341 putusage(argv[0]); 342 return; 343 } 344 targ = argv[argc - 1]; 345 if (strrchr(argv[argc - 1], ':')) { 346 347 for (n = 1; n < argc - 1; n++) 348 if (strchr(argv[n], ':')) { 349 putusage(argv[0]); 350 return; 351 } 352 cp = argv[argc - 1]; 353 targ = strrchr(cp, ':'); 354 *targ++ = 0; 355 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 356 cp[strlen(cp) - 1] = '\0'; 357 cp++; 358 } 359 setpeer(cp, NULL); 360 } 361 if (!connected) { 362 printf("No target machine specified.\n"); 363 return; 364 } 365 if (argc < 4) { 366 cp = argc == 2 ? tail(targ) : argv[1]; 367 fd = open(cp, O_RDONLY); 368 if (fd < 0) { 369 warn("open: %s", cp); 370 return; 371 } 372 if (verbose) 373 printf("putting %s to %s:%s [%s]\n", 374 cp, hostname, targ, mode); 375 sendfile(fd, targ, mode); 376 return; 377 } 378 379 /* 380 * this assumes the target is a directory on 381 * on a remote unix system. hmmmm. 382 */ 383 for (n = 1; n < argc - 1; n++) { 384 if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1) 385 err(1, "asprintf"); 386 fd = open(argv[n], O_RDONLY); 387 if (fd < 0) { 388 warn("open: %s", argv[n]); 389 free(cp); 390 continue; 391 } 392 if (verbose) 393 printf("putting %s to %s:%s [%s]\n", 394 argv[n], hostname, cp, mode); 395 sendfile(fd, cp, mode); 396 free(cp); 397 } 398 } 399 400 static void 401 putusage(char *s) 402 { 403 printf("usage: %s file [[host:]remotename]\n", s); 404 printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", 405 s); 406 } 407 408 /* 409 * Receive file(s). 410 */ 411 void 412 get(int argc, char *argv[]) 413 { 414 int fd; 415 int n; 416 char *cp; 417 char *src; 418 419 if (argc < 2) { 420 strlcpy(line, "get ", sizeof(line)); 421 printf("(files) "); 422 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 423 if (makeargv()) 424 return; 425 argc = margc; 426 argv = margv; 427 } 428 if (argc < 2) { 429 getusage(argv[0]); 430 return; 431 } 432 if (!connected) { 433 for (n = 1; n < argc; n++) 434 if (strrchr(argv[n], ':') == 0) { 435 getusage(argv[0]); 436 return; 437 } 438 } 439 for (n = 1; n < argc; n++) { 440 src = strrchr(argv[n], ':'); 441 if (src == NULL) 442 src = argv[n]; 443 else { 444 char *cp; 445 446 *src++ = 0; 447 cp = argv[n]; 448 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 449 cp[strlen(cp) - 1] = '\0'; 450 cp++; 451 } 452 setpeer(cp, NULL); 453 if (!connected) 454 continue; 455 } 456 if (argc < 4) { 457 cp = argc == 3 ? argv[2] : tail(src); 458 fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644); 459 if (fd < 0) { 460 warn("create: %s", cp); 461 return; 462 } 463 if (verbose) 464 printf("getting from %s:%s to %s [%s]\n", 465 hostname, src, cp, mode); 466 recvfile(fd, src, mode); 467 break; 468 } 469 cp = tail(src); /* new .. jdg */ 470 fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644); 471 if (fd < 0) { 472 warn("create: %s", cp); 473 continue; 474 } 475 if (verbose) 476 printf("getting from %s:%s to %s [%s]\n", 477 hostname, src, cp, mode); 478 recvfile(fd, src, mode); 479 } 480 } 481 482 static void 483 getusage(char *s) 484 { 485 printf("usage: %s [host:]file [localname]\n", s); 486 printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 487 } 488 489 void 490 setrexmt(int argc, char *argv[]) 491 { 492 int t; 493 const char *errstr; 494 495 if (argc < 2) { 496 strlcpy(line, "Rexmt-timeout ", sizeof(line)); 497 printf("(value) "); 498 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 499 if (makeargv()) 500 return; 501 argc = margc; 502 argv = margv; 503 } 504 if (argc != 2) { 505 printf("usage: %s value\n", argv[0]); 506 return; 507 } 508 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 509 if (errstr) 510 printf("%s: value is %s\n", argv[1], errstr); 511 else 512 rexmtval = t; 513 } 514 515 void 516 settimeout(int argc, char *argv[]) 517 { 518 int t; 519 const char *errstr; 520 521 if (argc < 2) { 522 strlcpy(line, "Maximum-timeout ", sizeof(line)); 523 printf("(value) "); 524 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 525 if (makeargv()) 526 return; 527 argc = margc; 528 argv = margv; 529 } 530 if (argc != 2) { 531 printf("usage: %s value\n", argv[0]); 532 return; 533 } 534 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 535 if (errstr) 536 printf("%s: value is %s\n", argv[1], errstr); 537 else 538 maxtimeout = t; 539 } 540 541 /* ARGSUSED */ 542 void 543 status(int argc, char *argv[]) 544 { 545 if (connected) 546 printf("Connected to %s.\n", hostname); 547 else 548 printf("Not connected.\n"); 549 printf("Mode: %s Verbose: %s Tracing: %s\n", 550 mode, verbose ? "on" : "off", trace ? "on" : "off"); 551 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 552 rexmtval, maxtimeout); 553 } 554 555 /* ARGSUSED */ 556 void 557 intr(int signo) 558 { 559 intrflag = 1; 560 } 561 562 char * 563 tail(char *filename) 564 { 565 char *s; 566 567 while (*filename) { 568 s = strrchr(filename, '/'); 569 if (s == NULL) 570 break; 571 if (s[1]) 572 return (s + 1); 573 *s = '\0'; 574 } 575 576 return (filename); 577 } 578 579 /* 580 * Command parser. 581 */ 582 static __dead void 583 command(void) 584 { 585 struct cmd *c; 586 587 for (;;) { 588 if (isatty(STDIN_FILENO)) 589 printf("%s> ", prompt); 590 if (readcmd(line, LBUFLEN, stdin) < 1) 591 continue; 592 if ((line[0] == 0) || (line[0] == '\n')) 593 continue; 594 if (makeargv()) 595 continue; 596 if (margc == 0) 597 continue; 598 c = getcmd(margv[0]); 599 if (c == (struct cmd *) - 1) { 600 printf("?Ambiguous command\n"); 601 continue; 602 } 603 if (c == 0) { 604 printf("?Invalid command\n"); 605 continue; 606 } 607 (*c->handler)(margc, margv); 608 } 609 } 610 611 struct cmd * 612 getcmd(char *name) 613 { 614 char *p, *q; 615 struct cmd *c, *found; 616 int nmatches, longest; 617 618 longest = 0; 619 nmatches = 0; 620 found = 0; 621 intrflag = 0; 622 for (c = cmdtab; (p = c->name) != NULL; c++) { 623 for (q = name; *q == *p++; q++) 624 if (*q == 0) /* exact match? */ 625 return (c); 626 if (!*q) { /* the name was a prefix */ 627 if (q - name > longest) { 628 longest = q - name; 629 nmatches = 1; 630 found = c; 631 } else if (q - name == longest) 632 nmatches++; 633 } 634 } 635 if (nmatches > 1) 636 return ((struct cmd *) - 1); 637 638 return (found); 639 } 640 641 /* 642 * Slice a string up into argc/argv. 643 */ 644 static int 645 makeargv(void) 646 { 647 char *cp; 648 char **argp = margv; 649 int ret = 0; 650 651 margc = 0; 652 for (cp = line; *cp;) { 653 if (margc >= MAXARGV) { 654 printf("too many arguments\n"); 655 ret = 1; 656 break; 657 } 658 while (isspace((unsigned char)*cp)) 659 cp++; 660 if (*cp == '\0') 661 break; 662 *argp++ = cp; 663 margc += 1; 664 while (*cp != '\0' && !isspace((unsigned char)*cp)) 665 cp++; 666 if (*cp == '\0') 667 break; 668 *cp++ = '\0'; 669 } 670 *argp++ = 0; 671 672 return (ret); 673 } 674 675 /* ARGSUSED */ 676 void 677 quit(int argc, char *argv[]) 678 { 679 exit(0); 680 } 681 682 /* 683 * Help command. 684 */ 685 void 686 help(int argc, char *argv[]) 687 { 688 struct cmd *c; 689 690 if (argc == 1) { 691 printf("Commands may be abbreviated. Commands are:\n\n"); 692 for (c = cmdtab; c->name != NULL; c++) 693 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 694 return; 695 } 696 while (--argc > 0) { 697 char *arg; 698 arg = *++argv; 699 c = getcmd(arg); 700 if (c == (struct cmd *) - 1) 701 printf("?Ambiguous help command %s\n", arg); 702 else if (c == NULL) 703 printf("?Invalid help command %s\n", arg); 704 else 705 printf("%s\n", c->help); 706 } 707 } 708 709 /* ARGSUSED */ 710 void 711 settrace(int argc, char *argv[]) 712 { 713 trace = !trace; 714 printf("Packet tracing %s.\n", trace ? "on" : "off"); 715 } 716 717 /* ARGSUSED */ 718 void 719 setverbose(int argc, char *argv[]) 720 { 721 verbose = !verbose; 722 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 723 } 724 725 /* ARGSUSED */ 726 void 727 settsize(int argc, char *argv[]) 728 { 729 opt_tsize = !opt_tsize; 730 printf("Tsize option %s.\n", opt_tsize ? "on" : "off"); 731 if (opt_tsize) 732 has_options++; 733 else 734 has_options--; 735 } 736 737 /* ARGSUSED */ 738 void 739 settout(int argc, char *argv[]) 740 { 741 opt_tout = !opt_tout; 742 printf("Timeout option %s.\n", opt_tout ? "on" : "off"); 743 if (opt_tout) 744 has_options++; 745 else 746 has_options--; 747 } 748 749 void 750 setblksize(int argc, char *argv[]) 751 { 752 int t; 753 const char *errstr; 754 755 if (argc < 2) { 756 strlcpy(line, "Blocksize ", sizeof(line)); 757 printf("(value) "); 758 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 759 if (makeargv()) 760 return; 761 argc = margc; 762 argv = margv; 763 } 764 if (argc != 2) { 765 printf("usage: %s value\n", argv[0]); 766 return; 767 } 768 t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr); 769 if (errstr) 770 printf("%s: value is %s\n", argv[1], errstr); 771 else { 772 if (opt_blksize == 0) 773 has_options++; 774 opt_blksize = t; 775 } 776 } 777 778 int 779 readcmd(char *input, int len, FILE *stream) 780 { 781 int nfds; 782 struct pollfd pfd[1]; 783 784 fflush(stdout); 785 786 pfd[0].fd = 0; 787 pfd[0].events = POLLIN; 788 nfds = poll(pfd, 1, INFTIM); 789 if (nfds == -1) { 790 if (intrflag) { 791 intrflag = 0; 792 putchar('\n'); 793 return (0); 794 } 795 exit(1); 796 } 797 798 if (fgets(input, len, stream) == NULL) { 799 if (feof(stdin)) 800 exit(0); 801 else 802 return (-1); 803 } 804 805 return (1); 806 } 807