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. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)main.c 8.1 (Berkeley) 6/6/93 31 * $FreeBSD: src/usr.bin/tftp/main.c,v 1.8.2.3 2002/05/14 22:08:07 bsd Exp $ 32 */ 33 34 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ 35 36 /* 37 * TFTP User Program -- Command Interface. 38 */ 39 #include <sys/param.h> 40 #include <sys/types.h> 41 #include <sys/socket.h> 42 43 #include <netinet/in.h> 44 45 #include <arpa/inet.h> 46 47 #include <ctype.h> 48 #include <err.h> 49 #include <fcntl.h> 50 #include <histedit.h> 51 #include <netdb.h> 52 #include <setjmp.h> 53 #include <signal.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "extern.h" 60 61 #define MAXLINE 200 62 #define TIMEOUT 5 /* secs between rexmt's */ 63 64 struct sockaddr_storage peeraddr; 65 int f; 66 int tftp_trace; 67 int verbose; 68 int connected; 69 char mode[32]; 70 char line[MAXLINE]; 71 int margc; 72 #define MAX_MARGV 20 73 char *margv[MAX_MARGV]; 74 extern jmp_buf toplevel; 75 volatile int txrx_error; 76 void intr(int) __dead2; 77 78 void get(int, char **); 79 void help(int, char **); 80 void modecmd(int, char **); 81 void put(int, char **); 82 void quit(int, char **) __dead2; 83 void setascii(int, char **); 84 void setbinary(int, char **); 85 void setpeer0(char *, const char *); 86 void setpeer(int, char **); 87 void setrexmt(int, char **); 88 void settimeout(int, char **); 89 void settrace(int, char **); 90 void setverbose(int, char **); 91 void status(int, char **); 92 93 static void command(void) __dead2; 94 static const char *command_prompt(void); 95 96 static void getusage(char *); 97 static void makeargv(void); 98 static void putusage(char *); 99 static void settftpmode(const char *); 100 101 #define HELPINDENT (sizeof("connect")) 102 103 struct cmd { 104 const char *name; 105 const char *help; 106 void (*handler)(int, char **); 107 }; 108 109 const char vhelp[] = "toggle verbose mode"; 110 const char thelp[] = "toggle packet tracing"; 111 const char chelp[] = "connect to remote tftp"; 112 const char qhelp[] = "exit tftp"; 113 const char hhelp[] = "print help information"; 114 const char shelp[] = "send file"; 115 const char rhelp[] = "receive file"; 116 const char mhelp[] = "set file transfer mode"; 117 const char sthelp[] = "show current status"; 118 const char xhelp[] = "set per-packet retransmission timeout"; 119 const char ihelp[] = "set total retransmission timeout"; 120 const char ashelp[] = "set mode to netascii"; 121 const char bnhelp[] = "set mode to octet"; 122 123 struct cmd cmdtab[] = { 124 { "connect", chelp, setpeer }, 125 { "mode", mhelp, modecmd }, 126 { "put", shelp, put }, 127 { "get", rhelp, get }, 128 { "quit", qhelp, quit }, 129 { "verbose", vhelp, setverbose }, 130 { "trace", thelp, settrace }, 131 { "status", sthelp, status }, 132 { "binary", bnhelp, setbinary }, 133 { "ascii", ashelp, setascii }, 134 { "rexmt", xhelp, setrexmt }, 135 { "timeout", ihelp, settimeout }, 136 { "?", hhelp, help }, 137 { .name = NULL } 138 }; 139 140 struct cmd *getcmd(char *); 141 char *tail(char *); 142 143 int 144 main(int argc, char **argv) 145 { 146 f = -1; 147 strcpy(mode, "netascii"); 148 signal(SIGINT, intr); 149 if (argc > 1) { 150 if (setjmp(toplevel) != 0) 151 exit(txrx_error); 152 setpeer(argc, argv); 153 } 154 if (setjmp(toplevel) != 0) 155 (void)putchar('\n'); 156 command(); 157 } 158 159 char hostname[MAXHOSTNAMELEN]; 160 161 void 162 setpeer0(char *host, const char *port) 163 { 164 struct addrinfo hints, *res0, *res; 165 int error; 166 struct sockaddr_storage ss; 167 const char *cause = "unknown"; 168 169 if (connected) { 170 close(f); 171 f = -1; 172 } 173 connected = 0; 174 175 memset(&hints, 0, sizeof(hints)); 176 hints.ai_family = PF_UNSPEC; 177 hints.ai_socktype = SOCK_DGRAM; 178 hints.ai_protocol = IPPROTO_UDP; 179 hints.ai_flags = AI_CANONNAME; 180 if (!port) 181 port = "tftp"; 182 error = getaddrinfo(host, port, &hints, &res0); 183 if (error) { 184 warnx("%s", gai_strerror(error)); 185 return; 186 } 187 188 for (res = res0; res; res = res->ai_next) { 189 if (res->ai_addrlen > sizeof(peeraddr)) 190 continue; 191 f = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 192 if (f < 0) { 193 cause = "socket"; 194 continue; 195 } 196 197 memset(&ss, 0, sizeof(ss)); 198 ss.ss_family = res->ai_family; 199 ss.ss_len = res->ai_addrlen; 200 if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) { 201 cause = "bind"; 202 close(f); 203 f = -1; 204 continue; 205 } 206 207 break; 208 } 209 210 if (f < 0) 211 warn("%s", cause); 212 else { 213 /* res->ai_addr <= sizeof(peeraddr) is guaranteed */ 214 memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); 215 if (res->ai_canonname) { 216 (void) strncpy(hostname, res->ai_canonname, 217 sizeof(hostname)); 218 } else 219 (void) strncpy(hostname, host, sizeof(hostname)); 220 hostname[sizeof(hostname)-1] = 0; 221 connected = 1; 222 } 223 224 freeaddrinfo(res0); 225 } 226 227 void 228 setpeer(int argc, char **argv) 229 { 230 231 if (argc < 2) { 232 strcpy(line, "Connect "); 233 printf("(to) "); 234 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 235 makeargv(); 236 argc = margc; 237 argv = margv; 238 } 239 if ((argc < 2) || (argc > 3)) { 240 printf("usage: %s host-name [port]\n", argv[0]); 241 return; 242 } 243 if (argc == 3) 244 setpeer0(argv[1], NULL); 245 else 246 setpeer0(argv[1], argv[2]); 247 } 248 249 struct modes { 250 const char *m_name; 251 const char *m_mode; 252 } modes[] = { 253 { "ascii", "netascii" }, 254 { "netascii", "netascii" }, 255 { "binary", "octet" }, 256 { "image", "octet" }, 257 { "octet", "octet" }, 258 /* { "mail", "mail" }, */ 259 { NULL, NULL } 260 }; 261 262 void 263 modecmd(int argc, char **argv) 264 { 265 struct modes *p; 266 const char *sep; 267 268 if (argc < 2) { 269 printf("Using %s mode to transfer files.\n", mode); 270 return; 271 } 272 if (argc == 2) { 273 for (p = modes; p->m_name; p++) 274 if (strcmp(argv[1], p->m_name) == 0) 275 break; 276 if (p->m_name) { 277 settftpmode(p->m_mode); 278 return; 279 } 280 printf("%s: unknown mode\n", argv[1]); 281 /* drop through and print usage message */ 282 } 283 284 printf("usage: %s [", argv[0]); 285 sep = " "; 286 for (p = modes; p->m_name; p++) { 287 printf("%s%s", sep, p->m_name); 288 if (*sep == ' ') 289 sep = " | "; 290 } 291 printf(" ]\n"); 292 return; 293 } 294 295 void 296 setbinary(int argc __unused, char **argv __unused) 297 { 298 299 settftpmode("octet"); 300 } 301 302 void 303 setascii(int argc __unused, char **argv __unused) 304 { 305 306 settftpmode("netascii"); 307 } 308 309 static void 310 settftpmode(const char *newmode) 311 { 312 strcpy(mode, newmode); 313 if (verbose) 314 printf("mode set to %s\n", mode); 315 } 316 317 318 /* 319 * Send file(s). 320 */ 321 void 322 put(int argc, char **argv) 323 { 324 int fd; 325 int n; 326 char *cp, *targ; 327 328 if (argc < 2) { 329 strcpy(line, "send "); 330 printf("(file) "); 331 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 332 makeargv(); 333 argc = margc; 334 argv = margv; 335 } 336 if (argc < 2) { 337 putusage(argv[0]); 338 return; 339 } 340 targ = argv[argc - 1]; 341 if (strrchr(argv[argc - 1], ':')) { 342 for (n = 1; n < argc - 1; n++) 343 if (strchr(argv[n], ':')) { 344 putusage(argv[0]); 345 return; 346 } 347 cp = argv[argc - 1]; 348 targ = strrchr(cp, ':'); 349 *targ++ = 0; 350 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 351 cp[strlen(cp) - 1] = '\0'; 352 cp++; 353 } 354 setpeer0(cp, NULL); 355 } 356 if (!connected) { 357 printf("No target machine specified.\n"); 358 return; 359 } 360 if (argc < 4) { 361 cp = argc == 2 ? tail(targ) : argv[1]; 362 fd = open(cp, O_RDONLY); 363 if (fd < 0) { 364 warn("%s", cp); 365 return; 366 } 367 if (verbose) 368 printf("putting %s to %s:%s [%s]\n", 369 cp, hostname, targ, mode); 370 xmitfile(fd, targ, mode); 371 return; 372 } 373 /* this assumes the target is a directory */ 374 /* on a remote unix system. hmmmm. */ 375 cp = strchr(targ, '\0'); 376 *cp++ = '/'; 377 for (n = 1; n < argc - 1; n++) { 378 strcpy(cp, tail(argv[n])); 379 fd = open(argv[n], O_RDONLY); 380 if (fd < 0) { 381 warn("%s", argv[n]); 382 continue; 383 } 384 if (verbose) 385 printf("putting %s to %s:%s [%s]\n", 386 argv[n], hostname, targ, mode); 387 xmitfile(fd, targ, mode); 388 } 389 } 390 391 static void 392 putusage(char *s) 393 { 394 printf("usage: %s file ... host:target, or\n", s); 395 printf(" %s file ... target (when already connected)\n", s); 396 } 397 398 /* 399 * Receive file(s). 400 */ 401 void 402 get(int argc, char **argv) 403 { 404 int fd; 405 int n; 406 char *cp; 407 char *src; 408 409 if (argc < 2) { 410 strcpy(line, "get "); 411 printf("(files) "); 412 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 413 makeargv(); 414 argc = margc; 415 argv = margv; 416 } 417 if (argc < 2) { 418 getusage(argv[0]); 419 return; 420 } 421 if (!connected) { 422 for (n = 1; n < argc ; n++) 423 if (strrchr(argv[n], ':') == 0) { 424 getusage(argv[0]); 425 return; 426 } 427 } 428 for (n = 1; n < argc ; n++) { 429 src = strrchr(argv[n], ':'); 430 if (src == NULL) 431 src = argv[n]; 432 else { 433 *src++ = 0; 434 cp = argv[n]; 435 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 436 cp[strlen(cp) - 1] = '\0'; 437 cp++; 438 } 439 setpeer0(cp, NULL); 440 if (!connected) 441 continue; 442 } 443 if (argc < 4) { 444 cp = argc == 3 ? argv[2] : tail(src); 445 fd = creat(cp, 0644); 446 if (fd < 0) { 447 warn("%s", cp); 448 return; 449 } 450 if (verbose) 451 printf("getting from %s:%s to %s [%s]\n", 452 hostname, src, cp, mode); 453 recvfile(fd, src, mode); 454 break; 455 } 456 cp = tail(src); /* new .. jdg */ 457 fd = creat(cp, 0644); 458 if (fd < 0) { 459 warn("%s", cp); 460 continue; 461 } 462 if (verbose) 463 printf("getting from %s:%s to %s [%s]\n", 464 hostname, src, cp, mode); 465 recvfile(fd, src, mode); 466 } 467 } 468 469 static void 470 getusage(char *s) 471 { 472 printf("usage: %s host:file host:file ... file, or\n", s); 473 printf(" %s file file ... file if connected\n", s); 474 } 475 476 int rexmtval = TIMEOUT; 477 478 void 479 setrexmt(int argc, char **argv) 480 { 481 int t; 482 483 if (argc < 2) { 484 strcpy(line, "Rexmt-timeout "); 485 printf("(value) "); 486 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 487 makeargv(); 488 argc = margc; 489 argv = margv; 490 } 491 if (argc != 2) { 492 printf("usage: %s value\n", argv[0]); 493 return; 494 } 495 t = atoi(argv[1]); 496 if (t < 0) 497 printf("%s: bad value\n", argv[1]); 498 else 499 rexmtval = t; 500 } 501 502 int maxtimeout = 5 * TIMEOUT; 503 504 void 505 settimeout(int argc, char **argv) 506 { 507 int t; 508 509 if (argc < 2) { 510 strcpy(line, "Maximum-timeout "); 511 printf("(value) "); 512 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 513 makeargv(); 514 argc = margc; 515 argv = margv; 516 } 517 if (argc != 2) { 518 printf("usage: %s value\n", argv[0]); 519 return; 520 } 521 t = atoi(argv[1]); 522 if (t < 0) 523 printf("%s: bad value\n", argv[1]); 524 else 525 maxtimeout = t; 526 } 527 528 void 529 status(int argc __unused, char **argv __unused) 530 { 531 if (connected) 532 printf("Connected to %s.\n", hostname); 533 else 534 printf("Not connected.\n"); 535 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 536 verbose ? "on" : "off", tftp_trace ? "on" : "off"); 537 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 538 rexmtval, maxtimeout); 539 } 540 541 void 542 intr(int signo __unused) 543 { 544 signal(SIGALRM, SIG_IGN); 545 alarm(0); 546 longjmp(toplevel, -1); 547 } 548 549 char * 550 tail(char *filename) 551 { 552 char *s; 553 554 while (*filename) { 555 s = strrchr(filename, '/'); 556 if (s == NULL) 557 break; 558 if (s[1]) 559 return (s + 1); 560 *s = '\0'; 561 } 562 return (filename); 563 } 564 565 static const char * 566 command_prompt(void) 567 { 568 return ("tftp> "); 569 } 570 571 /* 572 * Command parser. 573 */ 574 static void 575 command(void) 576 { 577 HistEvent he; 578 struct cmd *c; 579 static EditLine *el; 580 static History *hist; 581 const char *bp; 582 char *cp; 583 int len, num, vrbose; 584 585 vrbose = isatty(0); 586 if (vrbose) { 587 el = el_init("tftp", stdin, stdout, stderr); 588 hist = history_init(); 589 history(hist, &he, H_SETSIZE, 100); 590 el_set(el, EL_HIST, history, hist); 591 el_set(el, EL_EDITOR, "emacs"); 592 el_set(el, EL_PROMPT, command_prompt); 593 el_set(el, EL_SIGNAL, 1); 594 el_source(el, NULL); 595 } 596 for (;;) { 597 if (vrbose) { 598 if ((bp = el_gets(el, &num)) == NULL || num == 0) 599 exit(txrx_error); 600 len = (num > MAXLINE) ? MAXLINE : num; 601 memcpy(line, bp, len); 602 line[len] = '\0'; 603 history(hist, &he, H_ENTER, bp); 604 } else { 605 if (fgets(line, sizeof line , stdin) == 0) { 606 if (feof(stdin)) { 607 exit(txrx_error); 608 } else { 609 continue; 610 } 611 } 612 } 613 if ((cp = strchr(line, '\n'))) 614 *cp = '\0'; 615 if (line[0] == 0) 616 continue; 617 makeargv(); 618 if (margc == 0) 619 continue; 620 c = getcmd(margv[0]); 621 if (c == (struct cmd *)-1) { 622 printf("?Ambiguous command\n"); 623 continue; 624 } 625 if (c == NULL) { 626 printf("?Invalid command\n"); 627 continue; 628 } 629 (*c->handler)(margc, margv); 630 } 631 } 632 633 struct cmd * 634 getcmd(char *name) 635 { 636 const char *p; 637 char *q; 638 struct cmd *c, *found; 639 int nmatches, longest; 640 641 longest = 0; 642 nmatches = 0; 643 found = NULL; 644 for (c = cmdtab; (p = c->name) != NULL; c++) { 645 for (q = name; *q == *p++; q++) 646 if (*q == 0) /* exact match? */ 647 return (c); 648 if (!*q) { /* the name was a prefix */ 649 if (q - name > longest) { 650 longest = q - name; 651 nmatches = 1; 652 found = c; 653 } else if (q - name == longest) 654 nmatches++; 655 } 656 } 657 if (nmatches > 1) 658 return ((struct cmd *)-1); 659 return (found); 660 } 661 662 /* 663 * Slice a string up into argc/argv. 664 */ 665 static void 666 makeargv(void) 667 { 668 char *cp; 669 char **argp = margv; 670 671 margc = 0; 672 if ((cp = strchr(line, '\n'))) 673 *cp = '\0'; 674 for (cp = line; margc < MAX_MARGV -1 && *cp;) { 675 while (isspace(*cp)) 676 cp++; 677 if (*cp == '\0') 678 break; 679 *argp++ = cp; 680 margc += 1; 681 while (*cp != '\0' && !isspace(*cp)) 682 cp++; 683 if (*cp == '\0') 684 break; 685 *cp++ = '\0'; 686 } 687 *argp++ = NULL; 688 } 689 690 void 691 quit(int argc __unused, char **argv __unused) 692 { 693 694 exit(txrx_error); 695 } 696 697 /* 698 * Help command. 699 */ 700 void 701 help(int argc, char **argv) 702 { 703 struct cmd *c; 704 705 if (argc == 1) { 706 printf("Commands may be abbreviated. Commands are:\n\n"); 707 for (c = cmdtab; c->name; c++) 708 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 709 return; 710 } 711 while (--argc > 0) { 712 char *arg; 713 arg = *++argv; 714 c = getcmd(arg); 715 if (c == (struct cmd *)-1) 716 printf("?Ambiguous help command %s\n", arg); 717 else if (c == NULL) 718 printf("?Invalid help command %s\n", arg); 719 else 720 printf("%s\n", c->help); 721 } 722 } 723 724 void 725 settrace(int argc __unused, char **argv __unused) 726 { 727 tftp_trace = !tftp_trace; 728 printf("Packet tracing %s.\n", tftp_trace ? "on" : "off"); 729 } 730 731 void 732 setverbose(int argc __unused, char **argv __unused) 733 { 734 verbose = !verbose; 735 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 736 } 737