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 #include <sys/cdefs.h> 35 36 __FBSDID("$FreeBSD$"); 37 38 #ifndef lint 39 static const char copyright[] = 40 "@(#) Copyright (c) 1983, 1993\n\ 41 The Regents of the University of California. All rights reserved.\n"; 42 #endif 43 44 #ifndef lint 45 static const char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; 46 #endif 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 <histedit.h> 66 #include <netdb.h> 67 #include <setjmp.h> 68 #include <signal.h> 69 #include <stdio.h> 70 #include <stdlib.h> 71 #include <string.h> 72 #include <unistd.h> 73 74 #include "extern.h" 75 76 #define MAXLINE 200 77 #define TIMEOUT 5 /* secs between rexmt's */ 78 79 struct sockaddr_in peeraddr; 80 int f; 81 short port; 82 int trace; 83 int verbose; 84 int connected; 85 char mode[32]; 86 char line[MAXLINE]; 87 int margc; 88 char *margv[20]; 89 jmp_buf toplevel; 90 struct servent *sp; 91 92 void get __P((int, char **)); 93 void help __P((int, char **)); 94 void intr __P((int)); 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 void command __P((void)) __dead2; 108 static const char *command_prompt __P((void)); 109 110 static void getusage __P((char *)); 111 static void makeargv __P((void)); 112 static void putusage __P((char *)); 113 static void settftpmode __P((const char *)); 114 115 char *tail __P((char *)); 116 struct cmd *getcmd __P((char *)); 117 118 #define HELPINDENT (sizeof("connect")) 119 120 struct cmd { 121 const char *name; 122 char *help; 123 void (*handler) __P((int, char **)); 124 }; 125 126 char vhelp[] = "toggle verbose mode"; 127 char thelp[] = "toggle packet tracing"; 128 char chelp[] = "connect to remote tftp"; 129 char qhelp[] = "exit tftp"; 130 char hhelp[] = "print help information"; 131 char shelp[] = "send file"; 132 char rhelp[] = "receive file"; 133 char mhelp[] = "set file transfer mode"; 134 char sthelp[] = "show current status"; 135 char xhelp[] = "set per-packet retransmission timeout"; 136 char ihelp[] = "set total retransmission timeout"; 137 char ashelp[] = "set mode to netascii"; 138 char bnhelp[] = "set mode to octet"; 139 140 struct cmd cmdtab[] = { 141 { "connect", chelp, setpeer }, 142 { "mode", mhelp, modecmd }, 143 { "put", shelp, put }, 144 { "get", rhelp, get }, 145 { "quit", qhelp, quit }, 146 { "verbose", vhelp, setverbose }, 147 { "trace", thelp, settrace }, 148 { "status", sthelp, status }, 149 { "binary", bnhelp, setbinary }, 150 { "ascii", ashelp, setascii }, 151 { "rexmt", xhelp, setrexmt }, 152 { "timeout", ihelp, settimeout }, 153 { "?", hhelp, help }, 154 { NULL, NULL, NULL } 155 }; 156 157 int 158 main(argc, argv) 159 int argc; 160 char *argv[]; 161 { 162 struct sockaddr_in lsin; 163 164 sp = getservbyname("tftp", "udp"); 165 if (sp == 0) 166 errx(1, "udp/tftp: unknown service"); 167 f = socket(AF_INET, SOCK_DGRAM, 0); 168 if (f < 0) 169 err(3, "socket"); 170 bzero((char *)&lsin, sizeof(lsin)); 171 lsin.sin_family = AF_INET; 172 if (bind(f, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) 173 err(1, "bind"); 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[MAXHOSTNAMELEN]; 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 < 2) || (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, 211 MIN(sizeof(peeraddr.sin_addr), host->h_length)); 212 strncpy(hostname, host->h_name, sizeof(hostname)); 213 } else { 214 peeraddr.sin_family = AF_INET; 215 peeraddr.sin_addr.s_addr = inet_addr(argv[1]); 216 if (peeraddr.sin_addr.s_addr == -1) { 217 connected = 0; 218 printf("%s: unknown host\n", argv[1]); 219 return; 220 } 221 strncpy(hostname, argv[1], sizeof(hostname)); 222 } 223 hostname[sizeof(hostname) - 1] = '\0'; 224 port = sp->s_port; 225 if (argc == 3) { 226 port = atoi(argv[2]); 227 if (port < 0) { 228 printf("%s: bad port number\n", argv[2]); 229 connected = 0; 230 return; 231 } 232 port = htons(port); 233 } 234 connected = 1; 235 } 236 237 struct modes { 238 const char *m_name; 239 const char *m_mode; 240 } modes[] = { 241 { "ascii", "netascii" }, 242 { "netascii", "netascii" }, 243 { "binary", "octet" }, 244 { "image", "octet" }, 245 { "octet", "octet" }, 246 /* { "mail", "mail" }, */ 247 { 0, 0 } 248 }; 249 250 void 251 modecmd(argc, argv) 252 int argc; 253 char *argv[]; 254 { 255 struct modes *p; 256 const char *sep; 257 258 if (argc < 2) { 259 printf("Using %s mode to transfer files.\n", mode); 260 return; 261 } 262 if (argc == 2) { 263 for (p = modes; p->m_name; p++) 264 if (strcmp(argv[1], p->m_name) == 0) 265 break; 266 if (p->m_name) { 267 settftpmode(p->m_mode); 268 return; 269 } 270 printf("%s: unknown mode\n", argv[1]); 271 /* drop through and print usage message */ 272 } 273 274 printf("usage: %s [", argv[0]); 275 sep = " "; 276 for (p = modes; p->m_name; p++) { 277 printf("%s%s", sep, p->m_name); 278 if (*sep == ' ') 279 sep = " | "; 280 } 281 printf(" ]\n"); 282 return; 283 } 284 285 void 286 setbinary(argc, argv) 287 int argc __unused; 288 char *argv[] __unused; 289 { 290 291 settftpmode("octet"); 292 } 293 294 void 295 setascii(argc, argv) 296 int argc __unused; 297 char *argv[] __unused; 298 { 299 300 settftpmode("netascii"); 301 } 302 303 static void 304 settftpmode(newmode) 305 const char *newmode; 306 { 307 strcpy(mode, newmode); 308 if (verbose) 309 printf("mode set to %s\n", mode); 310 } 311 312 313 /* 314 * Send file(s). 315 */ 316 void 317 put(argc, argv) 318 int argc; 319 char *argv[]; 320 { 321 int fd; 322 int n; 323 char *cp, *targ; 324 325 if (argc < 2) { 326 strcpy(line, "send "); 327 printf("(file) "); 328 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 329 makeargv(); 330 argc = margc; 331 argv = margv; 332 } 333 if (argc < 2) { 334 putusage(argv[0]); 335 return; 336 } 337 targ = argv[argc - 1]; 338 if (index(argv[argc - 1], ':')) { 339 char *lcp; 340 struct hostent *hp; 341 342 for (n = 1; n < argc - 1; n++) 343 if (index(argv[n], ':')) { 344 putusage(argv[0]); 345 return; 346 } 347 lcp = argv[argc - 1]; 348 targ = index(lcp, ':'); 349 *targ++ = 0; 350 hp = gethostbyname(lcp); 351 if (hp == NULL) { 352 fprintf(stderr, "tftp: %s: ", lcp); 353 herror((char *)NULL); 354 return; 355 } 356 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 357 MIN(sizeof(peeraddr.sin_addr), hp->h_length)); 358 peeraddr.sin_family = hp->h_addrtype; 359 connected = 1; 360 strncpy(hostname, hp->h_name, sizeof(hostname)); 361 hostname[sizeof(hostname) - 1] = '\0'; 362 } 363 if (!connected) { 364 printf("No target machine specified.\n"); 365 return; 366 } 367 if (argc < 4) { 368 cp = argc == 2 ? tail(targ) : argv[1]; 369 fd = open(cp, O_RDONLY); 370 if (fd < 0) { 371 warn("%s", cp); 372 return; 373 } 374 if (verbose) 375 printf("putting %s to %s:%s [%s]\n", 376 cp, hostname, targ, mode); 377 peeraddr.sin_port = port; 378 xmitfile(fd, targ, mode); 379 return; 380 } 381 /* this assumes the target is a directory */ 382 /* on a remote unix system. hmmmm. */ 383 cp = index(targ, '\0'); 384 *cp++ = '/'; 385 for (n = 1; n < argc - 1; n++) { 386 strcpy(cp, tail(argv[n])); 387 fd = open(argv[n], O_RDONLY); 388 if (fd < 0) { 389 warn("%s", argv[n]); 390 continue; 391 } 392 if (verbose) 393 printf("putting %s to %s:%s [%s]\n", 394 argv[n], hostname, targ, mode); 395 peeraddr.sin_port = port; 396 xmitfile(fd, targ, mode); 397 } 398 } 399 400 static void 401 putusage(s) 402 char *s; 403 { 404 printf("usage: %s file ... host:target, or\n", s); 405 printf(" %s file ... target (when already connected)\n", s); 406 } 407 408 /* 409 * Receive file(s). 410 */ 411 void 412 get(argc, argv) 413 int argc; 414 char *argv[]; 415 { 416 int fd; 417 int n; 418 char *cp; 419 char *src; 420 421 if (argc < 2) { 422 strcpy(line, "get "); 423 printf("(files) "); 424 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 425 makeargv(); 426 argc = margc; 427 argv = margv; 428 } 429 if (argc < 2) { 430 getusage(argv[0]); 431 return; 432 } 433 if (!connected) { 434 for (n = 1; n < argc ; n++) 435 if (index(argv[n], ':') == 0) { 436 getusage(argv[0]); 437 return; 438 } 439 } 440 for (n = 1; n < argc ; n++) { 441 src = index(argv[n], ':'); 442 if (src == NULL) 443 src = argv[n]; 444 else { 445 struct hostent *hp; 446 447 *src++ = 0; 448 hp = gethostbyname(argv[n]); 449 if (hp == NULL) { 450 fprintf(stderr, "tftp: %s: ", argv[n]); 451 herror((char *)NULL); 452 continue; 453 } 454 bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, 455 MIN(sizeof(peeraddr.sin_addr), hp->h_length)); 456 peeraddr.sin_family = hp->h_addrtype; 457 connected = 1; 458 strncpy(hostname, hp->h_name, sizeof(hostname)); 459 hostname[sizeof(hostname) - 1] = '\0'; 460 } 461 if (argc < 4) { 462 cp = argc == 3 ? argv[2] : tail(src); 463 fd = creat(cp, 0644); 464 if (fd < 0) { 465 warn("%s", 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 warn("%s", 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)], sizeof line - 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)], sizeof line - 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 __unused; 556 char *argv[] __unused; 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(dummy) 570 int dummy __unused; 571 { 572 573 signal(SIGALRM, SIG_IGN); 574 alarm(0); 575 longjmp(toplevel, -1); 576 } 577 578 char * 579 tail(filename) 580 char *filename; 581 { 582 char *s; 583 584 while (*filename) { 585 s = rindex(filename, '/'); 586 if (s == NULL) 587 break; 588 if (s[1]) 589 return (s + 1); 590 *s = '\0'; 591 } 592 return (filename); 593 } 594 595 static const char * 596 command_prompt() 597 { 598 599 return ("tftp> "); 600 } 601 602 /* 603 * Command parser. 604 */ 605 static void 606 command() 607 { 608 HistEvent he; 609 struct cmd *c; 610 static EditLine *el; 611 static History *hist; 612 const char *bp; 613 char *cp; 614 int len, num, vrbose; 615 616 vrbose = isatty(0); 617 if (vrbose) { 618 el = el_init("tftp", stdin, stdout, stderr); 619 hist = history_init(); 620 history(hist, &he, H_EVENT, 100); 621 el_set(el, EL_HIST, history, hist); 622 el_set(el, EL_EDITOR, "emacs"); 623 el_set(el, EL_PROMPT, command_prompt); 624 el_set(el, EL_SIGNAL, 1); 625 el_source(el, NULL); 626 } 627 for (;;) { 628 if (vrbose) { 629 if ((bp = el_gets(el, &num)) == NULL || num == 0) 630 exit(0); 631 len = (num > MAXLINE) ? MAXLINE : num; 632 memcpy(line, bp, len); 633 line[len] = '\0'; 634 history(hist, &he, H_ENTER, bp); 635 } else { 636 if (fgets(line, sizeof line , stdin) == 0) { 637 if (feof(stdin)) { 638 exit(0); 639 } else { 640 continue; 641 } 642 } 643 } 644 if ((cp = strchr(line, '\n'))) 645 *cp = '\0'; 646 if (line[0] == 0) 647 continue; 648 makeargv(); 649 if (margc == 0) 650 continue; 651 c = getcmd(margv[0]); 652 if (c == (struct cmd *)-1) { 653 printf("?Ambiguous command\n"); 654 continue; 655 } 656 if (c == 0) { 657 printf("?Invalid command\n"); 658 continue; 659 } 660 (*c->handler)(margc, margv); 661 } 662 } 663 664 struct cmd * 665 getcmd(name) 666 char *name; 667 { 668 const char *p, *q; 669 struct cmd *c, *found; 670 int nmatches, longest; 671 672 longest = 0; 673 nmatches = 0; 674 found = 0; 675 for (c = cmdtab; (p = c->name) != NULL; c++) { 676 for (q = name; *q == *p++; q++) 677 if (*q == 0) /* exact match? */ 678 return (c); 679 if (!*q) { /* the name was a prefix */ 680 if (q - name > longest) { 681 longest = q - name; 682 nmatches = 1; 683 found = c; 684 } else if (q - name == longest) 685 nmatches++; 686 } 687 } 688 if (nmatches > 1) 689 return ((struct cmd *)-1); 690 return (found); 691 } 692 693 /* 694 * Slice a string up into argc/argv. 695 */ 696 static void 697 makeargv() 698 { 699 char *cp; 700 char **argp = margv; 701 702 margc = 0; 703 if ((cp = strchr(line, '\n'))) 704 *cp = '\0'; 705 for (cp = line; *cp;) { 706 while (isspace(*cp)) 707 cp++; 708 if (*cp == '\0') 709 break; 710 *argp++ = cp; 711 margc += 1; 712 while (*cp != '\0' && !isspace(*cp)) 713 cp++; 714 if (*cp == '\0') 715 break; 716 *cp++ = '\0'; 717 } 718 *argp++ = 0; 719 } 720 721 void 722 quit(argc, argv) 723 int argc __unused; 724 char *argv[] __unused; 725 { 726 727 exit(0); 728 } 729 730 /* 731 * Help command. 732 */ 733 void 734 help(argc, argv) 735 int argc; 736 char *argv[]; 737 { 738 struct cmd *c; 739 740 if (argc == 1) { 741 printf("Commands may be abbreviated. Commands are:\n\n"); 742 for (c = cmdtab; c->name; c++) 743 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 744 return; 745 } 746 while (--argc > 0) { 747 char *arg; 748 arg = *++argv; 749 c = getcmd(arg); 750 if (c == (struct cmd *)-1) 751 printf("?Ambiguous help command %s\n", arg); 752 else if (c == (struct cmd *)0) 753 printf("?Invalid help command %s\n", arg); 754 else 755 printf("%s\n", c->help); 756 } 757 } 758 759 void 760 settrace(argc, argv) 761 int argc __unused; 762 char **argv __unused; 763 { 764 trace = !trace; 765 printf("Packet tracing %s.\n", trace ? "on" : "off"); 766 } 767 768 void 769 setverbose(argc, argv) 770 int argc __unused; 771 char **argv __unused; 772 { 773 verbose = !verbose; 774 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 775 } 776