1 /* $NetBSD: main.c,v 1.19 2003/10/02 23:31:52 itojun 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.19 2003/10/02 23:31:52 itojun 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 int def_blksize=SEGSIZE; 80 int blksize=SEGSIZE; 81 int connected; 82 char mode[32]; 83 char line[LBUFLEN]; 84 int margc; 85 char *margv[20]; 86 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((char *, 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 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((char *)); 117 118 #define HELPINDENT (sizeof("connect")) 119 120 struct cmd { 121 char *name; 122 char *help; 123 void (*handler) __P((int, char **)); 124 }; 125 126 char vhelp[] = "toggle verbose mode"; 127 char thelp[] = "toggle packet tracing"; 128 char tshelp[] = "toggle extended tsize option"; 129 char tohelp[] = "toggle extended timeout option"; 130 char blhelp[] = "set an alternative blocksize (def. 512)"; 131 char chelp[] = "connect to remote tftp"; 132 char qhelp[] = "exit tftp"; 133 char hhelp[] = "print help information"; 134 char shelp[] = "send file"; 135 char rhelp[] = "receive file"; 136 char mhelp[] = "set file transfer mode"; 137 char sthelp[] = "show current status"; 138 char xhelp[] = "set per-packet retransmission timeout"; 139 char ihelp[] = "set total retransmission timeout"; 140 char ashelp[] = "set mode to netascii"; 141 char bnhelp[] = "set mode to octet"; 142 143 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 char *host; 210 char *port; 211 { 212 struct addrinfo hints, *res0, *res; 213 int error, soopt; 214 struct sockaddr_storage ss; 215 char *cause = "unknown"; 216 217 if (connected) { 218 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 *)&ss, ss.ss_len) < 0) { 249 cause = "bind"; 250 close(f); 251 f = -1; 252 continue; 253 } 254 255 break; 256 } 257 258 if (f >= 0) { 259 soopt = 65536; 260 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &soopt, sizeof(soopt)) 261 < 0) { 262 close(f); 263 f = -1; 264 cause = "setsockopt SNDBUF"; 265 } 266 if (setsockopt(f, SOL_SOCKET, SO_RCVBUF, &soopt, sizeof(soopt)) 267 < 0) { 268 close(f); 269 f = -1; 270 cause = "setsockopt RCVBUF"; 271 } 272 } 273 274 if (f < 0) 275 warn("%s", cause); 276 else { 277 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 278 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 279 if (res->ai_canonname) { 280 (void) strlcpy(hostname, res->ai_canonname, 281 sizeof(hostname)); 282 } else 283 (void) strlcpy(hostname, host, sizeof(hostname)); 284 connected = 1; 285 } 286 287 freeaddrinfo(res0); 288 } 289 290 void 291 setpeer(argc, argv) 292 int argc; 293 char *argv[]; 294 { 295 296 if (argc < 2) { 297 strcpy(line, "Connect "); 298 printf("(to) "); 299 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 300 makeargv(); 301 argc = margc; 302 argv = margv; 303 } 304 if ((argc < 2) || (argc > 3)) { 305 printf("usage: %s [-e] host-name [port]\n", getprogname()); 306 return; 307 } 308 if (argc == 2) 309 setpeer0(argv[1], NULL); 310 else 311 setpeer0(argv[1], argv[2]); 312 } 313 314 struct modes { 315 char *m_name; 316 char *m_mode; 317 } modes[] = { 318 { "ascii", "netascii" }, 319 { "netascii", "netascii" }, 320 { "binary", "octet" }, 321 { "image", "octet" }, 322 { "octet", "octet" }, 323 /* { "mail", "mail" }, */ 324 { 0, 0 } 325 }; 326 327 void 328 modecmd(argc, argv) 329 int argc; 330 char *argv[]; 331 { 332 struct modes *p; 333 char *sep; 334 335 if (argc < 2) { 336 printf("Using %s mode to transfer files.\n", mode); 337 return; 338 } 339 if (argc == 2) { 340 for (p = modes; p->m_name; p++) 341 if (strcmp(argv[1], p->m_name) == 0) 342 break; 343 if (p->m_name) { 344 settftpmode(p->m_mode); 345 return; 346 } 347 printf("%s: unknown mode\n", argv[1]); 348 /* drop through and print usage message */ 349 } 350 351 printf("usage: %s [", argv[0]); 352 sep = " "; 353 for (p = modes; p->m_name; p++) { 354 printf("%s%s", sep, p->m_name); 355 if (*sep == ' ') 356 sep = " | "; 357 } 358 printf(" ]\n"); 359 return; 360 } 361 362 void 363 setbinary(argc, argv) 364 int argc; 365 char *argv[]; 366 { 367 368 settftpmode("octet"); 369 } 370 371 void 372 setascii(argc, argv) 373 int argc; 374 char *argv[]; 375 { 376 377 settftpmode("netascii"); 378 } 379 380 static void 381 settftpmode(newmode) 382 char *newmode; 383 { 384 strcpy(mode, newmode); 385 if (verbose) 386 printf("mode set to %s\n", mode); 387 } 388 389 390 /* 391 * Send file(s). 392 */ 393 void 394 put(argc, argv) 395 int argc; 396 char *argv[]; 397 { 398 int fd; 399 int n; 400 char *cp, *targ; 401 402 if (argc < 2) { 403 strcpy(line, "send "); 404 printf("(file) "); 405 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 406 makeargv(); 407 argc = margc; 408 argv = margv; 409 } 410 if (argc < 2) { 411 putusage(argv[0]); 412 return; 413 } 414 targ = argv[argc - 1]; 415 if (strrchr(argv[argc - 1], ':')) { 416 char *cp; 417 418 for (n = 1; n < argc - 1; n++) 419 if (strchr(argv[n], ':')) { 420 putusage(argv[0]); 421 return; 422 } 423 cp = argv[argc - 1]; 424 targ = strrchr(cp, ':'); 425 *targ++ = 0; 426 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 427 cp[strlen(cp) - 1] = '\0'; 428 cp++; 429 } 430 setpeer0(cp, NULL); 431 } 432 if (!connected) { 433 printf("No target machine specified.\n"); 434 return; 435 } 436 if (argc < 4) { 437 cp = argc == 2 ? tail(targ) : argv[1]; 438 fd = open(cp, O_RDONLY); 439 if (fd < 0) { 440 warn("%s", cp); 441 return; 442 } 443 if (verbose) 444 printf("putting %s to %s:%s [%s]\n", 445 cp, hostname, targ, mode); 446 sendfile(fd, targ, mode); 447 return; 448 } 449 /* this assumes the target is a directory */ 450 /* on a remote unix system. hmmmm. */ 451 cp = strchr(targ, '\0'); 452 *cp++ = '/'; 453 for (n = 1; n < argc - 1; n++) { 454 strcpy(cp, tail(argv[n])); 455 fd = open(argv[n], O_RDONLY); 456 if (fd < 0) { 457 warn("%s", argv[n]); 458 continue; 459 } 460 if (verbose) 461 printf("putting %s to %s:%s [%s]\n", 462 argv[n], hostname, targ, mode); 463 sendfile(fd, targ, mode); 464 } 465 } 466 467 static void 468 putusage(s) 469 char *s; 470 { 471 printf("usage: %s file ... host:target, or\n", s); 472 printf(" %s file ... target (when already connected)\n", s); 473 } 474 475 /* 476 * Receive file(s). 477 */ 478 void 479 get(argc, argv) 480 int argc; 481 char *argv[]; 482 { 483 int fd; 484 int n; 485 char *cp; 486 char *src; 487 488 if (argc < 2) { 489 strcpy(line, "get "); 490 printf("(files) "); 491 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 492 makeargv(); 493 argc = margc; 494 argv = margv; 495 } 496 if (argc < 2) { 497 getusage(argv[0]); 498 return; 499 } 500 if (!connected) { 501 for (n = 1; n < argc ; n++) 502 if (strrchr(argv[n], ':') == 0) { 503 getusage(argv[0]); 504 return; 505 } 506 } 507 for (n = 1; n < argc ; n++) { 508 src = strrchr(argv[n], ':'); 509 if (src == NULL) 510 src = argv[n]; 511 else { 512 char *cp; 513 *src++ = 0; 514 cp = argv[n]; 515 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 516 cp[strlen(cp) - 1] = '\0'; 517 cp++; 518 } 519 setpeer0(cp, NULL); 520 if (!connected) 521 continue; 522 } 523 if (argc < 4) { 524 cp = argc == 3 ? argv[2] : tail(src); 525 fd = creat(cp, 0644); 526 if (fd < 0) { 527 warn("%s", cp); 528 return; 529 } 530 if (verbose) 531 printf("getting from %s:%s to %s [%s]\n", 532 hostname, src, cp, mode); 533 recvfile(fd, src, mode); 534 break; 535 } 536 cp = tail(src); /* new .. jdg */ 537 fd = creat(cp, 0644); 538 if (fd < 0) { 539 warn("%s", cp); 540 continue; 541 } 542 if (verbose) 543 printf("getting from %s:%s to %s [%s]\n", 544 hostname, src, cp, mode); 545 recvfile(fd, src, mode); 546 } 547 } 548 549 static void 550 getusage(s) 551 char *s; 552 { 553 printf("usage: %s host:file host:file ... file, or\n", s); 554 printf(" %s file file ... file if connected\n", s); 555 } 556 557 void 558 setblksize(argc, argv) 559 int argc; 560 char *argv[]; 561 { 562 int t; 563 564 if (argc < 2) { 565 strcpy(line, "blksize "); 566 printf("(blksize) "); 567 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 568 makeargv(); 569 argc = margc; 570 argv = margv; 571 } 572 if (argc != 2) { 573 printf("usage: %s value\n", argv[0]); 574 return; 575 } 576 t = atoi(argv[1]); 577 if (t < 8 || t > 65464) 578 printf("%s: bad value\n", argv[1]); 579 else 580 blksize = t; 581 } 582 583 int def_rexmtval = TIMEOUT; 584 int rexmtval = TIMEOUT; 585 586 void 587 setrexmt(argc, argv) 588 int argc; 589 char *argv[]; 590 { 591 int t; 592 593 if (argc < 2) { 594 strcpy(line, "Rexmt-timeout "); 595 printf("(value) "); 596 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 597 makeargv(); 598 argc = margc; 599 argv = margv; 600 } 601 if (argc != 2) { 602 printf("usage: %s value\n", argv[0]); 603 return; 604 } 605 t = atoi(argv[1]); 606 if (t < 0) 607 printf("%s: bad value\n", argv[1]); 608 else 609 rexmtval = t; 610 } 611 612 int maxtimeout = 5 * TIMEOUT; 613 614 void 615 settimeout(argc, argv) 616 int argc; 617 char *argv[]; 618 { 619 int t; 620 621 if (argc < 2) { 622 strcpy(line, "Maximum-timeout "); 623 printf("(value) "); 624 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 625 makeargv(); 626 argc = margc; 627 argv = margv; 628 } 629 if (argc != 2) { 630 printf("usage: %s value\n", argv[0]); 631 return; 632 } 633 t = atoi(argv[1]); 634 if (t < 0) 635 printf("%s: bad value\n", argv[1]); 636 else 637 maxtimeout = t; 638 } 639 640 void 641 status(argc, argv) 642 int argc; 643 char *argv[]; 644 { 645 if (connected) 646 printf("Connected to %s.\n", hostname); 647 else 648 printf("Not connected.\n"); 649 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 650 verbose ? "on" : "off", trace ? "on" : "off"); 651 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 652 rexmtval, maxtimeout); 653 } 654 655 void 656 intr(dummy) 657 int dummy; 658 { 659 660 signal(SIGALRM, SIG_IGN); 661 alarm(0); 662 longjmp(toplevel, -1); 663 } 664 665 char * 666 tail(filename) 667 char *filename; 668 { 669 char *s; 670 671 while (*filename) { 672 s = strrchr(filename, '/'); 673 if (s == NULL) 674 break; 675 if (s[1]) 676 return (s + 1); 677 *s = '\0'; 678 } 679 return (filename); 680 } 681 682 /* 683 * Command parser. 684 */ 685 static __dead void 686 command() 687 { 688 struct cmd *c; 689 690 for (;;) { 691 printf("%s> ", prompt); 692 if (fgets(line, LBUFLEN, stdin) == 0) { 693 if (feof(stdin)) { 694 exit(0); 695 } else { 696 continue; 697 } 698 } 699 if ((line[0] == 0) || (line[0] == '\n')) 700 continue; 701 makeargv(); 702 if (margc == 0) 703 continue; 704 c = getcmd(margv[0]); 705 if (c == (struct cmd *)-1) { 706 printf("?Ambiguous command\n"); 707 continue; 708 } 709 if (c == 0) { 710 printf("?Invalid command\n"); 711 continue; 712 } 713 (*c->handler)(margc, margv); 714 } 715 } 716 717 struct cmd * 718 getcmd(name) 719 char *name; 720 { 721 char *p, *q; 722 struct cmd *c, *found; 723 int nmatches, longest; 724 725 longest = 0; 726 nmatches = 0; 727 found = 0; 728 for (c = cmdtab; (p = c->name) != NULL; c++) { 729 for (q = name; *q == *p++; q++) 730 if (*q == 0) /* exact match? */ 731 return (c); 732 if (!*q) { /* the name was a prefix */ 733 if (q - name > longest) { 734 longest = q - name; 735 nmatches = 1; 736 found = c; 737 } else if (q - name == longest) 738 nmatches++; 739 } 740 } 741 if (nmatches > 1) 742 return ((struct cmd *)-1); 743 return (found); 744 } 745 746 /* 747 * Slice a string up into argc/argv. 748 */ 749 static void 750 makeargv() 751 { 752 char *cp; 753 char **argp = margv; 754 755 margc = 0; 756 for (cp = line; *cp;) { 757 while (isspace((unsigned char)*cp)) 758 cp++; 759 if (*cp == '\0') 760 break; 761 *argp++ = cp; 762 margc += 1; 763 while (*cp != '\0' && !isspace((unsigned char)*cp)) 764 cp++; 765 if (*cp == '\0') 766 break; 767 *cp++ = '\0'; 768 } 769 *argp++ = 0; 770 } 771 772 void 773 quit(argc, argv) 774 int argc; 775 char *argv[]; 776 { 777 778 exit(0); 779 } 780 781 /* 782 * Help command. 783 */ 784 void 785 help(argc, argv) 786 int argc; 787 char *argv[]; 788 { 789 struct cmd *c; 790 791 if (argc == 1) { 792 printf("Commands may be abbreviated. Commands are:\n\n"); 793 for (c = cmdtab; c->name; c++) 794 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 795 return; 796 } 797 while (--argc > 0) { 798 char *arg; 799 arg = *++argv; 800 c = getcmd(arg); 801 if (c == (struct cmd *)-1) 802 printf("?Ambiguous help command %s\n", arg); 803 else if (c == (struct cmd *)0) 804 printf("?Invalid help command %s\n", arg); 805 else 806 printf("%s\n", c->help); 807 } 808 } 809 810 void 811 settrace(argc, argv) 812 int argc; 813 char **argv; 814 { 815 trace = !trace; 816 printf("Packet tracing %s.\n", trace ? "on" : "off"); 817 } 818 819 void 820 setverbose(argc, argv) 821 int argc; 822 char **argv; 823 { 824 verbose = !verbose; 825 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 826 } 827 828 void 829 settsize(argc, argv) 830 int argc; 831 char **argv; 832 { 833 tsize = !tsize; 834 printf("Tsize mode %s.\n", tsize ? "on" : "off"); 835 } 836 837 void 838 settimeoutopt(argc, argv) 839 int argc; 840 char **argv; 841 { 842 tout = !tout; 843 printf("Timeout option %s.\n", tout ? "on" : "off"); 844 } 845