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