1 /* $OpenBSD: main.c,v 1.30 2009/10/27 23:59:44 deraadt 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/param.h> 40 #include <sys/socket.h> 41 #include <sys/file.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 <errno.h> 50 #include <netdb.h> 51 #include <poll.h> 52 #include <signal.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.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(int, char **); 72 void setrexmt(int, char **); 73 void settimeout(int, char **); 74 void settrace(int, char **); 75 void setverbose(int, char **); 76 void settsize(int, char **); 77 void settout(int, char **); 78 void setblksize(int, char **); 79 void status(int, char **); 80 int readcmd(char *, int, FILE *); 81 static void getusage(char *); 82 static int makeargv(void); 83 static void putusage(char *); 84 static void settftpmode(char *); 85 static __dead void command(void); 86 struct cmd *getcmd(char *); 87 char *tail(char *); 88 89 struct sockaddr_in peeraddr; 90 int f; 91 short port; 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 struct servent *sp; 102 int rexmtval = TIMEOUT; 103 int maxtimeout = 5 * TIMEOUT; 104 char hostname[MAXHOSTNAMELEN]; 105 FILE *file = NULL; 106 volatile sig_atomic_t intrflag = 0; 107 char *ackbuf; 108 int has_options = 0; 109 int opt_tsize = 0; 110 int opt_tout = 0; 111 int opt_blksize = 0; 112 113 char vhelp[] = "toggle verbose mode"; 114 char thelp[] = "toggle packet tracing"; 115 char chelp[] = "connect to remote tftp"; 116 char qhelp[] = "exit tftp"; 117 char hhelp[] = "print help information"; 118 char shelp[] = "send file"; 119 char rhelp[] = "receive file"; 120 char mhelp[] = "set file transfer mode"; 121 char sthelp[] = "show current status"; 122 char xhelp[] = "set per-packet retransmission timeout"; 123 char ihelp[] = "set total retransmission timeout"; 124 char ashelp[] = "set mode to netascii"; 125 char bnhelp[] = "set mode to octet"; 126 char oshelp[] = "toggle tsize option"; 127 char othelp[] = "toggle timeout option"; 128 char obhelp[] = "set alternative blksize option"; 129 130 struct cmd { 131 char *name; 132 char *help; 133 void (*handler)(int, char **); 134 }; 135 136 struct cmd cmdtab[] = { 137 { "connect", chelp, setpeer }, 138 { "mode", mhelp, modecmd }, 139 { "put", shelp, put }, 140 { "get", rhelp, get }, 141 { "quit", qhelp, quit }, 142 { "verbose", vhelp, setverbose }, 143 { "trace", thelp, settrace }, 144 { "status", sthelp, status }, 145 { "binary", bnhelp, setbinary }, 146 { "ascii", ashelp, setascii }, 147 { "rexmt", xhelp, setrexmt }, 148 { "timeout", ihelp, settimeout }, 149 { "tsize", oshelp, settsize }, 150 { "tout", othelp, settout }, 151 { "blksize", obhelp, setblksize }, 152 { "help", hhelp, help }, 153 { "?", hhelp, help }, 154 { NULL, NULL, NULL } 155 }; 156 157 struct modes { 158 char *m_name; 159 char *m_mode; 160 } modes[] = { 161 { "ascii", "netascii" }, 162 { "netascii", "netascii" }, 163 { "binary", "octet" }, 164 { "image", "octet" }, 165 { "octet", "octet" }, 166 /* { "mail", "mail" }, */ 167 { NULL, NULL } 168 }; 169 170 int 171 main(int argc, char *argv[]) 172 { 173 struct sockaddr_in s_in; 174 175 /* socket, bind */ 176 sp = getservbyname("tftp", "udp"); 177 if (sp == 0) 178 errx(1, "udp/tftp: unknown service"); 179 f = socket(AF_INET, SOCK_DGRAM, 0); 180 if (f < 0) 181 err(3, "socket"); 182 bzero((char *)&s_in, sizeof(s_in)); 183 s_in.sin_family = AF_INET; 184 if (bind(f, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) 185 err(1, "bind"); 186 187 /* set default transfer mode */ 188 strlcpy(mode, "netascii", sizeof(mode)); 189 190 /* set peer if given */ 191 if (argc > 1) 192 setpeer(argc, argv); 193 194 /* catch SIGINT */ 195 signal(SIGINT, intr); 196 197 /* allocate memory for packets */ 198 if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL) 199 err(1, "malloc"); 200 201 /* command prompt */ 202 command(); 203 204 return (0); 205 } 206 207 void 208 setpeer(int argc, char *argv[]) 209 { 210 struct hostent *host; 211 const char *errstr; 212 213 if (argc < 2) { 214 strlcpy(line, "Connect ", sizeof(line)); 215 printf("(to) "); 216 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 217 if (makeargv()) 218 return; 219 argc = margc; 220 argv = margv; 221 } 222 if ((argc < 2) || (argc > 3)) { 223 printf("usage: %s [host [port]]\n", argv[0]); 224 return; 225 } 226 if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) { 227 peeraddr.sin_family = AF_INET; 228 (void)strncpy(hostname, argv[1], sizeof(hostname)); 229 hostname[sizeof(hostname) - 1] = '\0'; 230 } else { 231 host = gethostbyname(argv[1]); 232 if (host == 0) { 233 connected = 0; 234 printf("%s: unknown host\n", argv[1]); 235 return; 236 } 237 peeraddr.sin_family = host->h_addrtype; 238 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); 239 (void)strlcpy(hostname, host->h_name, sizeof(hostname)); 240 } 241 port = sp->s_port; 242 if (argc == 3) { 243 port = strtonum(argv[2], 1, 65535, &errstr); 244 if (errstr) { 245 printf("%s: port number is %s\n", argv[2], errstr); 246 connected = 0; 247 return; 248 } 249 port = htons(port); 250 } 251 connected = 1; 252 } 253 254 void 255 modecmd(int argc, char *argv[]) 256 { 257 struct modes *p; 258 char *sep; 259 260 if (argc < 2) { 261 printf("Using %s mode to transfer files.\n", mode); 262 return; 263 } 264 if (argc == 2) { 265 for (p = modes; p->m_name != NULL; p++) 266 if (strcmp(argv[1], p->m_name) == 0) 267 break; 268 if (p->m_name) { 269 settftpmode(p->m_mode); 270 return; 271 } 272 printf("%s: unknown mode\n", argv[1]); 273 /* drop through and print usage message */ 274 } 275 276 printf("usage: %s [", argv[0]); 277 sep = " "; 278 for (p = modes; p->m_name != NULL; p++) { 279 printf("%s%s", sep, p->m_name); 280 if (*sep == ' ') 281 sep = " | "; 282 } 283 printf(" ]\n"); 284 285 return; 286 } 287 288 /* ARGSUSED */ 289 void 290 setbinary(int argc, char *argv[]) 291 { 292 settftpmode("octet"); 293 } 294 295 /* ARGSUSED */ 296 void 297 setascii(int argc, char *argv[]) 298 { 299 settftpmode("netascii"); 300 } 301 302 static void 303 settftpmode(char *newmode) 304 { 305 strlcpy(mode, newmode, sizeof(mode)); 306 if (verbose) 307 printf("mode set to %s\n", mode); 308 } 309 310 /* 311 * Send file(s). 312 */ 313 void 314 put(int argc, char *argv[]) 315 { 316 int fd; 317 int n; 318 char *cp, *targ; 319 320 if (argc < 2) { 321 strlcpy(line, "send ", sizeof(line)); 322 printf("(file) "); 323 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 324 if (makeargv()) 325 return; 326 argc = margc; 327 argv = margv; 328 } 329 if (argc < 2) { 330 putusage(argv[0]); 331 return; 332 } 333 targ = argv[argc - 1]; 334 if (strchr(argv[argc - 1], ':')) { 335 struct hostent *hp; 336 337 for (n = 1; n < argc - 1; n++) 338 if (strchr(argv[n], ':')) { 339 putusage(argv[0]); 340 return; 341 } 342 cp = argv[argc - 1]; 343 targ = strchr(cp, ':'); 344 *targ++ = 0; 345 hp = gethostbyname(cp); 346 if (hp == NULL) { 347 warnx("%s: %s", cp, hstrerror(h_errno)); 348 return; 349 } 350 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); 351 peeraddr.sin_family = hp->h_addrtype; 352 connected = 1; 353 port = sp->s_port; 354 strlcpy(hostname, hp->h_name, sizeof(hostname)); 355 } 356 if (!connected) { 357 printf("No target machine specified.\n"); 358 return; 359 } 360 if (argc < 4) { 361 cp = argc == 2 ? tail(targ) : argv[1]; 362 fd = open(cp, O_RDONLY); 363 if (fd < 0) { 364 warn("open: %s", cp); 365 return; 366 } 367 if (verbose) 368 printf("putting %s to %s:%s [%s]\n", 369 cp, hostname, targ, mode); 370 peeraddr.sin_port = port; 371 sendfile(fd, targ, mode); 372 return; 373 } 374 375 /* 376 * this assumes the target is a directory on 377 * on a remote unix system. hmmmm. 378 */ 379 for (n = 1; n < argc - 1; n++) { 380 if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1) 381 err(1, "asprintf"); 382 fd = open(argv[n], O_RDONLY); 383 if (fd < 0) { 384 warn("open: %s", argv[n]); 385 free(cp); 386 continue; 387 } 388 if (verbose) 389 printf("putting %s to %s:%s [%s]\n", 390 argv[n], hostname, cp, mode); 391 peeraddr.sin_port = port; 392 sendfile(fd, cp, mode); 393 free(cp); 394 } 395 } 396 397 static void 398 putusage(char *s) 399 { 400 printf("usage: %s file [[host:]remotename]\n", s); 401 printf(" %s file1 file2 ... fileN [[host:]remote-directory]\n", 402 s); 403 } 404 405 /* 406 * Receive file(s). 407 */ 408 void 409 get(int argc, char *argv[]) 410 { 411 int fd; 412 int n; 413 char *cp; 414 char *src; 415 416 if (argc < 2) { 417 strlcpy(line, "get ", sizeof(line)); 418 printf("(files) "); 419 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 420 if (makeargv()) 421 return; 422 argc = margc; 423 argv = margv; 424 } 425 if (argc < 2) { 426 getusage(argv[0]); 427 return; 428 } 429 if (!connected) { 430 for (n = 1; n < argc; n++) 431 if (strchr(argv[n], ':') == 0) { 432 getusage(argv[0]); 433 return; 434 } 435 } 436 for (n = 1; n < argc; n++) { 437 src = strchr(argv[n], ':'); 438 if (src == NULL) 439 src = argv[n]; 440 else { 441 struct hostent *hp; 442 443 *src++ = 0; 444 hp = gethostbyname(argv[n]); 445 if (hp == NULL) { 446 warnx("%s: %s", argv[n], hstrerror(h_errno)); 447 continue; 448 } 449 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 450 hp->h_length); 451 peeraddr.sin_family = hp->h_addrtype; 452 connected = 1; 453 strlcpy(hostname, hp->h_name, sizeof(hostname)); 454 } 455 if (argc < 4) { 456 cp = argc == 3 ? argv[2] : tail(src); 457 fd = creat(cp, 0644); 458 if (fd < 0) { 459 warn("create: %s", cp); 460 return; 461 } 462 if (verbose) 463 printf("getting from %s:%s to %s [%s]\n", 464 hostname, src, cp, mode); 465 peeraddr.sin_port = port; 466 recvfile(fd, src, mode); 467 break; 468 } 469 cp = tail(src); /* new .. jdg */ 470 fd = creat(cp, 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 peeraddr.sin_port = port; 479 recvfile(fd, src, mode); 480 } 481 } 482 483 static void 484 getusage(char *s) 485 { 486 printf("usage: %s [host:]file [localname]\n", s); 487 printf(" %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s); 488 } 489 490 void 491 setrexmt(int argc, char *argv[]) 492 { 493 int t; 494 const char *errstr; 495 496 if (argc < 2) { 497 strlcpy(line, "Rexmt-timeout ", sizeof(line)); 498 printf("(value) "); 499 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 500 if (makeargv()) 501 return; 502 argc = margc; 503 argv = margv; 504 } 505 if (argc != 2) { 506 printf("usage: %s value\n", argv[0]); 507 return; 508 } 509 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 510 if (errstr) 511 printf("%s: value is %s\n", argv[1], errstr); 512 else 513 rexmtval = t; 514 } 515 516 void 517 settimeout(int argc, char *argv[]) 518 { 519 int t; 520 const char *errstr; 521 522 if (argc < 2) { 523 strlcpy(line, "Maximum-timeout ", sizeof(line)); 524 printf("(value) "); 525 readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin); 526 if (makeargv()) 527 return; 528 argc = margc; 529 argv = margv; 530 } 531 if (argc != 2) { 532 printf("usage: %s value\n", argv[0]); 533 return; 534 } 535 t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr); 536 if (errstr) 537 printf("%s: value is %s\n", argv[1], errstr); 538 else 539 maxtimeout = t; 540 } 541 542 /* ARGSUSED */ 543 void 544 status(int argc, char *argv[]) 545 { 546 if (connected) 547 printf("Connected to %s.\n", hostname); 548 else 549 printf("Not connected.\n"); 550 printf("Mode: %s Verbose: %s Tracing: %s\n", 551 mode, verbose ? "on" : "off", trace ? "on" : "off"); 552 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 553 rexmtval, maxtimeout); 554 } 555 556 /* ARGSUSED */ 557 void 558 intr(int signo) 559 { 560 intrflag = 1; 561 } 562 563 char * 564 tail(char *filename) 565 { 566 char *s; 567 568 while (*filename) { 569 s = strrchr(filename, '/'); 570 if (s == NULL) 571 break; 572 if (s[1]) 573 return (s + 1); 574 *s = '\0'; 575 } 576 577 return (filename); 578 } 579 580 /* 581 * Command parser. 582 */ 583 static __dead void 584 command(void) 585 { 586 struct cmd *c; 587 588 for (;;) { 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(*cp)) 659 cp++; 660 if (*cp == '\0') 661 break; 662 *argp++ = cp; 663 margc += 1; 664 while (*cp != '\0' && !isspace(*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 == (struct cmd *)0) 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