1 /* $OpenBSD: main.c,v 1.46 2023/03/08 04:43:12 guenther 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 void 300 setbinary(int argc, char *argv[]) 301 { 302 settftpmode("octet"); 303 } 304 305 void 306 setascii(int argc, char *argv[]) 307 { 308 settftpmode("netascii"); 309 } 310 311 static void 312 settftpmode(char *newmode) 313 { 314 strlcpy(mode, newmode, sizeof(mode)); 315 if (verbose) 316 printf("mode set to %s\n", mode); 317 } 318 319 /* 320 * Send file(s). 321 */ 322 void 323 put(int argc, char *argv[]) 324 { 325 int fd; 326 int n; 327 char *cp, *targ; 328 329 if (argc < 2) { 330 strlcpy(line, "put ", sizeof(line)); 331 printf("(file) "); 332 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 333 if (makeargv()) 334 return; 335 argc = margc; 336 argv = margv; 337 } 338 if (argc < 2) { 339 putusage(argv[0]); 340 return; 341 } 342 targ = argv[argc - 1]; 343 if (strrchr(argv[argc - 1], ':')) { 344 345 for (n = 1; n < argc - 1; n++) 346 if (strchr(argv[n], ':')) { 347 putusage(argv[0]); 348 return; 349 } 350 cp = argv[argc - 1]; 351 targ = strrchr(cp, ':'); 352 *targ++ = 0; 353 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 354 cp[strlen(cp) - 1] = '\0'; 355 cp++; 356 } 357 setpeer(cp, NULL); 358 } 359 if (!connected) { 360 printf("No target machine specified.\n"); 361 return; 362 } 363 if (argc < 4) { 364 cp = argc == 2 ? tail(targ) : argv[1]; 365 fd = open(cp, O_RDONLY); 366 if (fd < 0) { 367 warn("open: %s", cp); 368 return; 369 } 370 if (verbose) 371 printf("putting %s to %s:%s [%s]\n", 372 cp, hostname, targ, mode); 373 sendfile(fd, targ, mode); 374 return; 375 } 376 377 /* 378 * this assumes the target is a directory on 379 * on a remote unix system. hmmmm. 380 */ 381 for (n = 1; n < argc - 1; n++) { 382 if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1) 383 err(1, "asprintf"); 384 fd = open(argv[n], O_RDONLY); 385 if (fd < 0) { 386 warn("open: %s", argv[n]); 387 free(cp); 388 continue; 389 } 390 if (verbose) 391 printf("putting %s to %s:%s [%s]\n", 392 argv[n], hostname, cp, mode); 393 sendfile(fd, cp, mode); 394 free(cp); 395 } 396 } 397 398 static void 399 putusage(char *s) 400 { 401 printf("usage: %s file [[host:]remotename]\n", s); 402 printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", 403 s); 404 } 405 406 /* 407 * Receive file(s). 408 */ 409 void 410 get(int argc, char *argv[]) 411 { 412 int fd; 413 int n; 414 char *cp; 415 char *src; 416 417 if (argc < 2) { 418 strlcpy(line, "get ", sizeof(line)); 419 printf("(files) "); 420 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 421 if (makeargv()) 422 return; 423 argc = margc; 424 argv = margv; 425 } 426 if (argc < 2) { 427 getusage(argv[0]); 428 return; 429 } 430 if (!connected) { 431 for (n = 1; n < argc; n++) 432 if (strrchr(argv[n], ':') == 0) { 433 getusage(argv[0]); 434 return; 435 } 436 } 437 for (n = 1; n < argc; n++) { 438 src = strrchr(argv[n], ':'); 439 if (src == NULL) 440 src = argv[n]; 441 else { 442 char *cp; 443 444 *src++ = 0; 445 cp = argv[n]; 446 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 447 cp[strlen(cp) - 1] = '\0'; 448 cp++; 449 } 450 setpeer(cp, NULL); 451 if (!connected) 452 continue; 453 } 454 if (argc < 4) { 455 cp = argc == 3 ? argv[2] : tail(src); 456 fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644); 457 if (fd < 0) { 458 warn("create: %s", cp); 459 return; 460 } 461 if (verbose) 462 printf("getting from %s:%s to %s [%s]\n", 463 hostname, src, cp, mode); 464 recvfile(fd, src, mode); 465 break; 466 } 467 cp = tail(src); /* new .. jdg */ 468 fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644); 469 if (fd < 0) { 470 warn("create: %s", cp); 471 continue; 472 } 473 if (verbose) 474 printf("getting from %s:%s to %s [%s]\n", 475 hostname, src, cp, mode); 476 recvfile(fd, src, mode); 477 } 478 } 479 480 static void 481 getusage(char *s) 482 { 483 printf("usage: %s [host:]file [localname]\n", s); 484 printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 485 } 486 487 void 488 setrexmt(int argc, char *argv[]) 489 { 490 int t; 491 const char *errstr; 492 493 if (argc < 2) { 494 strlcpy(line, "Rexmt-timeout ", sizeof(line)); 495 printf("(value) "); 496 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 497 if (makeargv()) 498 return; 499 argc = margc; 500 argv = margv; 501 } 502 if (argc != 2) { 503 printf("usage: %s value\n", argv[0]); 504 return; 505 } 506 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 507 if (errstr) 508 printf("%s: value is %s\n", argv[1], errstr); 509 else 510 rexmtval = t; 511 } 512 513 void 514 settimeout(int argc, char *argv[]) 515 { 516 int t; 517 const char *errstr; 518 519 if (argc < 2) { 520 strlcpy(line, "Maximum-timeout ", sizeof(line)); 521 printf("(value) "); 522 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 523 if (makeargv()) 524 return; 525 argc = margc; 526 argv = margv; 527 } 528 if (argc != 2) { 529 printf("usage: %s value\n", argv[0]); 530 return; 531 } 532 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 533 if (errstr) 534 printf("%s: value is %s\n", argv[1], errstr); 535 else 536 maxtimeout = t; 537 } 538 539 void 540 status(int argc, char *argv[]) 541 { 542 if (connected) 543 printf("Connected to %s.\n", hostname); 544 else 545 printf("Not connected.\n"); 546 printf("Mode: %s Verbose: %s Tracing: %s\n", 547 mode, verbose ? "on" : "off", trace ? "on" : "off"); 548 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 549 rexmtval, maxtimeout); 550 } 551 552 void 553 intr(int signo) 554 { 555 intrflag = 1; 556 } 557 558 char * 559 tail(char *filename) 560 { 561 char *s; 562 563 while (*filename) { 564 s = strrchr(filename, '/'); 565 if (s == NULL) 566 break; 567 if (s[1]) 568 return (s + 1); 569 *s = '\0'; 570 } 571 572 return (filename); 573 } 574 575 /* 576 * Command parser. 577 */ 578 static __dead void 579 command(void) 580 { 581 struct cmd *c; 582 583 for (;;) { 584 if (isatty(STDIN_FILENO)) 585 printf("%s> ", prompt); 586 if (readcmd(line, LBUFLEN, stdin) < 1) 587 continue; 588 if ((line[0] == 0) || (line[0] == '\n')) 589 continue; 590 if (makeargv()) 591 continue; 592 if (margc == 0) 593 continue; 594 c = getcmd(margv[0]); 595 if (c == (struct cmd *) - 1) { 596 printf("?Ambiguous command\n"); 597 continue; 598 } 599 if (c == 0) { 600 printf("?Invalid command\n"); 601 continue; 602 } 603 (*c->handler)(margc, margv); 604 } 605 } 606 607 struct cmd * 608 getcmd(char *name) 609 { 610 char *p, *q; 611 struct cmd *c, *found; 612 int nmatches, longest; 613 614 longest = 0; 615 nmatches = 0; 616 found = 0; 617 intrflag = 0; 618 for (c = cmdtab; (p = c->name) != NULL; c++) { 619 for (q = name; *q == *p++; q++) 620 if (*q == 0) /* exact match? */ 621 return (c); 622 if (!*q) { /* the name was a prefix */ 623 if (q - name > longest) { 624 longest = q - name; 625 nmatches = 1; 626 found = c; 627 } else if (q - name == longest) 628 nmatches++; 629 } 630 } 631 if (nmatches > 1) 632 return ((struct cmd *) - 1); 633 634 return (found); 635 } 636 637 /* 638 * Slice a string up into argc/argv. 639 */ 640 static int 641 makeargv(void) 642 { 643 char *cp; 644 char **argp = margv; 645 int ret = 0; 646 647 margc = 0; 648 for (cp = line; *cp;) { 649 if (margc >= MAXARGV) { 650 printf("too many arguments\n"); 651 ret = 1; 652 break; 653 } 654 while (isspace((unsigned char)*cp)) 655 cp++; 656 if (*cp == '\0') 657 break; 658 *argp++ = cp; 659 margc += 1; 660 while (*cp != '\0' && !isspace((unsigned char)*cp)) 661 cp++; 662 if (*cp == '\0') 663 break; 664 *cp++ = '\0'; 665 } 666 *argp++ = 0; 667 668 return (ret); 669 } 670 671 void 672 quit(int argc, char *argv[]) 673 { 674 exit(0); 675 } 676 677 /* 678 * Help command. 679 */ 680 void 681 help(int argc, char *argv[]) 682 { 683 struct cmd *c; 684 685 if (argc == 1) { 686 printf("Commands may be abbreviated. Commands are:\n\n"); 687 for (c = cmdtab; c->name != NULL; c++) 688 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 689 return; 690 } 691 while (--argc > 0) { 692 char *arg; 693 arg = *++argv; 694 c = getcmd(arg); 695 if (c == (struct cmd *) - 1) 696 printf("?Ambiguous help command %s\n", arg); 697 else if (c == NULL) 698 printf("?Invalid help command %s\n", arg); 699 else 700 printf("%s\n", c->help); 701 } 702 } 703 704 void 705 settrace(int argc, char *argv[]) 706 { 707 trace = !trace; 708 printf("Packet tracing %s.\n", trace ? "on" : "off"); 709 } 710 711 void 712 setverbose(int argc, char *argv[]) 713 { 714 verbose = !verbose; 715 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 716 } 717 718 void 719 settsize(int argc, char *argv[]) 720 { 721 opt_tsize = !opt_tsize; 722 printf("Tsize option %s.\n", opt_tsize ? "on" : "off"); 723 if (opt_tsize) 724 has_options++; 725 else 726 has_options--; 727 } 728 729 void 730 settout(int argc, char *argv[]) 731 { 732 opt_tout = !opt_tout; 733 printf("Timeout option %s.\n", opt_tout ? "on" : "off"); 734 if (opt_tout) 735 has_options++; 736 else 737 has_options--; 738 } 739 740 void 741 setblksize(int argc, char *argv[]) 742 { 743 int t; 744 const char *errstr; 745 746 if (argc < 2) { 747 strlcpy(line, "Blocksize ", sizeof(line)); 748 printf("(value) "); 749 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 750 if (makeargv()) 751 return; 752 argc = margc; 753 argv = margv; 754 } 755 if (argc != 2) { 756 printf("usage: %s value\n", argv[0]); 757 return; 758 } 759 t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr); 760 if (errstr) 761 printf("%s: value is %s\n", argv[1], errstr); 762 else { 763 if (opt_blksize == 0) 764 has_options++; 765 opt_blksize = t; 766 } 767 } 768 769 int 770 readcmd(char *input, int len, FILE *stream) 771 { 772 int nfds; 773 struct pollfd pfd[1]; 774 775 fflush(stdout); 776 777 pfd[0].fd = 0; 778 pfd[0].events = POLLIN; 779 nfds = poll(pfd, 1, INFTIM); 780 if (nfds == -1) { 781 if (intrflag) { 782 intrflag = 0; 783 putchar('\n'); 784 return (0); 785 } 786 exit(1); 787 } 788 789 if (fgets(input, len, stream) == NULL) { 790 if (feof(stdin)) 791 exit(0); 792 else 793 return (-1); 794 } 795 796 return (1); 797 } 798