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