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