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