1 /* $OpenBSD: main.c,v 1.43 2018/09/20 11:42:42 jsg 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 char vhelp[] = "toggle verbose mode"; 113 char thelp[] = "toggle packet tracing"; 114 char chelp[] = "connect to remote tftp"; 115 char qhelp[] = "exit tftp"; 116 char hhelp[] = "print help information"; 117 char shelp[] = "send file"; 118 char rhelp[] = "receive file"; 119 char mhelp[] = "set file transfer mode"; 120 char sthelp[] = "show current status"; 121 char xhelp[] = "set per-packet retransmission timeout"; 122 char ihelp[] = "set total retransmission timeout"; 123 char ashelp[] = "set mode to netascii"; 124 char bnhelp[] = "set mode to octet"; 125 char oshelp[] = "toggle tsize option"; 126 char othelp[] = "toggle timeout option"; 127 char obhelp[] = "set alternative blksize option"; 128 129 struct cmd { 130 char *name; 131 char *help; 132 void (*handler)(int, char **); 133 }; 134 135 struct cmd cmdtab[] = { 136 { "connect", chelp, parsearg }, 137 { "mode", mhelp, modecmd }, 138 { "put", shelp, put }, 139 { "get", rhelp, get }, 140 { "quit", qhelp, quit }, 141 { "verbose", vhelp, setverbose }, 142 { "trace", thelp, settrace }, 143 { "status", sthelp, status }, 144 { "binary", bnhelp, setbinary }, 145 { "ascii", ashelp, setascii }, 146 { "rexmt", xhelp, setrexmt }, 147 { "timeout", ihelp, settimeout }, 148 { "tsize", oshelp, settsize }, 149 { "tout", othelp, settout }, 150 { "blksize", obhelp, setblksize }, 151 { "help", hhelp, help }, 152 { "?", hhelp, help }, 153 { NULL, NULL, NULL } 154 }; 155 156 struct modes { 157 char *m_name; 158 char *m_mode; 159 } modes[] = { 160 { "ascii", "netascii" }, 161 { "netascii", "netascii" }, 162 { "binary", "octet" }, 163 { "image", "octet" }, 164 { "octet", "octet" }, 165 /* { "mail", "mail" }, */ 166 { NULL, NULL } 167 }; 168 169 int 170 main(int argc, char *argv[]) 171 { 172 f = -1; 173 174 if (pledge("stdio rpath wpath cpath dns inet", NULL) == -1) 175 err(1, "pledge"); 176 177 /* set default transfer mode */ 178 strlcpy(mode, "netascii", sizeof(mode)); 179 180 /* set peer if given */ 181 if (argc > 1) 182 parsearg(argc, argv); 183 184 /* catch SIGINT */ 185 signal(SIGINT, intr); 186 187 /* allocate memory for packets */ 188 if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL) 189 err(1, "malloc"); 190 191 /* command prompt */ 192 command(); 193 194 return (0); 195 } 196 197 void 198 setpeer(char *host, char *port) 199 { 200 struct addrinfo hints, *res0, *res; 201 int error; 202 struct sockaddr_storage ss; 203 char *cause = "unknown"; 204 205 if (connected) { 206 close(f); 207 f = -1; 208 } 209 connected = 0; 210 211 memset(&hints, 0, sizeof(hints)); 212 hints.ai_family = PF_UNSPEC; 213 hints.ai_socktype = SOCK_DGRAM; 214 hints.ai_protocol = IPPROTO_UDP; 215 hints.ai_flags = AI_CANONNAME; 216 if (!port) 217 port = "tftp"; 218 error = getaddrinfo(host, port, &hints, &res0); 219 if (error) { 220 warnx("%s", gai_strerror(error)); 221 return; 222 } 223 224 for (res = res0; res; res = res->ai_next) { 225 if (res->ai_addrlen > sizeof(peeraddr)) 226 continue; 227 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 228 if (f < 0) { 229 cause = "socket"; 230 continue; 231 } 232 233 memset(&ss, 0, sizeof(ss)); 234 ss.ss_family = res->ai_family; 235 if (bind(f, (struct sockaddr *)&ss, res->ai_addrlen) < 0) { 236 cause = "bind"; 237 close(f); 238 f = -1; 239 continue; 240 } 241 242 break; 243 } 244 245 if (f < 0) 246 warn("%s", cause); 247 else { 248 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 249 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 250 if (res->ai_canonname) { 251 (void)strlcpy(hostname, res->ai_canonname, 252 sizeof(hostname)); 253 } else 254 (void)strlcpy(hostname, host, sizeof(hostname)); 255 connected = 1; 256 } 257 freeaddrinfo(res0); 258 } 259 260 void 261 parsearg(int argc, char *argv[]) 262 { 263 if (argc < 2) { 264 strlcpy(line, "Connect ", sizeof(line)); 265 printf("(to) "); 266 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 267 if (makeargv()) 268 return; 269 argc = margc; 270 argv = margv; 271 } 272 if ((argc < 2) || (argc > 3)) { 273 printf("usage: %s [host [port]]\n", argv[0]); 274 return; 275 } 276 if (argc == 2) 277 setpeer(argv[1], NULL); 278 else 279 setpeer(argv[1], argv[2]); 280 } 281 282 void 283 modecmd(int argc, char *argv[]) 284 { 285 struct modes *p; 286 char *sep; 287 288 if (argc < 2) { 289 printf("Using %s mode to transfer files.\n", mode); 290 return; 291 } 292 if (argc == 2) { 293 for (p = modes; p->m_name != NULL; p++) 294 if (strcmp(argv[1], p->m_name) == 0) 295 break; 296 if (p->m_name) { 297 settftpmode(p->m_mode); 298 return; 299 } 300 printf("%s: unknown mode\n", argv[1]); 301 /* drop through and print usage message */ 302 } 303 304 printf("usage: %s [", argv[0]); 305 sep = " "; 306 for (p = modes; p->m_name != NULL; p++) { 307 printf("%s%s", sep, p->m_name); 308 if (*sep == ' ') 309 sep = " | "; 310 } 311 printf(" ]\n"); 312 313 return; 314 } 315 316 /* ARGSUSED */ 317 void 318 setbinary(int argc, char *argv[]) 319 { 320 settftpmode("octet"); 321 } 322 323 /* ARGSUSED */ 324 void 325 setascii(int argc, char *argv[]) 326 { 327 settftpmode("netascii"); 328 } 329 330 static void 331 settftpmode(char *newmode) 332 { 333 strlcpy(mode, newmode, sizeof(mode)); 334 if (verbose) 335 printf("mode set to %s\n", mode); 336 } 337 338 /* 339 * Send file(s). 340 */ 341 void 342 put(int argc, char *argv[]) 343 { 344 int fd; 345 int n; 346 char *cp, *targ; 347 348 if (argc < 2) { 349 strlcpy(line, "put ", sizeof(line)); 350 printf("(file) "); 351 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 352 if (makeargv()) 353 return; 354 argc = margc; 355 argv = margv; 356 } 357 if (argc < 2) { 358 putusage(argv[0]); 359 return; 360 } 361 targ = argv[argc - 1]; 362 if (strrchr(argv[argc - 1], ':')) { 363 364 for (n = 1; n < argc - 1; n++) 365 if (strchr(argv[n], ':')) { 366 putusage(argv[0]); 367 return; 368 } 369 cp = argv[argc - 1]; 370 targ = strrchr(cp, ':'); 371 *targ++ = 0; 372 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 373 cp[strlen(cp) - 1] = '\0'; 374 cp++; 375 } 376 setpeer(cp, NULL); 377 } 378 if (!connected) { 379 printf("No target machine specified.\n"); 380 return; 381 } 382 if (argc < 4) { 383 cp = argc == 2 ? tail(targ) : argv[1]; 384 fd = open(cp, O_RDONLY); 385 if (fd < 0) { 386 warn("open: %s", cp); 387 return; 388 } 389 if (verbose) 390 printf("putting %s to %s:%s [%s]\n", 391 cp, hostname, targ, mode); 392 sendfile(fd, targ, mode); 393 return; 394 } 395 396 /* 397 * this assumes the target is a directory on 398 * on a remote unix system. hmmmm. 399 */ 400 for (n = 1; n < argc - 1; n++) { 401 if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1) 402 err(1, "asprintf"); 403 fd = open(argv[n], O_RDONLY); 404 if (fd < 0) { 405 warn("open: %s", argv[n]); 406 free(cp); 407 continue; 408 } 409 if (verbose) 410 printf("putting %s to %s:%s [%s]\n", 411 argv[n], hostname, cp, mode); 412 sendfile(fd, cp, mode); 413 free(cp); 414 } 415 } 416 417 static void 418 putusage(char *s) 419 { 420 printf("usage: %s file [[host:]remotename]\n", s); 421 printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", 422 s); 423 } 424 425 /* 426 * Receive file(s). 427 */ 428 void 429 get(int argc, char *argv[]) 430 { 431 int fd; 432 int n; 433 char *cp; 434 char *src; 435 436 if (argc < 2) { 437 strlcpy(line, "get ", sizeof(line)); 438 printf("(files) "); 439 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 440 if (makeargv()) 441 return; 442 argc = margc; 443 argv = margv; 444 } 445 if (argc < 2) { 446 getusage(argv[0]); 447 return; 448 } 449 if (!connected) { 450 for (n = 1; n < argc; n++) 451 if (strrchr(argv[n], ':') == 0) { 452 getusage(argv[0]); 453 return; 454 } 455 } 456 for (n = 1; n < argc; n++) { 457 src = strrchr(argv[n], ':'); 458 if (src == NULL) 459 src = argv[n]; 460 else { 461 char *cp; 462 463 *src++ = 0; 464 cp = argv[n]; 465 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 466 cp[strlen(cp) - 1] = '\0'; 467 cp++; 468 } 469 setpeer(cp, NULL); 470 if (!connected) 471 continue; 472 } 473 if (argc < 4) { 474 cp = argc == 3 ? argv[2] : tail(src); 475 fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644); 476 if (fd < 0) { 477 warn("create: %s", cp); 478 return; 479 } 480 if (verbose) 481 printf("getting from %s:%s to %s [%s]\n", 482 hostname, src, cp, mode); 483 recvfile(fd, src, mode); 484 break; 485 } 486 cp = tail(src); /* new .. jdg */ 487 fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644); 488 if (fd < 0) { 489 warn("create: %s", cp); 490 continue; 491 } 492 if (verbose) 493 printf("getting from %s:%s to %s [%s]\n", 494 hostname, src, cp, mode); 495 recvfile(fd, src, mode); 496 } 497 } 498 499 static void 500 getusage(char *s) 501 { 502 printf("usage: %s [host:]file [localname]\n", s); 503 printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 504 } 505 506 void 507 setrexmt(int argc, char *argv[]) 508 { 509 int t; 510 const char *errstr; 511 512 if (argc < 2) { 513 strlcpy(line, "Rexmt-timeout ", sizeof(line)); 514 printf("(value) "); 515 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 516 if (makeargv()) 517 return; 518 argc = margc; 519 argv = margv; 520 } 521 if (argc != 2) { 522 printf("usage: %s value\n", argv[0]); 523 return; 524 } 525 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 526 if (errstr) 527 printf("%s: value is %s\n", argv[1], errstr); 528 else 529 rexmtval = t; 530 } 531 532 void 533 settimeout(int argc, char *argv[]) 534 { 535 int t; 536 const char *errstr; 537 538 if (argc < 2) { 539 strlcpy(line, "Maximum-timeout ", sizeof(line)); 540 printf("(value) "); 541 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 542 if (makeargv()) 543 return; 544 argc = margc; 545 argv = margv; 546 } 547 if (argc != 2) { 548 printf("usage: %s value\n", argv[0]); 549 return; 550 } 551 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 552 if (errstr) 553 printf("%s: value is %s\n", argv[1], errstr); 554 else 555 maxtimeout = t; 556 } 557 558 /* ARGSUSED */ 559 void 560 status(int argc, char *argv[]) 561 { 562 if (connected) 563 printf("Connected to %s.\n", hostname); 564 else 565 printf("Not connected.\n"); 566 printf("Mode: %s Verbose: %s Tracing: %s\n", 567 mode, verbose ? "on" : "off", trace ? "on" : "off"); 568 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 569 rexmtval, maxtimeout); 570 } 571 572 /* ARGSUSED */ 573 void 574 intr(int signo) 575 { 576 intrflag = 1; 577 } 578 579 char * 580 tail(char *filename) 581 { 582 char *s; 583 584 while (*filename) { 585 s = strrchr(filename, '/'); 586 if (s == NULL) 587 break; 588 if (s[1]) 589 return (s + 1); 590 *s = '\0'; 591 } 592 593 return (filename); 594 } 595 596 /* 597 * Command parser. 598 */ 599 static __dead void 600 command(void) 601 { 602 struct cmd *c; 603 604 for (;;) { 605 printf("%s> ", prompt); 606 if (readcmd(line, LBUFLEN, stdin) < 1) 607 continue; 608 if ((line[0] == 0) || (line[0] == '\n')) 609 continue; 610 if (makeargv()) 611 continue; 612 if (margc == 0) 613 continue; 614 c = getcmd(margv[0]); 615 if (c == (struct cmd *) - 1) { 616 printf("?Ambiguous command\n"); 617 continue; 618 } 619 if (c == 0) { 620 printf("?Invalid command\n"); 621 continue; 622 } 623 (*c->handler)(margc, margv); 624 } 625 } 626 627 struct cmd * 628 getcmd(char *name) 629 { 630 char *p, *q; 631 struct cmd *c, *found; 632 int nmatches, longest; 633 634 longest = 0; 635 nmatches = 0; 636 found = 0; 637 intrflag = 0; 638 for (c = cmdtab; (p = c->name) != NULL; c++) { 639 for (q = name; *q == *p++; q++) 640 if (*q == 0) /* exact match? */ 641 return (c); 642 if (!*q) { /* the name was a prefix */ 643 if (q - name > longest) { 644 longest = q - name; 645 nmatches = 1; 646 found = c; 647 } else if (q - name == longest) 648 nmatches++; 649 } 650 } 651 if (nmatches > 1) 652 return ((struct cmd *) - 1); 653 654 return (found); 655 } 656 657 /* 658 * Slice a string up into argc/argv. 659 */ 660 static int 661 makeargv(void) 662 { 663 char *cp; 664 char **argp = margv; 665 int ret = 0; 666 667 margc = 0; 668 for (cp = line; *cp;) { 669 if (margc >= MAXARGV) { 670 printf("too many arguments\n"); 671 ret = 1; 672 break; 673 } 674 while (isspace((unsigned char)*cp)) 675 cp++; 676 if (*cp == '\0') 677 break; 678 *argp++ = cp; 679 margc += 1; 680 while (*cp != '\0' && !isspace((unsigned char)*cp)) 681 cp++; 682 if (*cp == '\0') 683 break; 684 *cp++ = '\0'; 685 } 686 *argp++ = 0; 687 688 return (ret); 689 } 690 691 /* ARGSUSED */ 692 void 693 quit(int argc, char *argv[]) 694 { 695 exit(0); 696 } 697 698 /* 699 * Help command. 700 */ 701 void 702 help(int argc, char *argv[]) 703 { 704 struct cmd *c; 705 706 if (argc == 1) { 707 printf("Commands may be abbreviated. Commands are:\n\n"); 708 for (c = cmdtab; c->name != NULL; c++) 709 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 710 return; 711 } 712 while (--argc > 0) { 713 char *arg; 714 arg = *++argv; 715 c = getcmd(arg); 716 if (c == (struct cmd *) - 1) 717 printf("?Ambiguous help command %s\n", arg); 718 else if (c == NULL) 719 printf("?Invalid help command %s\n", arg); 720 else 721 printf("%s\n", c->help); 722 } 723 } 724 725 /* ARGSUSED */ 726 void 727 settrace(int argc, char *argv[]) 728 { 729 trace = !trace; 730 printf("Packet tracing %s.\n", trace ? "on" : "off"); 731 } 732 733 /* ARGSUSED */ 734 void 735 setverbose(int argc, char *argv[]) 736 { 737 verbose = !verbose; 738 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 739 } 740 741 /* ARGSUSED */ 742 void 743 settsize(int argc, char *argv[]) 744 { 745 opt_tsize = !opt_tsize; 746 printf("Tsize option %s.\n", opt_tsize ? "on" : "off"); 747 if (opt_tsize) 748 has_options++; 749 else 750 has_options--; 751 } 752 753 /* ARGSUSED */ 754 void 755 settout(int argc, char *argv[]) 756 { 757 opt_tout = !opt_tout; 758 printf("Timeout option %s.\n", opt_tout ? "on" : "off"); 759 if (opt_tout) 760 has_options++; 761 else 762 has_options--; 763 } 764 765 void 766 setblksize(int argc, char *argv[]) 767 { 768 int t; 769 const char *errstr; 770 771 if (argc < 2) { 772 strlcpy(line, "Blocksize ", sizeof(line)); 773 printf("(value) "); 774 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 775 if (makeargv()) 776 return; 777 argc = margc; 778 argv = margv; 779 } 780 if (argc != 2) { 781 printf("usage: %s value\n", argv[0]); 782 return; 783 } 784 t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr); 785 if (errstr) 786 printf("%s: value is %s\n", argv[1], errstr); 787 else { 788 if (opt_blksize == 0) 789 has_options++; 790 opt_blksize = t; 791 } 792 } 793 794 int 795 readcmd(char *input, int len, FILE *stream) 796 { 797 int nfds; 798 struct pollfd pfd[1]; 799 800 fflush(stdout); 801 802 pfd[0].fd = 0; 803 pfd[0].events = POLLIN; 804 nfds = poll(pfd, 1, INFTIM); 805 if (nfds == -1) { 806 if (intrflag) { 807 intrflag = 0; 808 putchar('\n'); 809 return (0); 810 } 811 exit(1); 812 } 813 814 if (fgets(input, len, stream) == NULL) { 815 if (feof(stdin)) 816 exit(0); 817 else 818 return (-1); 819 } 820 821 return (1); 822 } 823