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