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