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