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