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 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 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 42 #endif /* not lint */ 43 44 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 45 46 /* 47 * TFTP User Program -- Command Interface. 48 */ 49 #include <sys/types.h> 50 #include <sys/socket.h> 51 #include <sys/file.h> 52 53 #include <netinet/in.h> 54 55 #include <arpa/inet.h> 56 57 #include <ctype.h> 58 #include <errno.h> 59 #include <netdb.h> 60 #include <setjmp.h> 61 #include <signal.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 67 #include "extern.h" 68 69 #define TIMEOUT 5 /* secs between rexmt's */ 70 71 struct sockaddr_in peeraddr; 72 int f; 73 short port; 74 int trace; 75 int verbose; 76 int connected; 77 char mode[32]; 78 char line[200]; 79 int margc; 80 char *margv[20]; 81 char *prompt = "tftp"; 82 jmp_buf toplevel; 83 void intr(); 84 struct servent *sp; 85 86 void get __P((int, char **)); 87 void help __P((int, char **)); 88 void modecmd __P((int, char **)); 89 void put __P((int, char **)); 90 void quit __P((int, char **)); 91 void setascii __P((int, char **)); 92 void setbinary __P((int, char **)); 93 void setpeer __P((int, char **)); 94 void setrexmt __P((int, char **)); 95 void settimeout __P((int, char **)); 96 void settrace __P((int, char **)); 97 void setverbose __P((int, char **)); 98 void status __P((int, char **)); 99 100 static void command __P((void)) __dead2; 101 102 static void getusage __P((char *)); 103 static void makeargv __P((void)); 104 static void putusage __P((char *)); 105 static void settftpmode __P((char *)); 106 107 #define HELPINDENT (sizeof("connect")) 108 109 struct cmd { 110 char *name; 111 char *help; 112 void (*handler) __P((int, char **)); 113 }; 114 115 char vhelp[] = "toggle verbose mode"; 116 char thelp[] = "toggle packet tracing"; 117 char chelp[] = "connect to remote tftp"; 118 char qhelp[] = "exit tftp"; 119 char hhelp[] = "print help information"; 120 char shelp[] = "send file"; 121 char rhelp[] = "receive file"; 122 char mhelp[] = "set file transfer mode"; 123 char sthelp[] = "show current status"; 124 char xhelp[] = "set per-packet retransmission timeout"; 125 char ihelp[] = "set total retransmission timeout"; 126 char ashelp[] = "set mode to netascii"; 127 char bnhelp[] = "set mode to octet"; 128 129 struct cmd cmdtab[] = { 130 { "connect", chelp, setpeer }, 131 { "mode", mhelp, modecmd }, 132 { "put", shelp, put }, 133 { "get", rhelp, get }, 134 { "quit", qhelp, quit }, 135 { "verbose", vhelp, setverbose }, 136 { "trace", thelp, settrace }, 137 { "status", sthelp, status }, 138 { "binary", bnhelp, setbinary }, 139 { "ascii", ashelp, setascii }, 140 { "rexmt", xhelp, setrexmt }, 141 { "timeout", ihelp, settimeout }, 142 { "?", hhelp, help }, 143 { 0 } 144 }; 145 146 struct cmd *getcmd(); 147 char *tail(); 148 char *index(); 149 char *rindex(); 150 151 int 152 main(argc, argv) 153 int argc; 154 char *argv[]; 155 { 156 struct sockaddr_in sin; 157 158 sp = getservbyname("tftp", "udp"); 159 if (sp == 0) { 160 fprintf(stderr, "tftp: udp/tftp: unknown service\n"); 161 exit(1); 162 } 163 f = socket(AF_INET, SOCK_DGRAM, 0); 164 if (f < 0) { 165 perror("tftp: socket"); 166 exit(3); 167 } 168 bzero((char *)&sin, sizeof(sin)); 169 sin.sin_family = AF_INET; 170 if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 171 perror("tftp: bind"); 172 exit(1); 173 } 174 strcpy(mode, "netascii"); 175 signal(SIGINT, intr); 176 if (argc > 1) { 177 if (setjmp(toplevel) != 0) 178 exit(0); 179 setpeer(argc, argv); 180 } 181 if (setjmp(toplevel) != 0) 182 (void)putchar('\n'); 183 command(); 184 } 185 186 char hostname[100]; 187 188 void 189 setpeer(argc, argv) 190 int argc; 191 char *argv[]; 192 { 193 struct hostent *host; 194 195 if (argc < 2) { 196 strcpy(line, "Connect "); 197 printf("(to) "); 198 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 199 makeargv(); 200 argc = margc; 201 argv = margv; 202 } 203 if (argc > 3) { 204 printf("usage: %s host-name [port]\n", argv[0]); 205 return; 206 } 207 host = gethostbyname(argv[1]); 208 if (host) { 209 peeraddr.sin_family = host->h_addrtype; 210 bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); 211 strcpy(hostname, host->h_name); 212 } else { 213 peeraddr.sin_family = AF_INET; 214 peeraddr.sin_addr.s_addr = inet_addr(argv[1]); 215 if (peeraddr.sin_addr.s_addr == -1) { 216 connected = 0; 217 printf("%s: unknown host\n", argv[1]); 218 return; 219 } 220 strcpy(hostname, argv[1]); 221 } 222 port = sp->s_port; 223 if (argc == 3) { 224 port = atoi(argv[2]); 225 if (port < 0) { 226 printf("%s: bad port number\n", argv[2]); 227 connected = 0; 228 return; 229 } 230 port = htons(port); 231 } 232 connected = 1; 233 } 234 235 struct modes { 236 char *m_name; 237 char *m_mode; 238 } modes[] = { 239 { "ascii", "netascii" }, 240 { "netascii", "netascii" }, 241 { "binary", "octet" }, 242 { "image", "octet" }, 243 { "octet", "octet" }, 244 /* { "mail", "mail" }, */ 245 { 0, 0 } 246 }; 247 248 void 249 modecmd(argc, argv) 250 int argc; 251 char *argv[]; 252 { 253 register struct modes *p; 254 char *sep; 255 256 if (argc < 2) { 257 printf("Using %s mode to transfer files.\n", mode); 258 return; 259 } 260 if (argc == 2) { 261 for (p = modes; p->m_name; p++) 262 if (strcmp(argv[1], p->m_name) == 0) 263 break; 264 if (p->m_name) { 265 settftpmode(p->m_mode); 266 return; 267 } 268 printf("%s: unknown mode\n", argv[1]); 269 /* drop through and print usage message */ 270 } 271 272 printf("usage: %s [", argv[0]); 273 sep = " "; 274 for (p = modes; p->m_name; p++) { 275 printf("%s%s", sep, p->m_name); 276 if (*sep == ' ') 277 sep = " | "; 278 } 279 printf(" ]\n"); 280 return; 281 } 282 283 void 284 setbinary(argc, argv) 285 int argc; 286 char *argv[]; 287 { 288 289 settftpmode("octet"); 290 } 291 292 void 293 setascii(argc, argv) 294 int argc; 295 char *argv[]; 296 { 297 298 settftpmode("netascii"); 299 } 300 301 static void 302 settftpmode(newmode) 303 char *newmode; 304 { 305 strcpy(mode, newmode); 306 if (verbose) 307 printf("mode set to %s\n", mode); 308 } 309 310 311 /* 312 * Send file(s). 313 */ 314 void 315 put(argc, argv) 316 int argc; 317 char *argv[]; 318 { 319 int fd; 320 register int n; 321 register char *cp, *targ; 322 323 if (argc < 2) { 324 strcpy(line, "send "); 325 printf("(file) "); 326 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 327 makeargv(); 328 argc = margc; 329 argv = margv; 330 } 331 if (argc < 2) { 332 putusage(argv[0]); 333 return; 334 } 335 targ = argv[argc - 1]; 336 if (index(argv[argc - 1], ':')) { 337 char *cp; 338 struct hostent *hp; 339 340 for (n = 1; n < argc - 1; n++) 341 if (index(argv[n], ':')) { 342 putusage(argv[0]); 343 return; 344 } 345 cp = argv[argc - 1]; 346 targ = index(cp, ':'); 347 *targ++ = 0; 348 hp = gethostbyname(cp); 349 if (hp == NULL) { 350 fprintf(stderr, "tftp: %s: ", cp); 351 herror((char *)NULL); 352 return; 353 } 354 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); 355 peeraddr.sin_family = hp->h_addrtype; 356 connected = 1; 357 strcpy(hostname, hp->h_name); 358 } 359 if (!connected) { 360 printf("No target machine specified.\n"); 361 return; 362 } 363 if (argc < 4) { 364 cp = argc == 2 ? tail(targ) : argv[1]; 365 fd = open(cp, O_RDONLY); 366 if (fd < 0) { 367 fprintf(stderr, "tftp: "); perror(cp); 368 return; 369 } 370 if (verbose) 371 printf("putting %s to %s:%s [%s]\n", 372 cp, hostname, targ, mode); 373 peeraddr.sin_port = port; 374 sendfile(fd, targ, mode); 375 return; 376 } 377 /* this assumes the target is a directory */ 378 /* on a remote unix system. hmmmm. */ 379 cp = index(targ, '\0'); 380 *cp++ = '/'; 381 for (n = 1; n < argc - 1; n++) { 382 strcpy(cp, tail(argv[n])); 383 fd = open(argv[n], O_RDONLY); 384 if (fd < 0) { 385 fprintf(stderr, "tftp: "); perror(argv[n]); 386 continue; 387 } 388 if (verbose) 389 printf("putting %s to %s:%s [%s]\n", 390 argv[n], hostname, targ, mode); 391 peeraddr.sin_port = port; 392 sendfile(fd, targ, mode); 393 } 394 } 395 396 static void 397 putusage(s) 398 char *s; 399 { 400 printf("usage: %s file ... host:target, or\n", s); 401 printf(" %s file ... target (when already connected)\n", s); 402 } 403 404 /* 405 * Receive file(s). 406 */ 407 void 408 get(argc, argv) 409 int argc; 410 char *argv[]; 411 { 412 int fd; 413 register int n; 414 register char *cp; 415 char *src; 416 417 if (argc < 2) { 418 strcpy(line, "get "); 419 printf("(files) "); 420 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 421 makeargv(); 422 argc = margc; 423 argv = margv; 424 } 425 if (argc < 2) { 426 getusage(argv[0]); 427 return; 428 } 429 if (!connected) { 430 for (n = 1; n < argc ; n++) 431 if (index(argv[n], ':') == 0) { 432 getusage(argv[0]); 433 return; 434 } 435 } 436 for (n = 1; n < argc ; n++) { 437 src = index(argv[n], ':'); 438 if (src == NULL) 439 src = argv[n]; 440 else { 441 struct hostent *hp; 442 443 *src++ = 0; 444 hp = gethostbyname(argv[n]); 445 if (hp == NULL) { 446 fprintf(stderr, "tftp: %s: ", argv[n]); 447 herror((char *)NULL); 448 continue; 449 } 450 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 451 hp->h_length); 452 peeraddr.sin_family = hp->h_addrtype; 453 connected = 1; 454 strcpy(hostname, hp->h_name); 455 } 456 if (argc < 4) { 457 cp = argc == 3 ? argv[2] : tail(src); 458 fd = creat(cp, 0644); 459 if (fd < 0) { 460 fprintf(stderr, "tftp: "); perror(cp); 461 return; 462 } 463 if (verbose) 464 printf("getting from %s:%s to %s [%s]\n", 465 hostname, src, cp, mode); 466 peeraddr.sin_port = port; 467 recvfile(fd, src, mode); 468 break; 469 } 470 cp = tail(src); /* new .. jdg */ 471 fd = creat(cp, 0644); 472 if (fd < 0) { 473 fprintf(stderr, "tftp: "); perror(cp); 474 continue; 475 } 476 if (verbose) 477 printf("getting from %s:%s to %s [%s]\n", 478 hostname, src, cp, mode); 479 peeraddr.sin_port = port; 480 recvfile(fd, src, mode); 481 } 482 } 483 484 static void 485 getusage(s) 486 char *s; 487 { 488 printf("usage: %s host:file host:file ... file, or\n", s); 489 printf(" %s file file ... file if connected\n", s); 490 } 491 492 int rexmtval = TIMEOUT; 493 494 void 495 setrexmt(argc, argv) 496 int argc; 497 char *argv[]; 498 { 499 int t; 500 501 if (argc < 2) { 502 strcpy(line, "Rexmt-timeout "); 503 printf("(value) "); 504 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 505 makeargv(); 506 argc = margc; 507 argv = margv; 508 } 509 if (argc != 2) { 510 printf("usage: %s value\n", argv[0]); 511 return; 512 } 513 t = atoi(argv[1]); 514 if (t < 0) 515 printf("%s: bad value\n", argv[1]); 516 else 517 rexmtval = t; 518 } 519 520 int maxtimeout = 5 * TIMEOUT; 521 522 void 523 settimeout(argc, argv) 524 int argc; 525 char *argv[]; 526 { 527 int t; 528 529 if (argc < 2) { 530 strcpy(line, "Maximum-timeout "); 531 printf("(value) "); 532 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 533 makeargv(); 534 argc = margc; 535 argv = margv; 536 } 537 if (argc != 2) { 538 printf("usage: %s value\n", argv[0]); 539 return; 540 } 541 t = atoi(argv[1]); 542 if (t < 0) 543 printf("%s: bad value\n", argv[1]); 544 else 545 maxtimeout = t; 546 } 547 548 void 549 status(argc, argv) 550 int argc; 551 char *argv[]; 552 { 553 if (connected) 554 printf("Connected to %s.\n", hostname); 555 else 556 printf("Not connected.\n"); 557 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 558 verbose ? "on" : "off", trace ? "on" : "off"); 559 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 560 rexmtval, maxtimeout); 561 } 562 563 void 564 intr() 565 { 566 567 signal(SIGALRM, SIG_IGN); 568 alarm(0); 569 longjmp(toplevel, -1); 570 } 571 572 char * 573 tail(filename) 574 char *filename; 575 { 576 register char *s; 577 578 while (*filename) { 579 s = rindex(filename, '/'); 580 if (s == NULL) 581 break; 582 if (s[1]) 583 return (s + 1); 584 *s = '\0'; 585 } 586 return (filename); 587 } 588 589 /* 590 * Command parser. 591 */ 592 static void 593 command() 594 { 595 register struct cmd *c; 596 char *cp; 597 598 for (;;) { 599 printf("%s> ", prompt); 600 if (fgets(line, sizeof line , stdin) == 0) { 601 if (feof(stdin)) { 602 exit(0); 603 } else { 604 continue; 605 } 606 } 607 if ((cp = strchr(line, '\n'))) 608 *cp = '\0'; 609 if (line[0] == 0) 610 continue; 611 makeargv(); 612 if (margc == 0) 613 continue; 614 c = getcmd(margv[0]); 615 if (c == (struct cmd *)-1) { 616 printf("?Ambiguous command\n"); 617 continue; 618 } 619 if (c == 0) { 620 printf("?Invalid command\n"); 621 continue; 622 } 623 (*c->handler)(margc, margv); 624 } 625 } 626 627 struct cmd * 628 getcmd(name) 629 register char *name; 630 { 631 register char *p, *q; 632 register struct cmd *c, *found; 633 register int nmatches, longest; 634 635 longest = 0; 636 nmatches = 0; 637 found = 0; 638 for (c = cmdtab; (p = c->name) != NULL; c++) { 639 for (q = name; *q == *p++; q++) 640 if (*q == 0) /* exact match? */ 641 return (c); 642 if (!*q) { /* the name was a prefix */ 643 if (q - name > longest) { 644 longest = q - name; 645 nmatches = 1; 646 found = c; 647 } else if (q - name == longest) 648 nmatches++; 649 } 650 } 651 if (nmatches > 1) 652 return ((struct cmd *)-1); 653 return (found); 654 } 655 656 /* 657 * Slice a string up into argc/argv. 658 */ 659 static void 660 makeargv() 661 { 662 register char *cp; 663 register char **argp = margv; 664 665 margc = 0; 666 if ((cp = strchr(line, '\n'))) 667 *cp = '\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