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