1 2 /* 3 * (C)Copyright (C) 2012 by Darren Reed. 4 */ 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <sys/mman.h> 8 #include <sys/socket.h> 9 #include <sys/time.h> 10 #include <sys/ioctl.h> 11 12 #include <netinet/in.h> 13 #include <netinet/in_systm.h> 14 #include <netinet/ip.h> 15 16 #include <net/if.h> 17 18 #include <stdio.h> 19 #include <netdb.h> 20 #include <string.h> 21 #include <ctype.h> 22 #include <fcntl.h> 23 #include <errno.h> 24 #include <stdlib.h> 25 26 #include "ip_compat.h" 27 #include "ip_fil.h" 28 #include "ip_nat.h" 29 30 #include "ipf.h" 31 32 extern char *optarg; 33 34 35 typedef struct l4cfg { 36 struct l4cfg *l4_next; 37 struct ipnat l4_nat; /* NAT rule */ 38 struct sockaddr_in l4_sin; /* remote socket to connect */ 39 time_t l4_last; /* when we last connected */ 40 int l4_alive; /* 1 = remote alive */ 41 int l4_fd; 42 int l4_rw; /* 0 = reading, 1 = writing */ 43 char *l4_rbuf; /* read buffer */ 44 int l4_rsize; /* size of buffer */ 45 int l4_rlen; /* how much used */ 46 char *l4_wptr; /* next byte to write */ 47 int l4_wlen; /* length yet to be written */ 48 } l4cfg_t; 49 50 51 l4cfg_t *l4list = NULL; 52 char *response = NULL; 53 char *probe = NULL; 54 l4cfg_t template; 55 int frequency = 20; 56 int ctimeout = 1; 57 int rtimeout = 1; 58 size_t plen = 0; 59 size_t rlen = 0; 60 int natfd = -1; 61 int opts = 0; 62 63 #if defined(sun) && !defined(__svr4__) && !defined(__SVR4) 64 # define strerror(x) sys_errlist[x] 65 #endif 66 67 68 char * 69 copystr(char *dst, char *src) 70 { 71 register char *s, *t, c; 72 register int esc = 0; 73 74 for (s = src, t = dst; s && t && (c = *s++); ) 75 if (esc) { 76 esc = 0; 77 switch (c) 78 { 79 case 'n' : 80 *t++ = '\n'; 81 break; 82 case 'r' : 83 *t++ = '\r'; 84 break; 85 case 't' : 86 *t++ = '\t'; 87 break; 88 } 89 } else if (c != '\\') 90 *t++ = c; 91 else 92 esc = 1; 93 *t = '\0'; 94 return(dst); 95 } 96 97 void 98 addnat(l4cfg_t *l4) 99 { 100 ipnat_t *ipn = &l4->l4_nat; 101 102 printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]), 103 ipn->in_outmsk, ntohs(ipn->in_pmin)); 104 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext)); 105 if (!(opts & OPT_DONOTHING)) { 106 if (ioctl(natfd, SIOCADNAT, &ipn) == -1) 107 perror("ioctl(SIOCADNAT)"); 108 } 109 } 110 111 112 void 113 delnat(l4cfg_t *l4) 114 { 115 ipnat_t *ipn = &l4->l4_nat; 116 117 printf("Remove NAT rule for %s/%#x,%u -> ", 118 inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin); 119 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext); 120 if (!(opts & OPT_DONOTHING)) { 121 if (ioctl(natfd, SIOCRMNAT, &ipn) == -1) 122 perror("ioctl(SIOCRMNAT)"); 123 } 124 } 125 126 127 void 128 connectl4(l4cfg_t *l4) 129 { 130 l4->l4_rw = 1; 131 l4->l4_rlen = 0; 132 l4->l4_wlen = plen; 133 if (!l4->l4_wlen) { 134 l4->l4_alive = 1; 135 addnat(l4); 136 } else 137 l4->l4_wptr = probe; 138 } 139 140 141 void 142 closel4(l4cfg_t *l4, int dead) 143 { 144 close(l4->l4_fd); 145 l4->l4_fd = -1; 146 l4->l4_rw = -1; 147 if (dead && l4->l4_alive) { 148 l4->l4_alive = 0; 149 delnat(l4); 150 } 151 } 152 153 154 void 155 connectfd(l4cfg_t *l4) 156 { 157 if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin, 158 sizeof(l4->l4_sin)) == -1) { 159 if (errno == EISCONN) { 160 if (opts & OPT_VERBOSE) 161 fprintf(stderr, "Connected fd %d\n", 162 l4->l4_fd); 163 connectl4(l4); 164 return; 165 } 166 if (opts & OPT_VERBOSE) 167 fprintf(stderr, "Connect failed fd %d: %s\n", 168 l4->l4_fd, strerror(errno)); 169 closel4(l4, 1); 170 return; 171 } 172 l4->l4_rw = 1; 173 } 174 175 176 void 177 writefd(l4cfg_t *l4) 178 { 179 char buf[80], *ptr; 180 int n, i, fd; 181 182 fd = l4->l4_fd; 183 184 if (l4->l4_rw == -2) { 185 connectfd(l4); 186 return; 187 } 188 189 n = l4->l4_wlen; 190 191 i = send(fd, l4->l4_wptr, n, 0); 192 if (i == 0 || i == -1) { 193 if (opts & OPT_VERBOSE) 194 fprintf(stderr, "Send on fd %d failed: %s\n", 195 fd, strerror(errno)); 196 closel4(l4, 1); 197 } else { 198 l4->l4_wptr += i; 199 l4->l4_wlen -= i; 200 if (l4->l4_wlen == 0) 201 l4->l4_rw = 0; 202 if (opts & OPT_VERBOSE) 203 fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd); 204 } 205 } 206 207 208 void readfd(l4cfg_t *l4) 209 { 210 char buf[80], *ptr; 211 int n, i, fd; 212 213 fd = l4->l4_fd; 214 215 if (l4->l4_rw == -2) { 216 connectfd(l4); 217 return; 218 } 219 220 if (l4->l4_rsize) { 221 n = l4->l4_rsize - l4->l4_rlen; 222 ptr = l4->l4_rbuf + l4->l4_rlen; 223 } else { 224 n = sizeof(buf) - 1; 225 ptr = buf; 226 } 227 228 if (opts & OPT_VERBOSE) 229 fprintf(stderr, "Read %d bytes on fd %d to %p\n", 230 n, fd, ptr); 231 i = recv(fd, ptr, n, 0); 232 if (i == 0 || i == -1) { 233 if (opts & OPT_VERBOSE) 234 fprintf(stderr, "Read error on fd %d: %s\n", 235 fd, (i == 0) ? "EOF" : strerror(errno)); 236 closel4(l4, 1); 237 } else { 238 if (ptr == buf) 239 ptr[i] = '\0'; 240 if (opts & OPT_VERBOSE) 241 fprintf(stderr, "%d: Read %d bytes [%*.*s]\n", 242 fd, i, i, i, ptr); 243 if (ptr != buf) { 244 l4->l4_rlen += i; 245 if (l4->l4_rlen >= l4->l4_rsize) { 246 if (!strncmp(response, l4->l4_rbuf, 247 l4->l4_rsize)) { 248 printf("%d: Good response\n", 249 fd); 250 if (!l4->l4_alive) { 251 l4->l4_alive = 1; 252 addnat(l4); 253 } 254 closel4(l4, 0); 255 } else { 256 if (opts & OPT_VERBOSE) 257 printf("%d: Bad response\n", 258 fd); 259 closel4(l4, 1); 260 } 261 } 262 } else if (!l4->l4_alive) { 263 l4->l4_alive = 1; 264 addnat(l4); 265 closel4(l4, 0); 266 } 267 } 268 } 269 270 271 int 272 runconfig(void) 273 { 274 int fd, opt, res, mfd, i; 275 struct timeval tv; 276 time_t now, now1; 277 fd_set rfd, wfd; 278 l4cfg_t *l4; 279 280 mfd = 0; 281 opt = 1; 282 now = time(NULL); 283 284 /* 285 * First, initiate connections that are closed, as required. 286 */ 287 for (l4 = l4list; l4; l4 = l4->l4_next) { 288 if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) { 289 l4->l4_last = now; 290 fd = socket(AF_INET, SOCK_STREAM, 0); 291 if (fd == -1) 292 continue; 293 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, 294 sizeof(opt)); 295 #ifdef O_NONBLOCK 296 if ((res = fcntl(fd, F_GETFL, 0)) != -1) 297 fcntl(fd, F_SETFL, res | O_NONBLOCK); 298 #endif 299 if (opts & OPT_VERBOSE) 300 fprintf(stderr, 301 "Connecting to %s,%d (fd %d)...", 302 inet_ntoa(l4->l4_sin.sin_addr), 303 ntohs(l4->l4_sin.sin_port), fd); 304 if (connect(fd, (struct sockaddr *)&l4->l4_sin, 305 sizeof(l4->l4_sin)) == -1) { 306 if (errno != EINPROGRESS) { 307 if (opts & OPT_VERBOSE) 308 fprintf(stderr, "failed\n"); 309 perror("connect"); 310 close(fd); 311 fd = -1; 312 } else { 313 if (opts & OPT_VERBOSE) 314 fprintf(stderr, "waiting\n"); 315 l4->l4_rw = -2; 316 } 317 } else { 318 if (opts & OPT_VERBOSE) 319 fprintf(stderr, "connected\n"); 320 connectl4(l4); 321 } 322 l4->l4_fd = fd; 323 } 324 } 325 326 /* 327 * Now look for fd's which we're expecting to read/write from. 328 */ 329 FD_ZERO(&rfd); 330 FD_ZERO(&wfd); 331 tv.tv_sec = MIN(rtimeout, ctimeout); 332 tv.tv_usec = 0; 333 334 for (l4 = l4list; l4; l4 = l4->l4_next) 335 if (l4->l4_rw == 0) { 336 if (now - l4->l4_last > rtimeout) { 337 if (opts & OPT_VERBOSE) 338 fprintf(stderr, "%d: Read timeout\n", 339 l4->l4_fd); 340 closel4(l4, 1); 341 continue; 342 } 343 if (opts & OPT_VERBOSE) 344 fprintf(stderr, "Wait for read on fd %d\n", 345 l4->l4_fd); 346 FD_SET(l4->l4_fd, &rfd); 347 if (l4->l4_fd > mfd) 348 mfd = l4->l4_fd; 349 } else if ((l4->l4_rw == 1 && l4->l4_wlen) || 350 l4->l4_rw == -2) { 351 if ((l4->l4_rw == -2) && 352 (now - l4->l4_last > ctimeout)) { 353 if (opts & OPT_VERBOSE) 354 fprintf(stderr, 355 "%d: connect timeout\n", 356 l4->l4_fd); 357 closel4(l4); 358 continue; 359 } 360 if (opts & OPT_VERBOSE) 361 fprintf(stderr, "Wait for write on fd %d\n", 362 l4->l4_fd); 363 FD_SET(l4->l4_fd, &wfd); 364 if (l4->l4_fd > mfd) 365 mfd = l4->l4_fd; 366 } 367 368 if (opts & OPT_VERBOSE) 369 fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1, 370 tv.tv_sec); 371 i = select(mfd + 1, &rfd, &wfd, NULL, &tv); 372 if (i == -1) { 373 perror("select"); 374 return(-1); 375 } 376 377 now1 = time(NULL); 378 379 for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) { 380 if (l4->l4_fd < 0) 381 continue; 382 if (FD_ISSET(l4->l4_fd, &rfd)) { 383 if (opts & OPT_VERBOSE) 384 fprintf(stderr, "Ready to read on fd %d\n", 385 l4->l4_fd); 386 readfd(l4); 387 i--; 388 } 389 390 if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) { 391 if (opts & OPT_VERBOSE) 392 fprintf(stderr, "Ready to write on fd %d\n", 393 l4->l4_fd); 394 writefd(l4); 395 i--; 396 } 397 } 398 return(0); 399 } 400 401 402 int 403 gethostport(char *str, int lnum, u_32_t *ipp, u_short *portp) 404 { 405 struct servent *sp; 406 struct hostent *hp; 407 char *host, *port; 408 struct in_addr ip; 409 410 host = str; 411 port = strchr(host, ','); 412 if (port) 413 *port++ = '\0'; 414 415 #ifdef HAVE_INET_ATON 416 if (ISDIGIT(*host) && inet_aton(host, &ip)) 417 *ipp = ip.s_addr; 418 #else 419 if (ISDIGIT(*host)) 420 *ipp = inet_addr(host); 421 #endif 422 else { 423 if (!(hp = gethostbyname(host))) { 424 fprintf(stderr, "%d: can't resolve hostname: %s\n", 425 lnum, host); 426 return(0); 427 } 428 *ipp = *(u_32_t *)hp->h_addr; 429 } 430 431 if (port) { 432 if (ISDIGIT(*port)) 433 *portp = htons(atoi(port)); 434 else { 435 sp = getservbyname(port, "tcp"); 436 if (sp) 437 *portp = sp->s_port; 438 else { 439 fprintf(stderr, "%d: unknown service %s\n", 440 lnum, port); 441 return(0); 442 } 443 } 444 } else 445 *portp = 0; 446 return(1); 447 } 448 449 450 char * 451 mapfile(char *file, size_t *sizep) 452 { 453 struct stat sb; 454 caddr_t addr; 455 int fd; 456 457 fd = open(file, O_RDONLY); 458 if (fd == -1) { 459 perror("open(mapfile)"); 460 return(NULL); 461 } 462 463 if (fstat(fd, &sb) == -1) { 464 perror("fstat(mapfile)"); 465 close(fd); 466 return(NULL); 467 } 468 469 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 470 if (addr == (caddr_t)-1) { 471 perror("mmap(mapfile)"); 472 close(fd); 473 return(NULL); 474 } 475 close(fd); 476 *sizep = sb.st_size; 477 return(char *)addr; 478 } 479 480 481 int 482 readconfig(char *filename) 483 { 484 char c, buf[512], *s, *t, *errtxt = NULL, *line; 485 int num, err = 0; 486 ipnat_t *ipn; 487 l4cfg_t *l4; 488 FILE *fp; 489 490 fp = fopen(filename, "r"); 491 if (!fp) { 492 perror("open(configfile)"); 493 return(-1); 494 } 495 496 bzero((char *)&template, sizeof(template)); 497 template.l4_fd = -1; 498 template.l4_rw = -1; 499 template.l4_sin.sin_family = AF_INET; 500 ipn = &template.l4_nat; 501 ipn->in_flags = IPN_TCP|IPN_ROUNDR; 502 ipn->in_redir = NAT_REDIRECT; 503 504 for (num = 1; fgets(buf, sizeof(buf), fp); num++) { 505 s = strchr(buf, '\n'); 506 if (!s) { 507 fprintf(stderr, "%d: line too long\n", num); 508 fclose(fp); 509 return(-1); 510 } 511 512 *s = '\0'; 513 514 /* 515 * lines which are comments 516 */ 517 s = strchr(buf, '#'); 518 if (s) 519 *s = '\0'; 520 521 /* 522 * Skip leading whitespace 523 */ 524 for (line = buf; (c = *line) && ISSPACE(c); line++) 525 ; 526 if (!*line) 527 continue; 528 529 if (opts & OPT_VERBOSE) 530 fprintf(stderr, "Parsing: [%s]\n", line); 531 t = strtok(line, " \t"); 532 if (!t) 533 continue; 534 if (!strcasecmp(t, "interface")) { 535 s = strtok(NULL, " \t"); 536 if (s) 537 t = strtok(NULL, "\t"); 538 if (!s || !t) { 539 errtxt = line; 540 err = -1; 541 break; 542 } 543 544 if (!strchr(t, ',')) { 545 fprintf(stderr, 546 "%d: local address,port missing\n", 547 num); 548 err = -1; 549 break; 550 } 551 552 strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname)); 553 if (!gethostport(t, num, &ipn->in_outip, 554 &ipn->in_pmin)) { 555 errtxt = line; 556 err = -1; 557 break; 558 } 559 ipn->in_outmsk = 0xffffffff; 560 ipn->in_pmax = ipn->in_pmin; 561 if (opts & OPT_VERBOSE) 562 fprintf(stderr, 563 "Interface %s %s/%#x port %u\n", 564 ipn->in_ifname, 565 inet_ntoa(ipn->in_out[0]), 566 ipn->in_outmsk, ipn->in_pmin); 567 } else if (!strcasecmp(t, "remote")) { 568 if (!*ipn->in_ifname) { 569 fprintf(stderr, 570 "%d: ifname not set prior to remote\n", 571 num); 572 err = -1; 573 break; 574 } 575 s = strtok(NULL, " \t"); 576 if (s) 577 t = strtok(NULL, ""); 578 if (!s || !t || strcasecmp(s, "server")) { 579 errtxt = line; 580 err = -1; 581 break; 582 } 583 584 ipn->in_pnext = 0; 585 if (!gethostport(t, num, &ipn->in_inip, 586 &ipn->in_pnext)) { 587 errtxt = line; 588 err = -1; 589 break; 590 } 591 ipn->in_inmsk = 0xffffffff; 592 if (ipn->in_pnext == 0) 593 ipn->in_pnext = ipn->in_pmin; 594 595 l4 = (l4cfg_t *)malloc(sizeof(*l4)); 596 if (!l4) { 597 fprintf(stderr, "%d: out of memory (%d)\n", 598 num, sizeof(*l4)); 599 err = -1; 600 break; 601 } 602 bcopy((char *)&template, (char *)l4, sizeof(*l4)); 603 l4->l4_sin.sin_addr = ipn->in_in[0]; 604 l4->l4_sin.sin_port = ipn->in_pnext; 605 l4->l4_next = l4list; 606 l4list = l4; 607 } else if (!strcasecmp(t, "connect")) { 608 s = strtok(NULL, " \t"); 609 if (s) 610 t = strtok(NULL, "\t"); 611 if (!s || !t) { 612 errtxt = line; 613 err = -1; 614 break; 615 } else if (!strcasecmp(s, "timeout")) { 616 ctimeout = atoi(t); 617 if (opts & OPT_VERBOSE) 618 fprintf(stderr, "connect timeout %d\n", 619 ctimeout); 620 } else if (!strcasecmp(s, "frequency")) { 621 frequency = atoi(t); 622 if (opts & OPT_VERBOSE) 623 fprintf(stderr, 624 "connect frequency %d\n", 625 frequency); 626 } else { 627 errtxt = line; 628 err = -1; 629 break; 630 } 631 } else if (!strcasecmp(t, "probe")) { 632 s = strtok(NULL, " \t"); 633 if (!s) { 634 errtxt = line; 635 err = -1; 636 break; 637 } else if (!strcasecmp(s, "string")) { 638 if (probe) { 639 fprintf(stderr, 640 "%d: probe already set\n", 641 num); 642 err = -1; 643 break; 644 } 645 t = strtok(NULL, ""); 646 if (!t) { 647 fprintf(stderr, 648 "%d: No probe string\n", num); 649 err = -1; 650 break; 651 } 652 653 probe = malloc(strlen(t)); 654 copystr(probe, t); 655 plen = strlen(probe); 656 if (opts & OPT_VERBOSE) 657 fprintf(stderr, "Probe string [%s]\n", 658 probe); 659 } else if (!strcasecmp(s, "file")) { 660 t = strtok(NULL, " \t"); 661 if (!t) { 662 errtxt = line; 663 err = -1; 664 break; 665 } 666 if (probe) { 667 fprintf(stderr, 668 "%d: probe already set\n", 669 num); 670 err = -1; 671 break; 672 } 673 probe = mapfile(t, &plen); 674 if (opts & OPT_VERBOSE) 675 fprintf(stderr, 676 "Probe file %s len %u@%p\n", 677 t, plen, probe); 678 } 679 } else if (!strcasecmp(t, "response")) { 680 s = strtok(NULL, " \t"); 681 if (!s) { 682 errtxt = line; 683 err = -1; 684 break; 685 } else if (!strcasecmp(s, "timeout")) { 686 t = strtok(NULL, " \t"); 687 if (!t) { 688 errtxt = line; 689 err = -1; 690 break; 691 } 692 rtimeout = atoi(t); 693 if (opts & OPT_VERBOSE) 694 fprintf(stderr, 695 "response timeout %d\n", 696 rtimeout); 697 } else if (!strcasecmp(s, "string")) { 698 if (response) { 699 fprintf(stderr, 700 "%d: response already set\n", 701 num); 702 err = -1; 703 break; 704 } 705 response = strdup(strtok(NULL, "")); 706 rlen = strlen(response); 707 template.l4_rsize = rlen; 708 template.l4_rbuf = malloc(rlen); 709 if (opts & OPT_VERBOSE) 710 fprintf(stderr, 711 "Response string [%s]\n", 712 response); 713 } else if (!strcasecmp(s, "file")) { 714 t = strtok(NULL, " \t"); 715 if (!t) { 716 errtxt = line; 717 err = -1; 718 break; 719 } 720 if (response) { 721 fprintf(stderr, 722 "%d: response already set\n", 723 num); 724 err = -1; 725 break; 726 } 727 response = mapfile(t, &rlen); 728 template.l4_rsize = rlen; 729 template.l4_rbuf = malloc(rlen); 730 if (opts & OPT_VERBOSE) 731 fprintf(stderr, 732 "Response file %s len %u@%p\n", 733 t, rlen, response); 734 } 735 } else { 736 errtxt = line; 737 err = -1; 738 break; 739 } 740 } 741 742 if (errtxt) 743 fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt); 744 fclose(fp); 745 return(err); 746 } 747 748 749 void 750 usage(char *prog) 751 { 752 fprintf(stderr, "Usage: %s -f <configfile>\n", prog); 753 exit(1); 754 } 755 756 757 int 758 main(int argc, char *argv[]) 759 { 760 char *config = NULL; 761 int c; 762 763 while ((c = getopt(argc, argv, "f:nv")) != -1) 764 switch (c) 765 { 766 case 'f' : 767 config = optarg; 768 break; 769 case 'n' : 770 opts |= OPT_DONOTHING; 771 break; 772 case 'v' : 773 opts |= OPT_VERBOSE; 774 break; 775 } 776 777 if (config == NULL) 778 usage(argv[0]); 779 780 if (readconfig(config)) 781 exit(1); 782 783 if (!l4list) { 784 fprintf(stderr, "No remote servers, exiting."); 785 exit(1); 786 } 787 788 if (!(opts & OPT_DONOTHING)) { 789 natfd = open(IPL_NAT, O_RDWR); 790 if (natfd == -1) { 791 perror("open(IPL_NAT)"); 792 exit(1); 793 } 794 } 795 796 if (opts & OPT_VERBOSE) 797 fprintf(stderr, "Starting...\n"); 798 while (runconfig() == 0) 799 ; 800 } 801