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.4 2003/11/04 16:52:01 drhodus 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 char *prompt = "tftp"; 79 jmp_buf toplevel; 80 volatile int txrx_error; 81 void intr(); 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 *, 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(char *); 104 105 #define HELPINDENT (sizeof("connect")) 106 107 struct cmd { 108 char *name; 109 char *help; 110 void (*handler)(int, char **); 111 }; 112 113 char vhelp[] = "toggle verbose mode"; 114 char thelp[] = "toggle packet tracing"; 115 char chelp[] = "connect to remote tftp"; 116 char qhelp[] = "exit tftp"; 117 char hhelp[] = "print help information"; 118 char shelp[] = "send file"; 119 char rhelp[] = "receive file"; 120 char mhelp[] = "set file transfer mode"; 121 char sthelp[] = "show current status"; 122 char xhelp[] = "set per-packet retransmission timeout"; 123 char ihelp[] = "set total retransmission timeout"; 124 char ashelp[] = "set mode to netascii"; 125 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 { 0 } 142 }; 143 144 struct cmd *getcmd(); 145 char *tail(); 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, char *port) 167 { 168 struct addrinfo hints, *res0, *res; 169 int error; 170 struct sockaddr_storage ss; 171 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 char *m_name; 255 char *m_mode; 256 } modes[] = { 257 { "ascii", "netascii" }, 258 { "netascii", "netascii" }, 259 { "binary", "octet" }, 260 { "image", "octet" }, 261 { "octet", "octet" }, 262 /* { "mail", "mail" }, */ 263 { 0, 0 } 264 }; 265 266 void 267 modecmd(int argc, char **argv) 268 { 269 register struct modes *p; 270 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, char **argv) 301 { 302 303 settftpmode("octet"); 304 } 305 306 void 307 setascii(int argc, char **argv) 308 { 309 310 settftpmode("netascii"); 311 } 312 313 static void 314 settftpmode(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 register int n; 330 register 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 (rindex(argv[argc - 1], ':')) { 346 char *cp; 347 348 for (n = 1; n < argc - 1; n++) 349 if (index(argv[n], ':')) { 350 putusage(argv[0]); 351 return; 352 } 353 cp = argv[argc - 1]; 354 targ = rindex(cp, ':'); 355 *targ++ = 0; 356 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 357 cp[strlen(cp) - 1] = '\0'; 358 cp++; 359 } 360 setpeer0(cp, NULL); 361 } 362 if (!connected) { 363 printf("No target machine specified.\n"); 364 return; 365 } 366 if (argc < 4) { 367 cp = argc == 2 ? tail(targ) : argv[1]; 368 fd = open(cp, O_RDONLY); 369 if (fd < 0) { 370 warn("%s", cp); 371 return; 372 } 373 if (verbose) 374 printf("putting %s to %s:%s [%s]\n", 375 cp, hostname, targ, mode); 376 xmitfile(fd, targ, mode); 377 return; 378 } 379 /* this assumes the target is a directory */ 380 /* on a remote unix system. hmmmm. */ 381 cp = index(targ, '\0'); 382 *cp++ = '/'; 383 for (n = 1; n < argc - 1; n++) { 384 strcpy(cp, tail(argv[n])); 385 fd = open(argv[n], O_RDONLY); 386 if (fd < 0) { 387 warn("%s", argv[n]); 388 continue; 389 } 390 if (verbose) 391 printf("putting %s to %s:%s [%s]\n", 392 argv[n], hostname, targ, mode); 393 xmitfile(fd, targ, mode); 394 } 395 } 396 397 static void 398 putusage(char *s) 399 { 400 printf("usage: %s file ... host:target, or\n", s); 401 printf(" %s file ... target (when already connected)\n", s); 402 } 403 404 /* 405 * Receive file(s). 406 */ 407 void 408 get(int argc, char **argv) 409 { 410 int fd; 411 register int n; 412 register char *cp; 413 char *src; 414 415 if (argc < 2) { 416 strcpy(line, "get "); 417 printf("(files) "); 418 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 419 makeargv(); 420 argc = margc; 421 argv = margv; 422 } 423 if (argc < 2) { 424 getusage(argv[0]); 425 return; 426 } 427 if (!connected) { 428 for (n = 1; n < argc ; n++) 429 if (rindex(argv[n], ':') == 0) { 430 getusage(argv[0]); 431 return; 432 } 433 } 434 for (n = 1; n < argc ; n++) { 435 src = rindex(argv[n], ':'); 436 if (src == NULL) 437 src = argv[n]; 438 else { 439 char *cp; 440 *src++ = 0; 441 cp = argv[n]; 442 if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') { 443 cp[strlen(cp) - 1] = '\0'; 444 cp++; 445 } 446 setpeer0(cp, NULL); 447 if (!connected) 448 continue; 449 } 450 if (argc < 4) { 451 cp = argc == 3 ? argv[2] : tail(src); 452 fd = creat(cp, 0644); 453 if (fd < 0) { 454 warn("%s", cp); 455 return; 456 } 457 if (verbose) 458 printf("getting from %s:%s to %s [%s]\n", 459 hostname, src, cp, mode); 460 recvfile(fd, src, mode); 461 break; 462 } 463 cp = tail(src); /* new .. jdg */ 464 fd = creat(cp, 0644); 465 if (fd < 0) { 466 warn("%s", cp); 467 continue; 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 } 474 } 475 476 static void 477 getusage(char *s) 478 { 479 printf("usage: %s host:file host:file ... file, or\n", s); 480 printf(" %s file file ... file if connected\n", s); 481 } 482 483 int rexmtval = TIMEOUT; 484 485 void 486 setrexmt(int argc, char **argv) 487 { 488 int t; 489 490 if (argc < 2) { 491 strcpy(line, "Rexmt-timeout "); 492 printf("(value) "); 493 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 494 makeargv(); 495 argc = margc; 496 argv = margv; 497 } 498 if (argc != 2) { 499 printf("usage: %s value\n", argv[0]); 500 return; 501 } 502 t = atoi(argv[1]); 503 if (t < 0) 504 printf("%s: bad value\n", argv[1]); 505 else 506 rexmtval = t; 507 } 508 509 int maxtimeout = 5 * TIMEOUT; 510 511 void 512 settimeout(int argc, char **argv) 513 { 514 int t; 515 516 if (argc < 2) { 517 strcpy(line, "Maximum-timeout "); 518 printf("(value) "); 519 fgets(&line[strlen(line)], sizeof line - strlen(line), stdin); 520 makeargv(); 521 argc = margc; 522 argv = margv; 523 } 524 if (argc != 2) { 525 printf("usage: %s value\n", argv[0]); 526 return; 527 } 528 t = atoi(argv[1]); 529 if (t < 0) 530 printf("%s: bad value\n", argv[1]); 531 else 532 maxtimeout = t; 533 } 534 535 void 536 status(int argc, char **argv) 537 { 538 if (connected) 539 printf("Connected to %s.\n", hostname); 540 else 541 printf("Not connected.\n"); 542 printf("Mode: %s Verbose: %s Tracing: %s\n", mode, 543 verbose ? "on" : "off", trace ? "on" : "off"); 544 printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", 545 rexmtval, maxtimeout); 546 } 547 548 void 549 intr(void) 550 { 551 552 signal(SIGALRM, SIG_IGN); 553 alarm(0); 554 longjmp(toplevel, -1); 555 } 556 557 char * 558 tail(char *filename) 559 { 560 register char *s; 561 562 while (*filename) { 563 s = rindex(filename, '/'); 564 if (s == NULL) 565 break; 566 if (s[1]) 567 return (s + 1); 568 *s = '\0'; 569 } 570 return (filename); 571 } 572 573 /* 574 * Command parser. 575 */ 576 static void 577 command(void) 578 { 579 register struct cmd *c; 580 char *cp; 581 582 for (;;) { 583 printf("%s> ", prompt); 584 if (fgets(line, sizeof line , stdin) == 0) { 585 if (feof(stdin)) { 586 exit(txrx_error); 587 } else { 588 continue; 589 } 590 } 591 if ((cp = strchr(line, '\n'))) 592 *cp = '\0'; 593 if (line[0] == 0) 594 continue; 595 makeargv(); 596 if (margc == 0) 597 continue; 598 c = getcmd(margv[0]); 599 if (c == (struct cmd *)-1) { 600 printf("?Ambiguous command\n"); 601 continue; 602 } 603 if (c == 0) { 604 printf("?Invalid command\n"); 605 continue; 606 } 607 (*c->handler)(margc, margv); 608 } 609 } 610 611 struct cmd * 612 getcmd(register char *name) 613 { 614 register char *p, *q; 615 register struct cmd *c, *found; 616 register int nmatches, longest; 617 618 longest = 0; 619 nmatches = 0; 620 found = 0; 621 for (c = cmdtab; (p = c->name) != NULL; c++) { 622 for (q = name; *q == *p++; q++) 623 if (*q == 0) /* exact match? */ 624 return (c); 625 if (!*q) { /* the name was a prefix */ 626 if (q - name > longest) { 627 longest = q - name; 628 nmatches = 1; 629 found = c; 630 } else if (q - name == longest) 631 nmatches++; 632 } 633 } 634 if (nmatches > 1) 635 return ((struct cmd *)-1); 636 return (found); 637 } 638 639 /* 640 * Slice a string up into argc/argv. 641 */ 642 static void 643 makeargv(void) 644 { 645 register char *cp; 646 register char **argp = margv; 647 648 margc = 0; 649 if ((cp = strchr(line, '\n'))) 650 *cp = '\0'; 651 for (cp = line; margc < MAX_MARGV -1 && *cp;) { 652 while (isspace(*cp)) 653 cp++; 654 if (*cp == '\0') 655 break; 656 *argp++ = cp; 657 margc += 1; 658 while (*cp != '\0' && !isspace(*cp)) 659 cp++; 660 if (*cp == '\0') 661 break; 662 *cp++ = '\0'; 663 } 664 *argp++ = 0; 665 } 666 667 void 668 quit(int argc, char **argv) 669 { 670 671 exit(txrx_error); 672 } 673 674 /* 675 * Help command. 676 */ 677 void 678 help(int argc, char **argv) 679 { 680 register struct cmd *c; 681 682 if (argc == 1) { 683 printf("Commands may be abbreviated. Commands are:\n\n"); 684 for (c = cmdtab; c->name; c++) 685 printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); 686 return; 687 } 688 while (--argc > 0) { 689 register char *arg; 690 arg = *++argv; 691 c = getcmd(arg); 692 if (c == (struct cmd *)-1) 693 printf("?Ambiguous help command %s\n", arg); 694 else if (c == (struct cmd *)0) 695 printf("?Invalid help command %s\n", arg); 696 else 697 printf("%s\n", c->help); 698 } 699 } 700 701 void 702 settrace(int argc, char **argv) 703 { 704 trace = !trace; 705 printf("Packet tracing %s.\n", trace ? "on" : "off"); 706 } 707 708 void 709 setverbose(int argc, char **argv) 710 { 711 verbose = !verbose; 712 printf("Verbose mode %s.\n", verbose ? "on" : "off"); 713 } 714