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