1 /* $NetBSD: main.c,v 1.21 2006/01/31 17:36:56 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\ 35 The Regents of the University of California. All rights reserved.\n"); 36 #if 0 37 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 38 #else 39 __RCSID("$NetBSD: main.c,v 1.21 2006/01/31 17:36:56 christos Exp $"); 40 #endif 41 #endif /* not lint */ 42 43 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 44 45 /* 46 * TFTP User Program -- Command Interface. 47 */ 48 #include <sys/types.h> 49 #include <sys/socket.h> 50 51 #include <netinet/in.h> 52 53 #include <arpa/inet.h> 54 #include <arpa/tftp.h> 55 56 #include <ctype.h> 57 #include <fcntl.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <netdb.h> 61 #include <setjmp.h> 62 #include <signal.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 68 #include "extern.h" 69 70 #define TIMEOUT 5 /* secs between rexmt's */ 71 #define LBUFLEN 200 /* size of input buffer */ 72 73 struct sockaddr_storage peeraddr; 74 int f; 75 int trace; 76 int verbose; 77 int tsize=0; 78 int tout=0; 79 size_t def_blksize=SEGSIZE; 80 size_t blksize=SEGSIZE; 81 int connected; 82 char mode[32]; 83 char line[LBUFLEN]; 84 int margc; 85 char *margv[20]; 86 const char *prompt = "tftp"; 87 jmp_buf toplevel; 88 89 void get __P((int, char **)); 90 void help __P((int, char **)); 91 void modecmd __P((int, char **)); 92 void put __P((int, char **)); 93 void quit __P((int, char **)); 94 void setascii __P((int, char **)); 95 void setbinary __P((int, char **)); 96 void setpeer0 __P((const char *, const char *)); 97 void setpeer __P((int, char **)); 98 void setrexmt __P((int, char **)); 99 void settimeout __P((int, char **)); 100 void settrace __P((int, char **)); 101 void setverbose __P((int, char **)); 102 void setblksize __P((int, char **)); 103 void settsize __P((int, char **)); 104 void settimeoutopt __P((int, char **)); 105 void status __P((int, char **)); 106 char *tail __P((char *)); 107 int main __P((int, char *[])); 108 void intr __P((int)); 109 const struct cmd *getcmd __P((char *)); 110 111 static __dead void command __P((void)); 112 113 static void getusage __P((char *)); 114 static void makeargv __P((void)); 115 static void putusage __P((char *)); 116 static void settftpmode __P((const char *)); 117 118 #define HELPINDENT (sizeof("connect")) 119 120 struct cmd { 121 const char *name; 122 const char *help; 123 void (*handler) __P((int, char **)); 124 }; 125 126 const char vhelp[] = "toggle verbose mode"; 127 const char thelp[] = "toggle packet tracing"; 128 const char tshelp[] = "toggle extended tsize option"; 129 const char tohelp[] = "toggle extended timeout option"; 130 const char blhelp[] = "set an alternative blocksize (def. 512)"; 131 const char chelp[] = "connect to remote tftp"; 132 const char qhelp[] = "exit tftp"; 133 const char hhelp[] = "print help information"; 134 const char shelp[] = "send file"; 135 const char rhelp[] = "receive file"; 136 const char mhelp[] = "set file transfer mode"; 137 const char sthelp[] = "show current status"; 138 const char xhelp[] = "set per-packet retransmission timeout"; 139 const char ihelp[] = "set total retransmission timeout"; 140 const char ashelp[] = "set mode to netascii"; 141 const char bnhelp[] = "set mode to octet"; 142 143 const struct cmd cmdtab[] = { 144 { "connect", chelp, setpeer }, 145 { "mode", mhelp, modecmd }, 146 { "put", shelp, put }, 147 { "get", rhelp, get }, 148 { "quit", qhelp, quit }, 149 { "verbose", vhelp, setverbose }, 150 { "blksize", blhelp, setblksize }, 151 { "tsize", tshelp, settsize }, 152 { "trace", thelp, settrace }, 153 { "status", sthelp, status }, 154 { "binary", bnhelp, setbinary }, 155 { "ascii", ashelp, setascii }, 156 { "rexmt", xhelp, setrexmt }, 157 { "timeout", ihelp, settimeout }, 158 { "tout", tohelp, settimeoutopt }, 159 { "?", hhelp, help }, 160 { 0 } 161 }; 162 163 int 164 main(argc, argv) 165 int argc; 166 char *argv[]; 167 { 168 int c; 169 170 f = -1; 171 strcpy(mode, "netascii"); 172 signal(SIGINT, intr); 173 174 setprogname(argv[0]); 175 while ((c = getopt(argc, argv, "e")) != -1) { 176 switch (c) { 177 case 'e': 178 blksize = MAXSEGSIZE; 179 strcpy(mode, "octet"); 180 tsize = 1; 181 tout = 1; 182 break; 183 default: 184 printf("usage: %s [-e] host-name [port]\n", 185 getprogname()); 186 exit(1); 187 } 188 } 189 argc -= optind; 190 argv += optind; 191 192 if (argc >= 1) { 193 if (setjmp(toplevel) != 0) 194 exit(0); 195 argc++; 196 argv--; 197 setpeer(argc, argv); 198 } 199 if (setjmp(toplevel) != 0) 200 (void)putchar('\n'); 201 command(); 202 return (0); 203 } 204 205 char hostname[100]; 206 207 void 208 setpeer0(host, port) 209 const char *host; 210 const char *port; 211 { 212 struct addrinfo hints, *res0, *res; 213 int error, soopt; 214 struct sockaddr_storage ss; 215 const char *cause = "unknown"; 216 217 if (connected) { 218 (void)close(f); 219 f = -1; 220 } 221 connected = 0; 222 223 memset(&hints, 0, sizeof(hints)); 224 hints.ai_family = PF_UNSPEC; 225 hints.ai_socktype = SOCK_DGRAM; 226 hints.ai_protocol = IPPROTO_UDP; 227 hints.ai_flags = AI_CANONNAME; 228 if (!port) 229 port = "tftp"; 230 error = getaddrinfo(host, port, &hints, &res0); 231 if (error) { 232 warnx("%s", gai_strerror(error)); 233 return; 234 } 235 236 for (res = res0; res; res = res->ai_next) { 237 if (res->ai_addrlen > sizeof(peeraddr)) 238 continue; 239 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 240 if (f < 0) { 241 cause = "socket"; 242 continue; 243 } 244 245 memset(&ss, 0, sizeof(ss)); 246 ss.ss_family = res->ai_family; 247 ss.ss_len = res->ai_addrlen; 248 if (bind(f, (struct sockaddr *)(void *)&ss, 249 (socklen_t)ss.ss_len) < 0) { 250 cause = "bind"; 251 close(f); 252 f = -1; 253 continue; 254 } 255 256 break; 257 } 258 259 if (f >= 0) { 260 soopt = 65536; 261 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt)) 262 < 0) { 263 close(f); 264 f = -1; 265 cause = "setsockopt SNDBUF"; 266 } 267 if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt)) 268 < 0) { 269 close(f); 270 f = -1; 271 cause = "setsockopt RCVBUF"; 272 } 273 } 274 275 if (f < 0) 276 warn("%s", cause); 277 else { 278 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 279 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 280 if (res->ai_canonname) { 281 (void) strlcpy(hostname, res->ai_canonname, 282 sizeof(hostname)); 283 } else 284 (void) strlcpy(hostname, host, sizeof(hostname)); 285 connected = 1; 286 } 287 288 freeaddrinfo(res0); 289 } 290 291 void 292 setpeer(argc, argv) 293 int argc; 294 char *argv[]; 295 { 296 297 if (argc < 2) { 298 strcpy(line, "Connect "); 299 printf("(to) "); 300 fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 301 makeargv(); 302 argc = margc; 303 argv = margv; 304 } 305 if ((argc < 2) || (argc > 3)) { 306 printf("usage: %s [-e] host-name [port]\n", getprogname()); 307 return; 308 } 309 if (argc == 2) 310 setpeer0(argv[1], NULL); 311 else 312 setpeer0(argv[1], argv[2]); 313 } 314 315 struct modes { 316 const char *m_name; 317 const char *m_mode; 318 } modes[] = { 319 { "ascii", "netascii" }, 320 { "netascii", "netascii" }, 321 { "binary", "octet" }, 322 { "image", "octet" }, 323 { "octet", "octet" }, 324 /* { "mail", "mail" }, */ 325 { 0, 0 } 326 }; 327 328 void 329 modecmd(argc, argv) 330 int argc; 331 char *argv[]; 332 { 333 struct modes *p; 334 const char *sep; 335 336 if (argc < 2) { 337 printf("Using %s mode to transfer files.\n", mode); 338 return; 339 } 340 if (argc == 2) { 341 for (p = modes; p->m_name; p++) 342 if (strcmp(argv[1], p->m_name) == 0) 343 break; 344 if (p->m_name) { 345 settftpmode(p->m_mode); 346 return; 347 } 348 printf("%s: unknown mode\n", argv[1]); 349 /* drop through and print usage message */ 350 } 351 352 printf("usage: %s [", argv[0]); 353 sep = " "; 354 for (p = modes; p->m_name; p++) { 355 printf("%s%s", sep, p->m_name); 356 if (*sep == ' ') 357 sep = " | "; 358 } 359 printf(" ]\n"); 360 return; 361 } 362 363 void 364 /*ARGSUSED*/ 365 setbinary(argc, argv) 366 int argc; 367 char *argv[]; 368 { 369 370 settftpmode("octet"); 371 } 372 373 void 374 /*ARGSUSED*/ 375 setascii(argc, argv) 376 int argc; 377 char *argv[]; 378 { 379 380 settftpmode("netascii"); 381 } 382 383 static void 384 settftpmode(newmode) 385 const char *newmode; 386 { 387 strcpy(mode, newmode); 388 if (verbose) 389 printf("mode set to %s\n", mode); 390 } 391 392 393 /* 394 * Send file(s). 395 */ 396 void 397 put(argc, argv) 398 int argc; 399 char *argv[]; 400 { 401 int fd; 402 int n; 403 char *targ, *p; 404 405 if (argc < 2) { 406 strcpy(line, "send "); 407 printf("(file) "); 408 fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 409 makeargv(); 410 argc = margc; 411 argv = margv; 412 } 413 if (argc < 2) { 414 putusage(argv[0]); 415 return; 416 } 417 targ = argv[argc - 1]; 418 if (strrchr(argv[argc - 1], ':')) { 419 char *cp; 420 421 for (n = 1; n < argc - 1; n++) 422 if (strchr(argv[n], ':')) { 423 putusage(argv[0]); 424 return; 425 } 426 cp = argv[argc - 1]; 427 targ = strrchr(cp, ':'); 428 *targ++ = 0; 429 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 430 cp[strlen(cp) - 1] = '\0'; 431 cp++; 432 } 433 setpeer0(cp, NULL); 434 } 435 if (!connected) { 436 printf("No target machine specified.\n"); 437 return; 438 } 439 if (argc < 4) { 440 char *cp = argc == 2 ? tail(targ) : argv[1]; 441 fd = open(cp, O_RDONLY); 442 if (fd < 0) { 443 warn("%s", cp); 444 return; 445 } 446 if (verbose) 447 printf("putting %s to %s:%s [%s]\n", 448 cp, hostname, targ, mode); 449 sendfile(fd, targ, mode); 450 return; 451 } 452 /* this assumes the target is a directory */ 453 /* on a remote unix system. hmmmm. */ 454 p = strchr(targ, '\0'); 455 *p++ = '/'; 456 for (n = 1; n < argc - 1; n++) { 457 strcpy(p, tail(argv[n])); 458 fd = open(argv[n], O_RDONLY); 459 if (fd < 0) { 460 warn("%s", argv[n]); 461 continue; 462 } 463 if (verbose) 464 printf("putting %s to %s:%s [%s]\n", 465 argv[n], hostname, targ, mode); 466 sendfile(fd, targ, mode); 467 } 468 } 469 470 static void 471 putusage(s) 472 char *s; 473 { 474 printf("usage: %s file ... host:target, or\n", s); 475 printf(" %s file ... target (when already connected)\n", s); 476 } 477 478 /* 479 * Receive file(s). 480 */ 481 void 482 get(argc, argv) 483 int argc; 484 char *argv[]; 485 { 486 int fd; 487 int n; 488 char *p; 489 char *src; 490 491 if (argc < 2) { 492 strcpy(line, "get "); 493 printf("(files) "); 494 fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 495 makeargv(); 496 argc = margc; 497 argv = margv; 498 } 499 if (argc < 2) { 500 getusage(argv[0]); 501 return; 502 } 503 if (!connected) { 504 for (n = 1; n < argc ; n++) 505 if (strrchr(argv[n], ':') == 0) { 506 getusage(argv[0]); 507 return; 508 } 509 } 510 for (n = 1; n < argc ; n++) { 511 src = strrchr(argv[n], ':'); 512 if (src == NULL) 513 src = argv[n]; 514 else { 515 char *cp; 516 *src++ = 0; 517 cp = argv[n]; 518 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 519 cp[strlen(cp) - 1] = '\0'; 520 cp++; 521 } 522 setpeer0(cp, NULL); 523 if (!connected) 524 continue; 525 } 526 if (argc < 4) { 527 char *cp = argc == 3 ? argv[2] : tail(src); 528 fd = creat(cp, 0644); 529 if (fd < 0) { 530 warn("%s", cp); 531 return; 532 } 533 if (verbose) 534 printf("getting from %s:%s to %s [%s]\n", 535 hostname, src, cp, mode); 536 recvfile(fd, src, mode); 537 break; 538 } 539 p = tail(src); /* new .. jdg */ 540 fd = creat(p, 0644); 541 if (fd < 0) { 542 warn("%s", p); 543 continue; 544 } 545 if (verbose) 546 printf("getting from %s:%s to %s [%s]\n", 547 hostname, src, p, mode); 548 recvfile(fd, src, mode); 549 } 550 } 551 552 static void 553 getusage(s) 554 char *s; 555 { 556 printf("usage: %s host:file host:file ... file, or\n", s); 557 printf(" %s file file ... file if connected\n", s); 558 } 559 560 void 561 setblksize(argc, argv) 562 int argc; 563 char *argv[]; 564 { 565 int t; 566 567 if (argc < 2) { 568 strcpy(line, "blksize "); 569 printf("(blksize) "); 570 fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 571 makeargv(); 572 argc = margc; 573 argv = margv; 574 } 575 if (argc != 2) { 576 printf("usage: %s value\n", argv[0]); 577 return; 578 } 579 t = atoi(argv[1]); 580 if (t < 8 || t > 65464) 581 printf("%s: bad value\n", argv[1]); 582 else 583 blksize = t; 584 } 585 586 unsigned int def_rexmtval = TIMEOUT; 587 unsigned int rexmtval = TIMEOUT; 588 589 void 590 setrexmt(argc, argv) 591 int argc; 592 char *argv[]; 593 { 594 int t; 595 596 if (argc < 2) { 597 strcpy(line, "Rexmt-timeout "); 598 printf("(value) "); 599 fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 600 makeargv(); 601 argc = margc; 602 argv = margv; 603 } 604 if (argc != 2) { 605 printf("usage: %s value\n", argv[0]); 606 return; 607 } 608 t = atoi(argv[1]); 609 if (t < 0) 610 printf("%s: bad value\n", argv[1]); 611 else 612 rexmtval = t; 613 } 614 615 int maxtimeout = 5 * TIMEOUT; 616 617 void 618 settimeout(argc, argv) 619 int argc; 620 char *argv[]; 621 { 622 int t; 623 624 if (argc < 2) { 625 strcpy(line, "Maximum-timeout "); 626 printf("(value) "); 627 fgets(&line[strlen(line)], (int)(LBUFLEN-strlen(line)), stdin); 628 makeargv(); 629 argc = margc; 630 argv = margv; 631 } 632 if (argc != 2) { 633 printf("usage: %s value\n", argv[0]); 634 return; 635 } 636 t = atoi(argv[1]); 637 if (t < 0) 638 printf("%s: bad value\n", argv[1]); 639 else 640 maxtimeout = t; 641 } 642 643 void 644 /*ARGSUSED*/ 645 status(argc, argv) 646 int argc; 647 char *argv[]; 648 { 649 if (connected) 650 printf("Connected to %s.\n", hostname); 651 else 652 printf("Not connected.\n"); 653 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 654 verbose ? "on" : "off", trace ? "on" : "off"); 655 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 656 rexmtval, maxtimeout); 657 } 658 659 void 660 /*ARGSUSED*/ 661 intr(dummy) 662 int dummy; 663 { 664 665 signal(SIGALRM, SIG_IGN); 666 alarm(0); 667 longjmp(toplevel, -1); 668 } 669 670 char * 671 tail(filename) 672 char *filename; 673 { 674 char *s; 675 676 while (*filename) { 677 s = strrchr(filename, '/'); 678 if (s == NULL) 679 break; 680 if (s[1]) 681 return (s + 1); 682 *s = '\0'; 683 } 684 return (filename); 685 } 686 687 /* 688 * Command parser. 689 */ 690 static __dead void 691 command() 692 { 693 const struct cmd *c; 694 695 for (;;) { 696 printf("%s> ", prompt); 697 if (fgets(line, LBUFLEN, stdin) == 0) { 698 if (feof(stdin)) { 699 exit(0); 700 } else { 701 continue; 702 } 703 } 704 if ((line[0] == 0) || (line[0] == '\n')) 705 continue; 706 makeargv(); 707 if (margc == 0) 708 continue; 709 c = getcmd(margv[0]); 710 if (c == (struct cmd *)-1) { 711 printf("?Ambiguous command\n"); 712 continue; 713 } 714 if (c == 0) { 715 printf("?Invalid command\n"); 716 continue; 717 } 718 (*c->handler)(margc, margv); 719 } 720 } 721 722 const struct cmd * 723 getcmd(name) 724 char *name; 725 { 726 const char *p, *q; 727 const struct cmd *c, *found; 728 int nmatches, longest; 729 730 longest = 0; 731 nmatches = 0; 732 found = 0; 733 for (c = cmdtab; (p = c->name) != NULL; c++) { 734 for (q = name; *q == *p++; q++) 735 if (*q == 0) /* exact match? */ 736 return (c); 737 if (!*q) { /* the name was a prefix */ 738 if (q - name > longest) { 739 longest = q - name; 740 nmatches = 1; 741 found = c; 742 } else if (q - name == longest) 743 nmatches++; 744 } 745 } 746 if (nmatches > 1) 747 return ((struct cmd *)-1); 748 return (found); 749 } 750 751 /* 752 * Slice a string up into argc/argv. 753 */ 754 static void 755 makeargv() 756 { 757 char *cp; 758 char **argp = margv; 759 760 margc = 0; 761 for (cp = line; *cp;) { 762 while (isspace((unsigned char)*cp)) 763 cp++; 764 if (*cp == '\0') 765 break; 766 *argp++ = cp; 767 margc += 1; 768 while (*cp != '\0' && !isspace((unsigned char)*cp)) 769 cp++; 770 if (*cp == '\0') 771 break; 772 *cp++ = '\0'; 773 } 774 *argp++ = 0; 775 } 776 777 void 778 /*ARGSUSED*/ 779 quit(argc, argv) 780 int argc; 781 char *argv[]; 782 { 783 784 exit(0); 785 } 786 787 /* 788 * Help command. 789 */ 790 void 791 help(argc, argv) 792 int argc; 793 char *argv[]; 794 { 795 const struct cmd *c; 796 797 if (argc == 1) { 798 printf("Commands may be abbreviated. Commands are:\n\n"); 799 for (c = cmdtab; c->name; c++) 800 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 801 return; 802 } 803 while (--argc > 0) { 804 char *arg; 805 arg = *++argv; 806 c = getcmd(arg); 807 if (c == (struct cmd *)-1) 808 printf("?Ambiguous help command %s\n", arg); 809 else if (c == (struct cmd *)0) 810 printf("?Invalid help command %s\n", arg); 811 else 812 printf("%s\n", c->help); 813 } 814 } 815 816 void 817 /*ARGSUSED*/ 818 settrace(argc, argv) 819 int argc; 820 char **argv; 821 { 822 trace = !trace; 823 printf("Packet tracing %s.\n", trace ? "on" : "off"); 824 } 825 826 void 827 /*ARGSUSED*/ 828 setverbose(argc, argv) 829 int argc; 830 char **argv; 831 { 832 verbose = !verbose; 833 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 834 } 835 836 void 837 /*ARGSUSED*/ 838 settsize(argc, argv) 839 int argc; 840 char **argv; 841 { 842 tsize = !tsize; 843 printf("Tsize mode %s.\n", tsize ? "on" : "off"); 844 } 845 846 void 847 /*ARGSUSED*/ 848 settimeoutopt(argc, argv) 849 int argc; 850 char **argv; 851 { 852 tout = !tout; 853 printf("Timeout option %s.\n", tout ? "on" : "off"); 854 } 855