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_storage peeraddr; 81 int f; 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 91 void get(int, char **); 92 void help(int, char **); 93 void intr(int); 94 void modecmd(int, char **); 95 void put(int, char **); 96 void quit(int, char **); 97 void setascii(int, char **); 98 void setbinary(int, char **); 99 void setpeer0(char *, const char *); 100 void setpeer(int, char **); 101 void setrexmt(int, char **); 102 void settimeout(int, char **); 103 void settrace(int, char **); 104 void setverbose(int, char **); 105 void status(int, char **); 106 107 static void command(void) __dead2; 108 static const char *command_prompt(void); 109 110 static void getusage(char *); 111 static void makeargv(void); 112 static void putusage(char *); 113 static void settftpmode(const char *); 114 115 char *tail(char *); 116 struct cmd *getcmd(char *); 117 118 #define HELPINDENT (sizeof("connect")) 119 120 struct cmd { 121 const char *name; 122 char *help; 123 void (*handler)(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 f = -1; 163 strcpy(mode, "netascii"); 164 signal(SIGINT, intr); 165 if (argc > 1) { 166 if (setjmp(toplevel) != 0) 167 exit(0); 168 setpeer(argc, argv); 169 } 170 if (setjmp(toplevel) != 0) 171 (void)putchar('\n'); 172 command(); 173 } 174 175 char hostname[MAXHOSTNAMELEN]; 176 177 void 178 setpeer0(host, port) 179 char *host; 180 const char *port; 181 { 182 struct addrinfo hints, *res0, *res; 183 int error; 184 struct sockaddr_storage ss; 185 const char *cause = "unknown"; 186 187 if (connected) { 188 close(f); 189 f = -1; 190 } 191 connected = 0; 192 193 memset(&hints, 0, sizeof(hints)); 194 hints.ai_family = PF_UNSPEC; 195 hints.ai_socktype = SOCK_DGRAM; 196 hints.ai_protocol = IPPROTO_UDP; 197 hints.ai_flags = AI_CANONNAME; 198 if (!port) 199 port = "tftp"; 200 error = getaddrinfo(host, port, &hints, &res0); 201 if (error) { 202 warnx("%s", gai_strerror(error)); 203 return; 204 } 205 206 for (res = res0; res; res = res->ai_next) { 207 if (res->ai_addrlen > sizeof(peeraddr)) 208 continue; 209 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 210 if (f < 0) { 211 cause = "socket"; 212 continue; 213 } 214 215 memset(&ss, 0, sizeof(ss)); 216 ss.ss_family = res->ai_family; 217 ss.ss_len = res->ai_addrlen; 218 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) { 219 cause = "bind"; 220 close(f); 221 f = -1; 222 continue; 223 } 224 225 break; 226 } 227 228 if (f < 0) 229 warn("%s", cause); 230 else { 231 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 232 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 233 if (res->ai_canonname) { 234 (void) strncpy(hostname, res->ai_canonname, 235 sizeof(hostname)); 236 } else 237 (void) strncpy(hostname, host, sizeof(hostname)); 238 hostname[sizeof(hostname)-1] = 0; 239 connected = 1; 240 } 241 242 freeaddrinfo(res0); 243 } 244 245 void 246 setpeer(argc, argv) 247 int argc; 248 char *argv[]; 249 { 250 251 if (argc < 2) { 252 strcpy(line, "Connect "); 253 printf("(to) "); 254 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 255 makeargv(); 256 argc = margc; 257 argv = margv; 258 } 259 if ((argc < 2) || (argc > 3)) { 260 printf("usage: %s host-name [port]\n", argv[0]); 261 return; 262 } 263 if (argc == 3) 264 setpeer0(argv[1], NULL); 265 else 266 setpeer0(argv[1], argv[2]); 267 } 268 269 struct modes { 270 const char *m_name; 271 const char *m_mode; 272 } modes[] = { 273 { "ascii", "netascii" }, 274 { "netascii", "netascii" }, 275 { "binary", "octet" }, 276 { "image", "octet" }, 277 { "octet", "octet" }, 278 /* { "mail", "mail" }, */ 279 { 0, 0 } 280 }; 281 282 void 283 modecmd(argc, argv) 284 int argc; 285 char *argv[]; 286 { 287 struct modes *p; 288 const char *sep; 289 290 if (argc < 2) { 291 printf("Using %s mode to transfer files.\n", mode); 292 return; 293 } 294 if (argc == 2) { 295 for (p = modes; p->m_name; p++) 296 if (strcmp(argv[1], p->m_name) == 0) 297 break; 298 if (p->m_name) { 299 settftpmode(p->m_mode); 300 return; 301 } 302 printf("%s: unknown mode\n", argv[1]); 303 /* drop through and print usage message */ 304 } 305 306 printf("usage: %s [", argv[0]); 307 sep = " "; 308 for (p = modes; p->m_name; p++) { 309 printf("%s%s", sep, p->m_name); 310 if (*sep == ' ') 311 sep = " | "; 312 } 313 printf(" ]\n"); 314 return; 315 } 316 317 void 318 setbinary(argc, argv) 319 int argc __unused; 320 char *argv[] __unused; 321 { 322 323 settftpmode("octet"); 324 } 325 326 void 327 setascii(argc, argv) 328 int argc __unused; 329 char *argv[] __unused; 330 { 331 332 settftpmode("netascii"); 333 } 334 335 static void 336 settftpmode(newmode) 337 const char *newmode; 338 { 339 strcpy(mode, newmode); 340 if (verbose) 341 printf("mode set to %s\n", mode); 342 } 343 344 345 /* 346 * Send file(s). 347 */ 348 void 349 put(argc, argv) 350 int argc; 351 char *argv[]; 352 { 353 int fd; 354 int n; 355 char *cp, *targ; 356 357 if (argc < 2) { 358 strcpy(line, "send "); 359 printf("(file) "); 360 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 361 makeargv(); 362 argc = margc; 363 argv = margv; 364 } 365 if (argc < 2) { 366 putusage(argv[0]); 367 return; 368 } 369 targ = argv[argc - 1]; 370 if (rindex(argv[argc - 1], ':')) { 371 char *lcp; 372 373 for (n = 1; n < argc - 1; n++) 374 if (index(argv[n], ':')) { 375 putusage(argv[0]); 376 return; 377 } 378 lcp = argv[argc - 1]; 379 targ = rindex(lcp, ':'); 380 *targ++ = 0; 381 if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 382 lcp[strlen(lcp) - 1] = '\0'; 383 lcp++; 384 } 385 setpeer0(lcp, NULL); 386 } 387 if (!connected) { 388 printf("No target machine specified.\n"); 389 return; 390 } 391 if (argc < 4) { 392 cp = argc == 2 ? tail(targ) : argv[1]; 393 fd = open(cp, O_RDONLY); 394 if (fd < 0) { 395 warn("%s", cp); 396 return; 397 } 398 if (verbose) 399 printf("putting %s to %s:%s [%s]\n", 400 cp, hostname, targ, mode); 401 xmitfile(fd, targ, mode); 402 return; 403 } 404 /* this assumes the target is a directory */ 405 /* on a remote unix system. hmmmm. */ 406 cp = index(targ, '\0'); 407 *cp++ = '/'; 408 for (n = 1; n < argc - 1; n++) { 409 strcpy(cp, tail(argv[n])); 410 fd = open(argv[n], O_RDONLY); 411 if (fd < 0) { 412 warn("%s", argv[n]); 413 continue; 414 } 415 if (verbose) 416 printf("putting %s to %s:%s [%s]\n", 417 argv[n], hostname, targ, mode); 418 xmitfile(fd, targ, mode); 419 } 420 } 421 422 static void 423 putusage(s) 424 char *s; 425 { 426 printf("usage: %s file ... host:target, or\n", s); 427 printf(" %s file ... target (when already connected)\n", s); 428 } 429 430 /* 431 * Receive file(s). 432 */ 433 void 434 get(argc, argv) 435 int argc; 436 char *argv[]; 437 { 438 int fd; 439 int n; 440 char *cp; 441 char *src; 442 443 if (argc < 2) { 444 strcpy(line, "get "); 445 printf("(files) "); 446 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 447 makeargv(); 448 argc = margc; 449 argv = margv; 450 } 451 if (argc < 2) { 452 getusage(argv[0]); 453 return; 454 } 455 if (!connected) { 456 for (n = 1; n < argc ; n++) 457 if (rindex(argv[n], ':') == 0) { 458 getusage(argv[0]); 459 return; 460 } 461 } 462 for (n = 1; n < argc ; n++) { 463 src = rindex(argv[n], ':'); 464 if (src == NULL) 465 src = argv[n]; 466 else { 467 char *lcp; 468 469 *src++ = 0; 470 lcp = argv[n]; 471 if (lcp[0] == '[' && lcp[strlen(lcp) - 1] == ']') { 472 lcp[strlen(lcp) - 1] = '\0'; 473 lcp++; 474 } 475 setpeer0(lcp, NULL); 476 if (!connected) 477 continue; 478 } 479 if (argc < 4) { 480 cp = argc == 3 ? argv[2] : tail(src); 481 fd = creat(cp, 0644); 482 if (fd < 0) { 483 warn("%s", cp); 484 return; 485 } 486 if (verbose) 487 printf("getting from %s:%s to %s [%s]\n", 488 hostname, src, cp, mode); 489 recvfile(fd, src, mode); 490 break; 491 } 492 cp = tail(src); /* new .. jdg */ 493 fd = creat(cp, 0644); 494 if (fd < 0) { 495 warn("%s", cp); 496 continue; 497 } 498 if (verbose) 499 printf("getting from %s:%s to %s [%s]\n", 500 hostname, src, cp, mode); 501 recvfile(fd, src, mode); 502 } 503 } 504 505 static void 506 getusage(s) 507 char *s; 508 { 509 printf("usage: %s host:file host:file ... file, or\n", s); 510 printf(" %s file file ... file if connected\n", s); 511 } 512 513 int rexmtval = TIMEOUT; 514 515 void 516 setrexmt(argc, argv) 517 int argc; 518 char *argv[]; 519 { 520 int t; 521 522 if (argc < 2) { 523 strcpy(line, "Rexmt-timeout "); 524 printf("(value) "); 525 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 526 makeargv(); 527 argc = margc; 528 argv = margv; 529 } 530 if (argc != 2) { 531 printf("usage: %s value\n", argv[0]); 532 return; 533 } 534 t = atoi(argv[1]); 535 if (t < 0) 536 printf("%s: bad value\n", argv[1]); 537 else 538 rexmtval = t; 539 } 540 541 int maxtimeout = 5 * TIMEOUT; 542 543 void 544 settimeout(argc, argv) 545 int argc; 546 char *argv[]; 547 { 548 int t; 549 550 if (argc < 2) { 551 strcpy(line, "Maximum-timeout "); 552 printf("(value) "); 553 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 554 makeargv(); 555 argc = margc; 556 argv = margv; 557 } 558 if (argc != 2) { 559 printf("usage: %s value\n", argv[0]); 560 return; 561 } 562 t = atoi(argv[1]); 563 if (t < 0) 564 printf("%s: bad value\n", argv[1]); 565 else 566 maxtimeout = t; 567 } 568 569 void 570 status(argc, argv) 571 int argc __unused; 572 char *argv[] __unused; 573 { 574 if (connected) 575 printf("Connected to %s.\n", hostname); 576 else 577 printf("Not connected.\n"); 578 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 579 verbose ? "on" : "off", trace ? "on" : "off"); 580 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 581 rexmtval, maxtimeout); 582 } 583 584 void 585 intr(dummy) 586 int dummy __unused; 587 { 588 589 signal(SIGALRM, SIG_IGN); 590 alarm(0); 591 longjmp(toplevel, -1); 592 } 593 594 char * 595 tail(filename) 596 char *filename; 597 { 598 char *s; 599 600 while (*filename) { 601 s = rindex(filename, '/'); 602 if (s == NULL) 603 break; 604 if (s[1]) 605 return (s + 1); 606 *s = '\0'; 607 } 608 return (filename); 609 } 610 611 static const char * 612 command_prompt() 613 { 614 615 return ("tftp> "); 616 } 617 618 /* 619 * Command parser. 620 */ 621 static void 622 command() 623 { 624 HistEvent he; 625 struct cmd *c; 626 static EditLine *el; 627 static History *hist; 628 const char *bp; 629 char *cp; 630 int len, num, vrbose; 631 632 vrbose = isatty(0); 633 if (vrbose) { 634 el = el_init("tftp", stdin, stdout, stderr); 635 hist = history_init(); 636 history(hist, &he, H_EVENT, 100); 637 el_set(el, EL_HIST, history, hist); 638 el_set(el, EL_EDITOR, "emacs"); 639 el_set(el, EL_PROMPT, command_prompt); 640 el_set(el, EL_SIGNAL, 1); 641 el_source(el, NULL); 642 } 643 for (;;) { 644 if (vrbose) { 645 if ((bp = el_gets(el, &num)) == NULL || num == 0) 646 exit(0); 647 len = (num > MAXLINE) ? MAXLINE : num; 648 memcpy(line, bp, len); 649 line[len] = '\0'; 650 history(hist, &he, H_ENTER, bp); 651 } else { 652 if (fgets(line, sizeof line , stdin) == 0) { 653 if (feof(stdin)) { 654 exit(0); 655 } else { 656 continue; 657 } 658 } 659 } 660 if ((cp = strchr(line, '\n'))) 661 *cp = '\0'; 662 if (line[0] == 0) 663 continue; 664 makeargv(); 665 if (margc == 0) 666 continue; 667 c = getcmd(margv[0]); 668 if (c == (struct cmd *)-1) { 669 printf("?Ambiguous command\n"); 670 continue; 671 } 672 if (c == 0) { 673 printf("?Invalid command\n"); 674 continue; 675 } 676 (*c->handler)(margc, margv); 677 } 678 } 679 680 struct cmd * 681 getcmd(name) 682 char *name; 683 { 684 const char *p, *q; 685 struct cmd *c, *found; 686 int nmatches, longest; 687 688 longest = 0; 689 nmatches = 0; 690 found = 0; 691 for (c = cmdtab; (p = c->name) != NULL; c++) { 692 for (q = name; *q == *p++; q++) 693 if (*q == 0) /* exact match? */ 694 return (c); 695 if (!*q) { /* the name was a prefix */ 696 if (q - name > longest) { 697 longest = q - name; 698 nmatches = 1; 699 found = c; 700 } else if (q - name == longest) 701 nmatches++; 702 } 703 } 704 if (nmatches > 1) 705 return ((struct cmd *)-1); 706 return (found); 707 } 708 709 /* 710 * Slice a string up into argc/argv. 711 */ 712 static void 713 makeargv() 714 { 715 char *cp; 716 char **argp = margv; 717 718 margc = 0; 719 if ((cp = strchr(line, '\n'))) 720 *cp = '\0'; 721 for (cp = line; *cp;) { 722 while (isspace(*cp)) 723 cp++; 724 if (*cp == '\0') 725 break; 726 *argp++ = cp; 727 margc += 1; 728 while (*cp != '\0' && !isspace(*cp)) 729 cp++; 730 if (*cp == '\0') 731 break; 732 *cp++ = '\0'; 733 } 734 *argp++ = 0; 735 } 736 737 void 738 quit(argc, argv) 739 int argc __unused; 740 char *argv[] __unused; 741 { 742 743 exit(0); 744 } 745 746 /* 747 * Help command. 748 */ 749 void 750 help(argc, argv) 751 int argc; 752 char *argv[]; 753 { 754 struct cmd *c; 755 756 if (argc == 1) { 757 printf("Commands may be abbreviated. Commands are:\n\n"); 758 for (c = cmdtab; c->name; c++) 759 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 760 return; 761 } 762 while (--argc > 0) { 763 char *arg; 764 arg = *++argv; 765 c = getcmd(arg); 766 if (c == (struct cmd *)-1) 767 printf("?Ambiguous help command %s\n", arg); 768 else if (c == (struct cmd *)0) 769 printf("?Invalid help command %s\n", arg); 770 else 771 printf("%s\n", c->help); 772 } 773 } 774 775 void 776 settrace(argc, argv) 777 int argc __unused; 778 char **argv __unused; 779 { 780 trace = !trace; 781 printf("Packet tracing %s.\n", trace ? "on" : "off"); 782 } 783 784 void 785 setverbose(argc, argv) 786 int argc __unused; 787 char **argv __unused; 788 { 789 verbose = !verbose; 790 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 791 } 792