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