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