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 39 40 #if 0 41 #ifndef lint 42 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 43 #endif 44 #endif 45 46 #include <sys/cdefs.h> 47 __FBSDID("$FreeBSD$"); 48 49 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 50 51 /* 52 * TFTP User Program -- Command Interface. 53 */ 54 #include <sys/param.h> 55 #include <sys/types.h> 56 #include <sys/socket.h> 57 #include <sys/file.h> 58 #include <sys/param.h> 59 60 #include <netinet/in.h> 61 62 #include <arpa/inet.h> 63 64 #include <ctype.h> 65 #include <err.h> 66 #include <histedit.h> 67 #include <netdb.h> 68 #include <setjmp.h> 69 #include <signal.h> 70 #include <stdio.h> 71 #include <stdlib.h> 72 #include <string.h> 73 #include <unistd.h> 74 75 #include "extern.h" 76 77 #define MAXLINE 200 78 #define TIMEOUT 5 /* secs between rexmt's */ 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[MAXLINE]; 88 int margc; 89 char *margv[20]; 90 jmp_buf toplevel; 91 struct servent *sp; 92 93 void get(int, char **); 94 void help(int, char **); 95 void intr(int); 96 void modecmd(int, char **); 97 void put(int, char **); 98 void quit(int, char **); 99 void setascii(int, char **); 100 void setbinary(int, char **); 101 void setpeer(int, char **); 102 void setrexmt(int, char **); 103 void settimeout(int, char **); 104 void settrace(int, char **); 105 void setverbose(int, char **); 106 void status(int, char **); 107 108 static void command(void) __dead2; 109 static const char *command_prompt(void); 110 111 static void getusage(char *); 112 static void makeargv(void); 113 static void putusage(char *); 114 static void settftpmode(const char *); 115 116 char *tail(char *); 117 struct cmd *getcmd(char *); 118 119 #define HELPINDENT (sizeof("connect")) 120 121 struct cmd { 122 const char *name; 123 char *help; 124 void (*handler)(int, char **); 125 }; 126 127 char vhelp[] = "toggle verbose mode"; 128 char thelp[] = "toggle packet tracing"; 129 char chelp[] = "connect to remote tftp"; 130 char qhelp[] = "exit tftp"; 131 char hhelp[] = "print help information"; 132 char shelp[] = "send file"; 133 char rhelp[] = "receive file"; 134 char mhelp[] = "set file transfer mode"; 135 char sthelp[] = "show current status"; 136 char xhelp[] = "set per-packet retransmission timeout"; 137 char ihelp[] = "set total retransmission timeout"; 138 char ashelp[] = "set mode to netascii"; 139 char bnhelp[] = "set mode to octet"; 140 141 struct cmd cmdtab[] = { 142 { "connect", chelp, setpeer }, 143 { "mode", mhelp, modecmd }, 144 { "put", shelp, put }, 145 { "get", rhelp, get }, 146 { "quit", qhelp, quit }, 147 { "verbose", vhelp, setverbose }, 148 { "trace", thelp, settrace }, 149 { "status", sthelp, status }, 150 { "binary", bnhelp, setbinary }, 151 { "ascii", ashelp, setascii }, 152 { "rexmt", xhelp, setrexmt }, 153 { "timeout", ihelp, settimeout }, 154 { "?", hhelp, help }, 155 { NULL, NULL, NULL } 156 }; 157 158 int 159 main(argc, argv) 160 int argc; 161 char *argv[]; 162 { 163 struct sockaddr_in lsin; 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, "socket"); 171 bzero((char *)&lsin, sizeof(lsin)); 172 lsin.sin_family = AF_INET; 173 if (bind(f, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) 174 err(1, "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 } 186 187 char hostname[MAXHOSTNAMELEN]; 188 189 void 190 setpeer(argc, argv) 191 int argc; 192 char *argv[]; 193 { 194 struct hostent *host; 195 196 if (argc < 2) { 197 strcpy(line, "Connect "); 198 printf("(to) "); 199 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 200 makeargv(); 201 argc = margc; 202 argv = margv; 203 } 204 if ((argc < 2) || (argc > 3)) { 205 printf("usage: %s host-name [port]\n", argv[0]); 206 return; 207 } 208 host = gethostbyname(argv[1]); 209 if (host) { 210 peeraddr.sin_family = host->h_addrtype; 211 bcopy(host->h_addr, &peeraddr.sin_addr, 212 MIN(sizeof(peeraddr.sin_addr), (size_t)host->h_length)); 213 strncpy(hostname, host->h_name, sizeof(hostname)); 214 } else { 215 peeraddr.sin_family = AF_INET; 216 peeraddr.sin_addr.s_addr = inet_addr(argv[1]); 217 if (peeraddr.sin_addr.s_addr == INADDR_NONE) { 218 connected = 0; 219 printf("%s: unknown host\n", argv[1]); 220 return; 221 } 222 strncpy(hostname, argv[1], sizeof(hostname)); 223 } 224 hostname[sizeof(hostname) - 1] = '\0'; 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 const char *m_name; 240 const char *m_mode; 241 } modes[] = { 242 { "ascii", "netascii" }, 243 { "netascii", "netascii" }, 244 { "binary", "octet" }, 245 { "image", "octet" }, 246 { "octet", "octet" }, 247 /* { "mail", "mail" }, */ 248 { 0, 0 } 249 }; 250 251 void 252 modecmd(argc, argv) 253 int argc; 254 char *argv[]; 255 { 256 struct modes *p; 257 const 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; 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; 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 __unused; 289 char *argv[] __unused; 290 { 291 292 settftpmode("octet"); 293 } 294 295 void 296 setascii(argc, argv) 297 int argc __unused; 298 char *argv[] __unused; 299 { 300 301 settftpmode("netascii"); 302 } 303 304 static void 305 settftpmode(newmode) 306 const 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)], sizeof line - strlen(line), stdin); 330 makeargv(); 331 argc = margc; 332 argv = margv; 333 } 334 if (argc < 2) { 335 putusage(argv[0]); 336 return; 337 } 338 targ = argv[argc - 1]; 339 if (index(argv[argc - 1], ':')) { 340 char *lcp; 341 struct hostent *hp; 342 343 for (n = 1; n < argc - 1; n++) 344 if (index(argv[n], ':')) { 345 putusage(argv[0]); 346 return; 347 } 348 lcp = argv[argc - 1]; 349 targ = index(lcp, ':'); 350 *targ++ = 0; 351 hp = gethostbyname(lcp); 352 if (hp == NULL) { 353 fprintf(stderr, "tftp: %s: ", lcp); 354 herror((char *)NULL); 355 return; 356 } 357 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 358 MIN(sizeof(peeraddr.sin_addr), (size_t)hp->h_length)); 359 peeraddr.sin_family = hp->h_addrtype; 360 connected = 1; 361 strncpy(hostname, hp->h_name, sizeof(hostname)); 362 hostname[sizeof(hostname) - 1] = '\0'; 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 warn("%s", 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 xmitfile(fd, targ, mode); 380 return; 381 } 382 /* this assumes the target is a directory */ 383 /* on a remote unix system. hmmmm. */ 384 cp = index(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 warn("%s", 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 xmitfile(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 int n; 419 char *cp; 420 char *src; 421 422 if (argc < 2) { 423 strcpy(line, "get "); 424 printf("(files) "); 425 fgets(&line[strlen(line)], sizeof line - 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 (index(argv[n], ':') == 0) { 437 getusage(argv[0]); 438 return; 439 } 440 } 441 for (n = 1; n < argc ; n++) { 442 src = index(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 MIN(sizeof(peeraddr.sin_addr), (size_t)hp->h_length)); 457 peeraddr.sin_family = hp->h_addrtype; 458 connected = 1; 459 strncpy(hostname, hp->h_name, sizeof(hostname)); 460 hostname[sizeof(hostname) - 1] = '\0'; 461 } 462 if (argc < 4) { 463 cp = argc == 3 ? argv[2] : tail(src); 464 fd = creat(cp, 0644); 465 if (fd < 0) { 466 warn("%s", cp); 467 return; 468 } 469 if (verbose) 470 printf("getting from %s:%s to %s [%s]\n", 471 hostname, src, cp, mode); 472 peeraddr.sin_port = port; 473 recvfile(fd, src, mode); 474 break; 475 } 476 cp = tail(src); /* new .. jdg */ 477 fd = creat(cp, 0644); 478 if (fd < 0) { 479 warn("%s", cp); 480 continue; 481 } 482 if (verbose) 483 printf("getting from %s:%s to %s [%s]\n", 484 hostname, src, cp, mode); 485 peeraddr.sin_port = port; 486 recvfile(fd, src, mode); 487 } 488 } 489 490 static void 491 getusage(s) 492 char *s; 493 { 494 printf("usage: %s host:file host:file ... file, or\n", s); 495 printf(" %s file file ... file if connected\n", s); 496 } 497 498 int rexmtval = TIMEOUT; 499 500 void 501 setrexmt(argc, argv) 502 int argc; 503 char *argv[]; 504 { 505 int t; 506 507 if (argc < 2) { 508 strcpy(line, "Rexmt-timeout "); 509 printf("(value) "); 510 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 511 makeargv(); 512 argc = margc; 513 argv = margv; 514 } 515 if (argc != 2) { 516 printf("usage: %s value\n", argv[0]); 517 return; 518 } 519 t = atoi(argv[1]); 520 if (t < 0) 521 printf("%s: bad value\n", argv[1]); 522 else 523 rexmtval = t; 524 } 525 526 int maxtimeout = 5 * TIMEOUT; 527 528 void 529 settimeout(argc, argv) 530 int argc; 531 char *argv[]; 532 { 533 int t; 534 535 if (argc < 2) { 536 strcpy(line, "Maximum-timeout "); 537 printf("(value) "); 538 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 539 makeargv(); 540 argc = margc; 541 argv = margv; 542 } 543 if (argc != 2) { 544 printf("usage: %s value\n", argv[0]); 545 return; 546 } 547 t = atoi(argv[1]); 548 if (t < 0) 549 printf("%s: bad value\n", argv[1]); 550 else 551 maxtimeout = t; 552 } 553 554 void 555 status(argc, argv) 556 int argc __unused; 557 char *argv[] __unused; 558 { 559 if (connected) 560 printf("Connected to %s.\n", hostname); 561 else 562 printf("Not connected.\n"); 563 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 564 verbose ? "on" : "off", trace ? "on" : "off"); 565 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 566 rexmtval, maxtimeout); 567 } 568 569 void 570 intr(dummy) 571 int dummy __unused; 572 { 573 574 signal(SIGALRM, SIG_IGN); 575 alarm(0); 576 longjmp(toplevel, -1); 577 } 578 579 char * 580 tail(filename) 581 char *filename; 582 { 583 char *s; 584 585 while (*filename) { 586 s = rindex(filename, '/'); 587 if (s == NULL) 588 break; 589 if (s[1]) 590 return (s + 1); 591 *s = '\0'; 592 } 593 return (filename); 594 } 595 596 static const char * 597 command_prompt() 598 { 599 600 return ("tftp> "); 601 } 602 603 /* 604 * Command parser. 605 */ 606 static void 607 command() 608 { 609 HistEvent he; 610 struct cmd *c; 611 static EditLine *el; 612 static History *hist; 613 const char *bp; 614 char *cp; 615 int len, num, vrbose; 616 617 vrbose = isatty(0); 618 if (vrbose) { 619 el = el_init("tftp", stdin, stdout, stderr); 620 hist = history_init(); 621 history(hist, &he, H_EVENT, 100); 622 el_set(el, EL_HIST, history, hist); 623 el_set(el, EL_EDITOR, "emacs"); 624 el_set(el, EL_PROMPT, command_prompt); 625 el_set(el, EL_SIGNAL, 1); 626 el_source(el, NULL); 627 } 628 for (;;) { 629 if (vrbose) { 630 if ((bp = el_gets(el, &num)) == NULL || num == 0) 631 exit(0); 632 len = (num > MAXLINE) ? MAXLINE : num; 633 memcpy(line, bp, len); 634 line[len] = '\0'; 635 history(hist, &he, H_ENTER, bp); 636 } else { 637 if (fgets(line, sizeof line , stdin) == 0) { 638 if (feof(stdin)) { 639 exit(0); 640 } else { 641 continue; 642 } 643 } 644 } 645 if ((cp = strchr(line, '\n'))) 646 *cp = '\0'; 647 if (line[0] == 0) 648 continue; 649 makeargv(); 650 if (margc == 0) 651 continue; 652 c = getcmd(margv[0]); 653 if (c == (struct cmd *)-1) { 654 printf("?Ambiguous command\n"); 655 continue; 656 } 657 if (c == 0) { 658 printf("?Invalid command\n"); 659 continue; 660 } 661 (*c->handler)(margc, margv); 662 } 663 } 664 665 struct cmd * 666 getcmd(name) 667 char *name; 668 { 669 const char *p, *q; 670 struct cmd *c, *found; 671 int nmatches, longest; 672 673 longest = 0; 674 nmatches = 0; 675 found = 0; 676 for (c = cmdtab; (p = c->name) != NULL; c++) { 677 for (q = name; *q == *p++; q++) 678 if (*q == 0) /* exact match? */ 679 return (c); 680 if (!*q) { /* the name was a prefix */ 681 if (q - name > longest) { 682 longest = q - name; 683 nmatches = 1; 684 found = c; 685 } else if (q - name == longest) 686 nmatches++; 687 } 688 } 689 if (nmatches > 1) 690 return ((struct cmd *)-1); 691 return (found); 692 } 693 694 /* 695 * Slice a string up into argc/argv. 696 */ 697 static void 698 makeargv() 699 { 700 char *cp; 701 char **argp = margv; 702 703 margc = 0; 704 if ((cp = strchr(line, '\n'))) 705 *cp = '\0'; 706 for (cp = line; *cp;) { 707 while (isspace(*cp)) 708 cp++; 709 if (*cp == '\0') 710 break; 711 *argp++ = cp; 712 margc += 1; 713 while (*cp != '\0' && !isspace(*cp)) 714 cp++; 715 if (*cp == '\0') 716 break; 717 *cp++ = '\0'; 718 } 719 *argp++ = 0; 720 } 721 722 void 723 quit(argc, argv) 724 int argc __unused; 725 char *argv[] __unused; 726 { 727 728 exit(0); 729 } 730 731 /* 732 * Help command. 733 */ 734 void 735 help(argc, argv) 736 int argc; 737 char *argv[]; 738 { 739 struct cmd *c; 740 741 if (argc == 1) { 742 printf("Commands may be abbreviated. Commands are:\n\n"); 743 for (c = cmdtab; c->name; c++) 744 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 745 return; 746 } 747 while (--argc > 0) { 748 char *arg; 749 arg = *++argv; 750 c = getcmd(arg); 751 if (c == (struct cmd *)-1) 752 printf("?Ambiguous help command %s\n", arg); 753 else if (c == (struct cmd *)0) 754 printf("?Invalid help command %s\n", arg); 755 else 756 printf("%s\n", c->help); 757 } 758 } 759 760 void 761 settrace(argc, argv) 762 int argc __unused; 763 char **argv __unused; 764 { 765 trace = !trace; 766 printf("Packet tracing %s.\n", trace ? "on" : "off"); 767 } 768 769 void 770 setverbose(argc, argv) 771 int argc __unused; 772 char **argv __unused; 773 { 774 verbose = !verbose; 775 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 776 } 777