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: main.c,v 1.6 1998/06/09 04:31:02 imp Exp $"; 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 #include <sys/param.h> 58 59 #include <netinet/in.h> 60 61 #include <arpa/inet.h> 62 63 #include <ctype.h> 64 #include <err.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 77 struct sockaddr_in peeraddr; 78 int f; 79 short port; 80 int trace; 81 int verbose; 82 int connected; 83 char mode[32]; 84 char line[200]; 85 int margc; 86 char *margv[20]; 87 char *prompt = "tftp"; 88 jmp_buf toplevel; 89 void intr(); 90 struct servent *sp; 91 92 void get __P((int, char **)); 93 void help __P((int, char **)); 94 void modecmd __P((int, char **)); 95 void put __P((int, char **)); 96 void quit __P((int, char **)); 97 void setascii __P((int, char **)); 98 void setbinary __P((int, char **)); 99 void setpeer __P((int, char **)); 100 void setrexmt __P((int, char **)); 101 void settimeout __P((int, char **)); 102 void settrace __P((int, char **)); 103 void setverbose __P((int, char **)); 104 void status __P((int, char **)); 105 106 static void command __P((void)) __dead2; 107 108 static void getusage __P((char *)); 109 static void makeargv __P((void)); 110 static void putusage __P((char *)); 111 static void settftpmode __P((char *)); 112 113 #define HELPINDENT (sizeof("connect")) 114 115 struct cmd { 116 char *name; 117 char *help; 118 void (*handler) __P((int, char **)); 119 }; 120 121 char vhelp[] = "toggle verbose mode"; 122 char thelp[] = "toggle packet tracing"; 123 char chelp[] = "connect to remote tftp"; 124 char qhelp[] = "exit tftp"; 125 char hhelp[] = "print help information"; 126 char shelp[] = "send file"; 127 char rhelp[] = "receive file"; 128 char mhelp[] = "set file transfer mode"; 129 char sthelp[] = "show current status"; 130 char xhelp[] = "set per-packet retransmission timeout"; 131 char ihelp[] = "set total retransmission timeout"; 132 char ashelp[] = "set mode to netascii"; 133 char bnhelp[] = "set mode to octet"; 134 135 struct cmd cmdtab[] = { 136 { "connect", chelp, setpeer }, 137 { "mode", mhelp, modecmd }, 138 { "put", shelp, put }, 139 { "get", rhelp, get }, 140 { "quit", qhelp, quit }, 141 { "verbose", vhelp, setverbose }, 142 { "trace", thelp, settrace }, 143 { "status", sthelp, status }, 144 { "binary", bnhelp, setbinary }, 145 { "ascii", ashelp, setascii }, 146 { "rexmt", xhelp, setrexmt }, 147 { "timeout", ihelp, settimeout }, 148 { "?", hhelp, help }, 149 { 0 } 150 }; 151 152 struct cmd *getcmd(); 153 char *tail(); 154 155 int 156 main(argc, argv) 157 int argc; 158 char *argv[]; 159 { 160 struct sockaddr_in sin; 161 162 sp = getservbyname("tftp", "udp"); 163 if (sp == 0) 164 errx(1, "udp/tftp: unknown service"); 165 f = socket(AF_INET, SOCK_DGRAM, 0); 166 if (f < 0) 167 err(3, "socket"); 168 bzero((char *)&sin, sizeof(sin)); 169 sin.sin_family = AF_INET; 170 if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) 171 err(1, "bind"); 172 strcpy(mode, "netascii"); 173 signal(SIGINT, intr); 174 if (argc > 1) { 175 if (setjmp(toplevel) != 0) 176 exit(0); 177 setpeer(argc, argv); 178 } 179 if (setjmp(toplevel) != 0) 180 (void)putchar('\n'); 181 command(); 182 } 183 184 char hostname[MAXHOSTNAMELEN]; 185 186 void 187 setpeer(argc, argv) 188 int argc; 189 char *argv[]; 190 { 191 struct hostent *host; 192 193 if (argc < 2) { 194 strcpy(line, "Connect "); 195 printf("(to) "); 196 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 197 makeargv(); 198 argc = margc; 199 argv = margv; 200 } 201 if (argc > 3) { 202 printf("usage: %s host-name [port]\n", argv[0]); 203 return; 204 } 205 host = gethostbyname(argv[1]); 206 if (host) { 207 peeraddr.sin_family = host->h_addrtype; 208 bcopy(host->h_addr, &peeraddr.sin_addr, 209 MIN(sizeof(peeraddr.sin_addr), host->h_length)); 210 strncpy(hostname, host->h_name, sizeof(hostname)); 211 } else { 212 peeraddr.sin_family = AF_INET; 213 peeraddr.sin_addr.s_addr = inet_addr(argv[1]); 214 if (peeraddr.sin_addr.s_addr == -1) { 215 connected = 0; 216 printf("%s: unknown host\n", argv[1]); 217 return; 218 } 219 strncpy(hostname, argv[1], sizeof(hostname)); 220 } 221 hostname[sizeof(hostname) - 1] = '\0'; 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, 355 MIN(sizeof(peeraddr.sin_addr), hp->h_length)); 356 peeraddr.sin_family = hp->h_addrtype; 357 connected = 1; 358 strncpy(hostname, hp->h_name, sizeof(hostname)); 359 hostname[sizeof(hostname) - 1] = '\0'; 360 } 361 if (!connected) { 362 printf("No target machine specified.\n"); 363 return; 364 } 365 if (argc < 4) { 366 cp = argc == 2 ? tail(targ) : argv[1]; 367 fd = open(cp, O_RDONLY); 368 if (fd < 0) { 369 warn("%s", cp); 370 return; 371 } 372 if (verbose) 373 printf("putting %s to %s:%s [%s]\n", 374 cp, hostname, targ, mode); 375 peeraddr.sin_port = port; 376 xmitfile(fd, targ, mode); 377 return; 378 } 379 /* this assumes the target is a directory */ 380 /* on a remote unix system. hmmmm. */ 381 cp = index(targ, '\0'); 382 *cp++ = '/'; 383 for (n = 1; n < argc - 1; n++) { 384 strcpy(cp, tail(argv[n])); 385 fd = open(argv[n], O_RDONLY); 386 if (fd < 0) { 387 warn("%s", argv[n]); 388 continue; 389 } 390 if (verbose) 391 printf("putting %s to %s:%s [%s]\n", 392 argv[n], hostname, targ, mode); 393 peeraddr.sin_port = port; 394 xmitfile(fd, targ, mode); 395 } 396 } 397 398 static void 399 putusage(s) 400 char *s; 401 { 402 printf("usage: %s file ... host:target, or\n", s); 403 printf(" %s file ... target (when already connected)\n", s); 404 } 405 406 /* 407 * Receive file(s). 408 */ 409 void 410 get(argc, argv) 411 int argc; 412 char *argv[]; 413 { 414 int fd; 415 register int n; 416 register char *cp; 417 char *src; 418 419 if (argc < 2) { 420 strcpy(line, "get "); 421 printf("(files) "); 422 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 423 makeargv(); 424 argc = margc; 425 argv = margv; 426 } 427 if (argc < 2) { 428 getusage(argv[0]); 429 return; 430 } 431 if (!connected) { 432 for (n = 1; n < argc ; n++) 433 if (index(argv[n], ':') == 0) { 434 getusage(argv[0]); 435 return; 436 } 437 } 438 for (n = 1; n < argc ; n++) { 439 src = index(argv[n], ':'); 440 if (src == NULL) 441 src = argv[n]; 442 else { 443 struct hostent *hp; 444 445 *src++ = 0; 446 hp = gethostbyname(argv[n]); 447 if (hp == NULL) { 448 fprintf(stderr, "tftp: %s: ", argv[n]); 449 herror((char *)NULL); 450 continue; 451 } 452 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 453 MIN(sizeof(peeraddr.sin_addr), hp->h_length)); 454 peeraddr.sin_family = hp->h_addrtype; 455 connected = 1; 456 strncpy(hostname, hp->h_name, sizeof(hostname)); 457 hostname[sizeof(hostname) - 1] = '\0'; 458 } 459 if (argc < 4) { 460 cp = argc == 3 ? argv[2] : tail(src); 461 fd = creat(cp, 0644); 462 if (fd < 0) { 463 warn("%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("%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)], sizeof line - strlen(line), stdin); 508 makeargv(); 509 argc = margc; 510 argv = margv; 511 } 512 if (argc != 2) { 513 printf("usage: %s value\n", argv[0]); 514 return; 515 } 516 t = atoi(argv[1]); 517 if (t < 0) 518 printf("%s: bad value\n", argv[1]); 519 else 520 rexmtval = t; 521 } 522 523 int maxtimeout = 5 * TIMEOUT; 524 525 void 526 settimeout(argc, argv) 527 int argc; 528 char *argv[]; 529 { 530 int t; 531 532 if (argc < 2) { 533 strcpy(line, "Maximum-timeout "); 534 printf("(value) "); 535 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 536 makeargv(); 537 argc = margc; 538 argv = margv; 539 } 540 if (argc != 2) { 541 printf("usage: %s value\n", argv[0]); 542 return; 543 } 544 t = atoi(argv[1]); 545 if (t < 0) 546 printf("%s: bad value\n", argv[1]); 547 else 548 maxtimeout = t; 549 } 550 551 void 552 status(argc, argv) 553 int argc; 554 char *argv[]; 555 { 556 if (connected) 557 printf("Connected to %s.\n", hostname); 558 else 559 printf("Not connected.\n"); 560 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 561 verbose ? "on" : "off", trace ? "on" : "off"); 562 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 563 rexmtval, maxtimeout); 564 } 565 566 void 567 intr() 568 { 569 570 signal(SIGALRM, SIG_IGN); 571 alarm(0); 572 longjmp(toplevel, -1); 573 } 574 575 char * 576 tail(filename) 577 char *filename; 578 { 579 register char *s; 580 581 while (*filename) { 582 s = rindex(filename, '/'); 583 if (s == NULL) 584 break; 585 if (s[1]) 586 return (s + 1); 587 *s = '\0'; 588 } 589 return (filename); 590 } 591 592 /* 593 * Command parser. 594 */ 595 static void 596 command() 597 { 598 register struct cmd *c; 599 char *cp; 600 601 for (;;) { 602 printf("%s> ", prompt); 603 if (fgets(line, sizeof line , stdin) == 0) { 604 if (feof(stdin)) { 605 exit(0); 606 } else { 607 continue; 608 } 609 } 610 if ((cp = strchr(line, '\n'))) 611 *cp = '\0'; 612 if (line[0] == 0) 613 continue; 614 makeargv(); 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 register char *name; 633 { 634 register char *p, *q; 635 register struct cmd *c, *found; 636 register 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 void 663 makeargv() 664 { 665 register char *cp; 666 register char **argp = margv; 667 668 margc = 0; 669 if ((cp = strchr(line, '\n'))) 670 *cp = '\0'; 671 for (cp = line; *cp;) { 672 while (isspace(*cp)) 673 cp++; 674 if (*cp == '\0') 675 break; 676 *argp++ = cp; 677 margc += 1; 678 while (*cp != '\0' && !isspace(*cp)) 679 cp++; 680 if (*cp == '\0') 681 break; 682 *cp++ = '\0'; 683 } 684 *argp++ = 0; 685 } 686 687 void 688 quit(argc, argv) 689 int argc; 690 char *argv[]; 691 { 692 693 exit(0); 694 } 695 696 /* 697 * Help command. 698 */ 699 void 700 help(argc, argv) 701 int argc; 702 char *argv[]; 703 { 704 register struct cmd *c; 705 706 if (argc == 1) { 707 printf("Commands may be abbreviated. Commands are:\n\n"); 708 for (c = cmdtab; c->name; c++) 709 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 710 return; 711 } 712 while (--argc > 0) { 713 register char *arg; 714 arg = *++argv; 715 c = getcmd(arg); 716 if (c == (struct cmd *)-1) 717 printf("?Ambiguous help command %s\n", arg); 718 else if (c == (struct cmd *)0) 719 printf("?Invalid help command %s\n", arg); 720 else 721 printf("%s\n", c->help); 722 } 723 } 724 725 void 726 settrace(argc, argv) 727 int argc; 728 char **argv; 729 { 730 trace = !trace; 731 printf("Packet tracing %s.\n", trace ? "on" : "off"); 732 } 733 734 void 735 setverbose(argc, argv) 736 int argc; 737 char **argv; 738 { 739 verbose = !verbose; 740 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 741 } 742