1 /* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert 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.4 1997/01/17 07:13:30 millert 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 73 #include "extern.h" 74 75 #define TIMEOUT 5 /* secs between rexmt's */ 76 #define LBUFLEN 200 /* size of input buffer */ 77 78 struct sockaddr_in peeraddr; 79 int f; 80 short port; 81 int trace; 82 int verbose; 83 int connected; 84 char mode[32]; 85 char line[LBUFLEN]; 86 int margc; 87 char *margv[20]; 88 char *prompt = "tftp"; 89 jmp_buf toplevel; 90 void intr(); 91 struct servent *sp; 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 setpeer __P((int, char **)); 101 void setrexmt __P((int, char **)); 102 void settimeout __P((int, char **)); 103 void settrace __P((int, char **)); 104 void setverbose __P((int, char **)); 105 void status __P((int, char **)); 106 107 static __dead void command __P((void)); 108 109 static void getusage __P((char *)); 110 static void makeargv __P((void)); 111 static void putusage __P((char *)); 112 static void settftpmode __P((char *)); 113 114 #define HELPINDENT (sizeof("connect")) 115 116 struct cmd { 117 char *name; 118 char *help; 119 void (*handler) __P((int, char **)); 120 }; 121 122 char vhelp[] = "toggle verbose mode"; 123 char thelp[] = "toggle packet tracing"; 124 char chelp[] = "connect to remote tftp"; 125 char qhelp[] = "exit tftp"; 126 char hhelp[] = "print help information"; 127 char shelp[] = "send file"; 128 char rhelp[] = "receive file"; 129 char mhelp[] = "set file transfer mode"; 130 char sthelp[] = "show current status"; 131 char xhelp[] = "set per-packet retransmission timeout"; 132 char ihelp[] = "set total retransmission timeout"; 133 char ashelp[] = "set mode to netascii"; 134 char bnhelp[] = "set mode to octet"; 135 136 struct cmd cmdtab[] = { 137 { "connect", chelp, setpeer }, 138 { "mode", mhelp, modecmd }, 139 { "put", shelp, put }, 140 { "get", rhelp, get }, 141 { "quit", qhelp, quit }, 142 { "verbose", vhelp, setverbose }, 143 { "trace", thelp, settrace }, 144 { "status", sthelp, status }, 145 { "binary", bnhelp, setbinary }, 146 { "ascii", ashelp, setascii }, 147 { "rexmt", xhelp, setrexmt }, 148 { "timeout", ihelp, settimeout }, 149 { "?", hhelp, help }, 150 { 0 } 151 }; 152 153 struct cmd *getcmd(); 154 char *tail(); 155 156 int 157 main(argc, argv) 158 int argc; 159 char *argv[]; 160 { 161 struct sockaddr_in s_in; 162 163 sp = getservbyname("tftp", "udp"); 164 if (sp == 0) { 165 fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 166 exit(1); 167 } 168 f = socket(AF_INET, SOCK_DGRAM, 0); 169 if (f < 0) { 170 perror("tftp: socket"); 171 exit(3); 172 } 173 bzero((char *)&s_in, sizeof (s_in)); 174 s_in.sin_family = AF_INET; 175 if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { 176 perror("tftp: bind"); 177 exit(1); 178 } 179 strcpy(mode, "netascii"); 180 signal(SIGINT, intr); 181 if (argc > 1) { 182 if (setjmp(toplevel) != 0) 183 exit(0); 184 setpeer(argc, argv); 185 } 186 if (setjmp(toplevel) != 0) 187 (void)putchar('\n'); 188 command(); 189 } 190 191 char hostname[MAXHOSTNAMELEN]; 192 193 void 194 setpeer(argc, argv) 195 int argc; 196 char *argv[]; 197 { 198 struct hostent *host; 199 200 if (argc < 2) { 201 strcpy(line, "Connect "); 202 printf("(to) "); 203 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 204 makeargv(); 205 argc = margc; 206 argv = margv; 207 } 208 if ((argc < 2) || (argc > 3)) { 209 printf("usage: %s host-name [port]\n", argv[0]); 210 return; 211 } 212 if (inet_aton(argv[1], &peeraddr.sin_addr) != 0) { 213 peeraddr.sin_family = AF_INET; 214 (void) strncpy(hostname, argv[1], sizeof hostname); 215 hostname[sizeof(hostname)-1] = '\0'; 216 } else { 217 host = gethostbyname(argv[1]); 218 if (host == 0) { 219 connected = 0; 220 printf("%s: unknown host\n", argv[1]); 221 return; 222 } 223 peeraddr.sin_family = host->h_addrtype; 224 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); 225 (void) strcpy(hostname, host->h_name); 226 } 227 port = sp->s_port; 228 if (argc == 3) { 229 port = atoi(argv[2]); 230 if (port < 0) { 231 printf("%s: bad port number\n", argv[2]); 232 connected = 0; 233 return; 234 } 235 port = htons(port); 236 } 237 connected = 1; 238 } 239 240 struct modes { 241 char *m_name; 242 char *m_mode; 243 } modes[] = { 244 { "ascii", "netascii" }, 245 { "netascii", "netascii" }, 246 { "binary", "octet" }, 247 { "image", "octet" }, 248 { "octet", "octet" }, 249 /* { "mail", "mail" }, */ 250 { 0, 0 } 251 }; 252 253 void 254 modecmd(argc, argv) 255 int argc; 256 char *argv[]; 257 { 258 register struct modes *p; 259 char *sep; 260 261 if (argc < 2) { 262 printf("Using %s mode to transfer files.\n", mode); 263 return; 264 } 265 if (argc == 2) { 266 for (p = modes; p->m_name; p++) 267 if (strcmp(argv[1], p->m_name) == 0) 268 break; 269 if (p->m_name) { 270 settftpmode(p->m_mode); 271 return; 272 } 273 printf("%s: unknown mode\n", argv[1]); 274 /* drop through and print usage message */ 275 } 276 277 printf("usage: %s [", argv[0]); 278 sep = " "; 279 for (p = modes; p->m_name; p++) { 280 printf("%s%s", sep, p->m_name); 281 if (*sep == ' ') 282 sep = " | "; 283 } 284 printf(" ]\n"); 285 return; 286 } 287 288 void 289 setbinary(argc, argv) 290 int argc; 291 char *argv[]; 292 { 293 294 settftpmode("octet"); 295 } 296 297 void 298 setascii(argc, argv) 299 int argc; 300 char *argv[]; 301 { 302 303 settftpmode("netascii"); 304 } 305 306 static void 307 settftpmode(newmode) 308 char *newmode; 309 { 310 strcpy(mode, newmode); 311 if (verbose) 312 printf("mode set to %s\n", mode); 313 } 314 315 316 /* 317 * Send file(s). 318 */ 319 void 320 put(argc, argv) 321 int argc; 322 char *argv[]; 323 { 324 int fd; 325 register int n; 326 register char *cp, *targ; 327 328 if (argc < 2) { 329 strcpy(line, "send "); 330 printf("(file) "); 331 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 332 makeargv(); 333 argc = margc; 334 argv = margv; 335 } 336 if (argc < 2) { 337 putusage(argv[0]); 338 return; 339 } 340 targ = argv[argc - 1]; 341 if (strchr(argv[argc - 1], ':')) { 342 char *cp; 343 struct hostent *hp; 344 345 for (n = 1; n < argc - 1; n++) 346 if (strchr(argv[n], ':')) { 347 putusage(argv[0]); 348 return; 349 } 350 cp = argv[argc - 1]; 351 targ = strchr(cp, ':'); 352 *targ++ = 0; 353 hp = gethostbyname(cp); 354 if (hp == NULL) { 355 fprintf(stderr, "tftp: %s: ", cp); 356 herror((char *)NULL); 357 return; 358 } 359 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); 360 peeraddr.sin_family = hp->h_addrtype; 361 connected = 1; 362 strcpy(hostname, hp->h_name); 363 } 364 if (!connected) { 365 printf("No target machine specified.\n"); 366 return; 367 } 368 if (argc < 4) { 369 cp = argc == 2 ? tail(targ) : argv[1]; 370 fd = open(cp, O_RDONLY); 371 if (fd < 0) { 372 fprintf(stderr, "tftp: "); perror(cp); 373 return; 374 } 375 if (verbose) 376 printf("putting %s to %s:%s [%s]\n", 377 cp, hostname, targ, mode); 378 peeraddr.sin_port = port; 379 sendfile(fd, targ, mode); 380 return; 381 } 382 /* this assumes the target is a directory */ 383 /* on a remote unix system. hmmmm. */ 384 cp = strchr(targ, '\0'); 385 *cp++ = '/'; 386 for (n = 1; n < argc - 1; n++) { 387 strcpy(cp, tail(argv[n])); 388 fd = open(argv[n], O_RDONLY); 389 if (fd < 0) { 390 fprintf(stderr, "tftp: "); perror(argv[n]); 391 continue; 392 } 393 if (verbose) 394 printf("putting %s to %s:%s [%s]\n", 395 argv[n], hostname, targ, mode); 396 peeraddr.sin_port = port; 397 sendfile(fd, targ, mode); 398 } 399 } 400 401 static void 402 putusage(s) 403 char *s; 404 { 405 printf("usage: %s file ... host:target, or\n", s); 406 printf(" %s file ... target (when already connected)\n", s); 407 } 408 409 /* 410 * Receive file(s). 411 */ 412 void 413 get(argc, argv) 414 int argc; 415 char *argv[]; 416 { 417 int fd; 418 register int n; 419 register char *cp; 420 char *src; 421 422 if (argc < 2) { 423 strcpy(line, "get "); 424 printf("(files) "); 425 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 426 makeargv(); 427 argc = margc; 428 argv = margv; 429 } 430 if (argc < 2) { 431 getusage(argv[0]); 432 return; 433 } 434 if (!connected) { 435 for (n = 1; n < argc ; n++) 436 if (strchr(argv[n], ':') == 0) { 437 getusage(argv[0]); 438 return; 439 } 440 } 441 for (n = 1; n < argc ; n++) { 442 src = strchr(argv[n], ':'); 443 if (src == NULL) 444 src = argv[n]; 445 else { 446 struct hostent *hp; 447 448 *src++ = 0; 449 hp = gethostbyname(argv[n]); 450 if (hp == NULL) { 451 fprintf(stderr, "tftp: %s: ", argv[n]); 452 herror((char *)NULL); 453 continue; 454 } 455 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 456 hp->h_length); 457 peeraddr.sin_family = hp->h_addrtype; 458 connected = 1; 459 strcpy(hostname, hp->h_name); 460 } 461 if (argc < 4) { 462 cp = argc == 3 ? argv[2] : tail(src); 463 fd = creat(cp, 0644); 464 if (fd < 0) { 465 fprintf(stderr, "tftp: "); perror(cp); 466 return; 467 } 468 if (verbose) 469 printf("getting from %s:%s to %s [%s]\n", 470 hostname, src, cp, mode); 471 peeraddr.sin_port = port; 472 recvfile(fd, src, mode); 473 break; 474 } 475 cp = tail(src); /* new .. jdg */ 476 fd = creat(cp, 0644); 477 if (fd < 0) { 478 fprintf(stderr, "tftp: "); perror(cp); 479 continue; 480 } 481 if (verbose) 482 printf("getting from %s:%s to %s [%s]\n", 483 hostname, src, cp, mode); 484 peeraddr.sin_port = port; 485 recvfile(fd, src, mode); 486 } 487 } 488 489 static void 490 getusage(s) 491 char *s; 492 { 493 printf("usage: %s host:file host:file ... file, or\n", s); 494 printf(" %s file file ... file if connected\n", s); 495 } 496 497 int rexmtval = TIMEOUT; 498 499 void 500 setrexmt(argc, argv) 501 int argc; 502 char *argv[]; 503 { 504 int t; 505 506 if (argc < 2) { 507 strcpy(line, "Rexmt-timeout "); 508 printf("(value) "); 509 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 510 makeargv(); 511 argc = margc; 512 argv = margv; 513 } 514 if (argc != 2) { 515 printf("usage: %s value\n", argv[0]); 516 return; 517 } 518 t = atoi(argv[1]); 519 if (t < 0) 520 printf("%s: bad value\n", argv[1]); 521 else 522 rexmtval = t; 523 } 524 525 int maxtimeout = 5 * TIMEOUT; 526 527 void 528 settimeout(argc, argv) 529 int argc; 530 char *argv[]; 531 { 532 int t; 533 534 if (argc < 2) { 535 strcpy(line, "Maximum-timeout "); 536 printf("(value) "); 537 fgets(&line[strlen(line)], LBUFLEN-strlen(line), stdin); 538 makeargv(); 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 register 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 register 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 makeargv(); 614 if (margc == 0) 615 continue; 616 c = getcmd(margv[0]); 617 if (c == (struct cmd *)-1) { 618 printf("?Ambiguous command\n"); 619 continue; 620 } 621 if (c == 0) { 622 printf("?Invalid command\n"); 623 continue; 624 } 625 (*c->handler)(margc, margv); 626 } 627 } 628 629 struct cmd * 630 getcmd(name) 631 register char *name; 632 { 633 register char *p, *q; 634 register struct cmd *c, *found; 635 register int nmatches, longest; 636 637 longest = 0; 638 nmatches = 0; 639 found = 0; 640 for (c = cmdtab; (p = c->name) != NULL; c++) { 641 for (q = name; *q == *p++; q++) 642 if (*q == 0) /* exact match? */ 643 return (c); 644 if (!*q) { /* the name was a prefix */ 645 if (q - name > longest) { 646 longest = q - name; 647 nmatches = 1; 648 found = c; 649 } else if (q - name == longest) 650 nmatches++; 651 } 652 } 653 if (nmatches > 1) 654 return ((struct cmd *)-1); 655 return (found); 656 } 657 658 /* 659 * Slice a string up into argc/argv. 660 */ 661 static void 662 makeargv() 663 { 664 register char *cp; 665 register char **argp = margv; 666 667 margc = 0; 668 for (cp = line; *cp;) { 669 while (isspace(*cp)) 670 cp++; 671 if (*cp == '\0') 672 break; 673 *argp++ = cp; 674 margc += 1; 675 while (*cp != '\0' && !isspace(*cp)) 676 cp++; 677 if (*cp == '\0') 678 break; 679 *cp++ = '\0'; 680 } 681 *argp++ = 0; 682 } 683 684 void 685 quit(argc, argv) 686 int argc; 687 char *argv[]; 688 { 689 690 exit(0); 691 } 692 693 /* 694 * Help command. 695 */ 696 void 697 help(argc, argv) 698 int argc; 699 char *argv[]; 700 { 701 register struct cmd *c; 702 703 if (argc == 1) { 704 printf("Commands may be abbreviated. Commands are:\n\n"); 705 for (c = cmdtab; c->name; c++) 706 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 707 return; 708 } 709 while (--argc > 0) { 710 register char *arg; 711 arg = *++argv; 712 c = getcmd(arg); 713 if (c == (struct cmd *)-1) 714 printf("?Ambiguous help command %s\n", arg); 715 else if (c == (struct cmd *)0) 716 printf("?Invalid help command %s\n", arg); 717 else 718 printf("%s\n", c->help); 719 } 720 } 721 722 void 723 settrace(argc, argv) 724 int argc; 725 char **argv; 726 { 727 trace = !trace; 728 printf("Packet tracing %s.\n", trace ? "on" : "off"); 729 } 730 731 void 732 setverbose(argc, argv) 733 int argc; 734 char **argv; 735 { 736 verbose = !verbose; 737 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 738 } 739