1 /* $NetBSD: main.c,v 1.15 2003/06/11 01:44:32 briggs 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.15 2003/06/11 01:44:32 briggs 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) strncpy(hostname, res->ai_canonname, 283 sizeof(hostname)); 284 } else 285 (void) strncpy(hostname, host, sizeof(hostname)); 286 hostname[sizeof(hostname)-1] = 0; 287 connected = 1; 288 } 289 290 freeaddrinfo(res0); 291 } 292 293 void 294 setpeer(argc, argv) 295 int argc; 296 char *argv[]; 297 { 298 299 if (argc < 1) { 300 strcpy(line, "Connect "); 301 printf("(to) "); 302 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 303 makeargv(); 304 argc = margc; 305 argv = margv; 306 } 307 if ((argc < 1) || (argc > 2)) { 308 printf("usage: %s [-e] host-name [port]\n", getprogname()); 309 return; 310 } 311 if (argc == 1) 312 setpeer0(argv[0], NULL); 313 else 314 setpeer0(argv[0], argv[1]); 315 } 316 317 struct modes { 318 char *m_name; 319 char *m_mode; 320 } modes[] = { 321 { "ascii", "netascii" }, 322 { "netascii", "netascii" }, 323 { "binary", "octet" }, 324 { "image", "octet" }, 325 { "octet", "octet" }, 326 /* { "mail", "mail" }, */ 327 { 0, 0 } 328 }; 329 330 void 331 modecmd(argc, argv) 332 int argc; 333 char *argv[]; 334 { 335 struct modes *p; 336 char *sep; 337 338 if (argc < 2) { 339 printf("Using %s mode to transfer files.\n", mode); 340 return; 341 } 342 if (argc == 2) { 343 for (p = modes; p->m_name; p++) 344 if (strcmp(argv[1], p->m_name) == 0) 345 break; 346 if (p->m_name) { 347 settftpmode(p->m_mode); 348 return; 349 } 350 printf("%s: unknown mode\n", argv[1]); 351 /* drop through and print usage message */ 352 } 353 354 printf("usage: %s [", argv[0]); 355 sep = " "; 356 for (p = modes; p->m_name; p++) { 357 printf("%s%s", sep, p->m_name); 358 if (*sep == ' ') 359 sep = " | "; 360 } 361 printf(" ]\n"); 362 return; 363 } 364 365 void 366 setbinary(argc, argv) 367 int argc; 368 char *argv[]; 369 { 370 371 settftpmode("octet"); 372 } 373 374 void 375 setascii(argc, argv) 376 int argc; 377 char *argv[]; 378 { 379 380 settftpmode("netascii"); 381 } 382 383 static void 384 settftpmode(newmode) 385 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 *cp, *targ; 404 405 if (argc < 2) { 406 strcpy(line, "send "); 407 printf("(file) "); 408 fgets(&line[strlen(line)], 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 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 cp = strchr(targ, '\0'); 455 *cp++ = '/'; 456 for (n = 1; n < argc - 1; n++) { 457 strcpy(cp, 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 *cp; 489 char *src; 490 491 if (argc < 2) { 492 strcpy(line, "get "); 493 printf("(files) "); 494 fgets(&line[strlen(line)], 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 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 cp = tail(src); /* new .. jdg */ 540 fd = creat(cp, 0644); 541 if (fd < 0) { 542 warn("%s", cp); 543 continue; 544 } 545 if (verbose) 546 printf("getting from %s:%s to %s [%s]\n", 547 hostname, src, cp, 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)], 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 int def_rexmtval = TIMEOUT; 587 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)], 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)], 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 status(argc, argv) 645 int argc; 646 char *argv[]; 647 { 648 if (connected) 649 printf("Connected to %s.\n", hostname); 650 else 651 printf("Not connected.\n"); 652 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 653 verbose ? "on" : "off", trace ? "on" : "off"); 654 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 655 rexmtval, maxtimeout); 656 } 657 658 void 659 intr(dummy) 660 int dummy; 661 { 662 663 signal(SIGALRM, SIG_IGN); 664 alarm(0); 665 longjmp(toplevel, -1); 666 } 667 668 char * 669 tail(filename) 670 char *filename; 671 { 672 char *s; 673 674 while (*filename) { 675 s = strrchr(filename, '/'); 676 if (s == NULL) 677 break; 678 if (s[1]) 679 return (s + 1); 680 *s = '\0'; 681 } 682 return (filename); 683 } 684 685 /* 686 * Command parser. 687 */ 688 static __dead void 689 command() 690 { 691 struct cmd *c; 692 693 for (;;) { 694 printf("%s> ", prompt); 695 if (fgets(line, LBUFLEN, stdin) == 0) { 696 if (feof(stdin)) { 697 exit(0); 698 } else { 699 continue; 700 } 701 } 702 if ((line[0] == 0) || (line[0] == '\n')) 703 continue; 704 makeargv(); 705 if (margc == 0) 706 continue; 707 c = getcmd(margv[0]); 708 if (c == (struct cmd *)-1) { 709 printf("?Ambiguous command\n"); 710 continue; 711 } 712 if (c == 0) { 713 printf("?Invalid command\n"); 714 continue; 715 } 716 (*c->handler)(margc, margv); 717 } 718 } 719 720 struct cmd * 721 getcmd(name) 722 char *name; 723 { 724 char *p, *q; 725 struct cmd *c, *found; 726 int nmatches, longest; 727 728 longest = 0; 729 nmatches = 0; 730 found = 0; 731 for (c = cmdtab; (p = c->name) != NULL; c++) { 732 for (q = name; *q == *p++; q++) 733 if (*q == 0) /* exact match? */ 734 return (c); 735 if (!*q) { /* the name was a prefix */ 736 if (q - name > longest) { 737 longest = q - name; 738 nmatches = 1; 739 found = c; 740 } else if (q - name == longest) 741 nmatches++; 742 } 743 } 744 if (nmatches > 1) 745 return ((struct cmd *)-1); 746 return (found); 747 } 748 749 /* 750 * Slice a string up into argc/argv. 751 */ 752 static void 753 makeargv() 754 { 755 char *cp; 756 char **argp = margv; 757 758 margc = 0; 759 for (cp = line; *cp;) { 760 while (isspace((unsigned char)*cp)) 761 cp++; 762 if (*cp == '\0') 763 break; 764 *argp++ = cp; 765 margc += 1; 766 while (*cp != '\0' && !isspace((unsigned char)*cp)) 767 cp++; 768 if (*cp == '\0') 769 break; 770 *cp++ = '\0'; 771 } 772 *argp++ = 0; 773 } 774 775 void 776 quit(argc, argv) 777 int argc; 778 char *argv[]; 779 { 780 781 exit(0); 782 } 783 784 /* 785 * Help command. 786 */ 787 void 788 help(argc, argv) 789 int argc; 790 char *argv[]; 791 { 792 struct cmd *c; 793 794 if (argc == 1) { 795 printf("Commands may be abbreviated. Commands are:\n\n"); 796 for (c = cmdtab; c->name; c++) 797 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 798 return; 799 } 800 while (--argc > 0) { 801 char *arg; 802 arg = *++argv; 803 c = getcmd(arg); 804 if (c == (struct cmd *)-1) 805 printf("?Ambiguous help command %s\n", arg); 806 else if (c == (struct cmd *)0) 807 printf("?Invalid help command %s\n", arg); 808 else 809 printf("%s\n", c->help); 810 } 811 } 812 813 void 814 settrace(argc, argv) 815 int argc; 816 char **argv; 817 { 818 trace = !trace; 819 printf("Packet tracing %s.\n", trace ? "on" : "off"); 820 } 821 822 void 823 setverbose(argc, argv) 824 int argc; 825 char **argv; 826 { 827 verbose = !verbose; 828 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 829 } 830 831 void 832 settsize(argc, argv) 833 int argc; 834 char **argv; 835 { 836 tsize = !tsize; 837 printf("Tsize mode %s.\n", tsize ? "on" : "off"); 838 } 839 840 void 841 settimeoutopt(argc, argv) 842 int argc; 843 char **argv; 844 { 845 tout = !tout; 846 printf("Timeout option %s.\n", tout ? "on" : "off"); 847 } 848