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