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