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