l4check.c revision 60841
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 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 154void connectfd(l4) 155l4cfg_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 176void writefd(l4) 177l4cfg_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 208void readfd(l4) 209l4cfg_t *l4; 210{ 211 char buf[80], *ptr; 212 int n, i, fd; 213 214 fd = l4->l4_fd; 215 216 if (l4->l4_rw == -2) { 217 connectfd(l4); 218 return; 219 } 220 221 if (l4->l4_rsize) { 222 n = l4->l4_rsize - l4->l4_rlen; 223 ptr = l4->l4_rbuf + l4->l4_rlen; 224 } else { 225 n = sizeof(buf) - 1; 226 ptr = buf; 227 } 228 229 if (opts & OPT_VERBOSE) 230 fprintf(stderr, "Read %d bytes on fd %d to %p\n", 231 n, fd, ptr); 232 i = recv(fd, ptr, n, 0); 233 if (i == 0 || i == -1) { 234 if (opts & OPT_VERBOSE) 235 fprintf(stderr, "Read error on fd %d: %s\n", 236 fd, (i == 0) ? "EOF" : strerror(errno)); 237 closel4(l4, 1); 238 } else { 239 if (ptr == buf) 240 ptr[i] = '\0'; 241 if (opts & OPT_VERBOSE) 242 fprintf(stderr, "%d: Read %d bytes [%*.*s]\n", 243 fd, i, i, i, ptr); 244 if (ptr != buf) { 245 l4->l4_rlen += i; 246 if (l4->l4_rlen >= l4->l4_rsize) { 247 if (!strncmp(response, l4->l4_rbuf, 248 l4->l4_rsize)) { 249 printf("%d: Good response\n", 250 fd); 251 if (!l4->l4_alive) { 252 l4->l4_alive = 1; 253 addnat(l4); 254 } 255 closel4(l4, 0); 256 } else { 257 if (opts & OPT_VERBOSE) 258 printf("%d: Bad response\n", 259 fd); 260 closel4(l4, 1); 261 } 262 } 263 } else if (!l4->l4_alive) { 264 l4->l4_alive = 1; 265 addnat(l4); 266 closel4(l4, 0); 267 } 268 } 269} 270 271 272int runconfig() 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 402int gethostport(str, lnum, ipp, portp) 403char *str; 404int lnum; 405u_32_t *ipp; 406u_short *portp; 407{ 408 struct servent *sp; 409 struct hostent *hp; 410 char *host, *port; 411 struct in_addr ip; 412 413 host = str; 414 port = strchr(host, ','); 415 if (port) 416 *port++ = '\0'; 417 418#ifdef HAVE_INET_ATON 419 if (isdigit(*host) && inet_aton(host, &ip)) 420 *ipp = ip.s_addr; 421#else 422 if (isdigit(*host)) 423 *ipp = inet_addr(host); 424#endif 425 else { 426 if (!(hp = gethostbyname(host))) { 427 fprintf(stderr, "%d: can't resolve hostname: %s\n", 428 lnum, host); 429 return 0; 430 } 431 *ipp = *(u_32_t *)hp->h_addr; 432 } 433 434 if (port) { 435 if (isdigit(*port)) 436 *portp = htons(atoi(port)); 437 else { 438 sp = getservbyname(port, "tcp"); 439 if (sp) 440 *portp = sp->s_port; 441 else { 442 fprintf(stderr, "%d: unknown service %s\n", 443 lnum, port); 444 return 0; 445 } 446 } 447 } else 448 *portp = 0; 449 return 1; 450} 451 452 453char *mapfile(file, sizep) 454char *file; 455size_t *sizep; 456{ 457 struct stat sb; 458 caddr_t addr; 459 int fd; 460 461 fd = open(file, O_RDONLY); 462 if (fd == -1) { 463 perror("open(mapfile)"); 464 return NULL; 465 } 466 467 if (fstat(fd, &sb) == -1) { 468 perror("fstat(mapfile)"); 469 close(fd); 470 return NULL; 471 } 472 473 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 474 if (addr == (caddr_t)-1) { 475 perror("mmap(mapfile)"); 476 close(fd); 477 return NULL; 478 } 479 close(fd); 480 *sizep = sb.st_size; 481 return (char *)addr; 482} 483 484 485int readconfig(filename) 486char *filename; 487{ 488 char c, buf[512], *s, *t, *errtxt = NULL, *line; 489 int num, err = 0; 490 ipnat_t *ipn; 491 l4cfg_t *l4; 492 FILE *fp; 493 494 fp = fopen(filename, "r"); 495 if (!fp) { 496 perror("open(configfile)"); 497 return -1; 498 } 499 500 bzero((char *)&template, sizeof(template)); 501 template.l4_fd = -1; 502 template.l4_rw = -1; 503 template.l4_sin.sin_family = AF_INET; 504 ipn = &template.l4_nat; 505 ipn->in_flags = IPN_TCP|IPN_ROUNDR; 506 ipn->in_redir = NAT_REDIRECT; 507 508 for (num = 1; fgets(buf, sizeof(buf), fp); num++) { 509 s = strchr(buf, '\n'); 510 if (!s) { 511 fprintf(stderr, "%d: line too long\n", num); 512 fclose(fp); 513 return -1; 514 } 515 516 *s = '\0'; 517 518 /* 519 * lines which are comments 520 */ 521 s = strchr(buf, '#'); 522 if (s) 523 *s = '\0'; 524 525 /* 526 * Skip leading whitespace 527 */ 528 for (line = buf; (c = *line) && isspace(c); line++) 529 ; 530 if (!*line) 531 continue; 532 533 if (opts & OPT_VERBOSE) 534 fprintf(stderr, "Parsing: [%s]\n", line); 535 t = strtok(line, " \t"); 536 if (!t) 537 continue; 538 if (!strcasecmp(t, "interface")) { 539 s = strtok(NULL, " \t"); 540 if (s) 541 t = strtok(NULL, "\t"); 542 if (!s || !t) { 543 errtxt = line; 544 err = -1; 545 break; 546 } 547 548 if (!strchr(t, ',')) { 549 fprintf(stderr, 550 "%d: local address,port missing\n", 551 num); 552 err = -1; 553 break; 554 } 555 556 strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname)); 557 if (!gethostport(t, num, &ipn->in_outip, 558 &ipn->in_pmin)) { 559 errtxt = line; 560 err = -1; 561 break; 562 } 563 ipn->in_outmsk = 0xffffffff; 564 ipn->in_pmax = ipn->in_pmin; 565 if (opts & OPT_VERBOSE) 566 fprintf(stderr, 567 "Interface %s %s/%#x port %u\n", 568 ipn->in_ifname, 569 inet_ntoa(ipn->in_out[0]), 570 ipn->in_outmsk, ipn->in_pmin); 571 } else if (!strcasecmp(t, "remote")) { 572 if (!*ipn->in_ifname) { 573 fprintf(stderr, 574 "%d: ifname not set prior to remote\n", 575 num); 576 err = -1; 577 break; 578 } 579 s = strtok(NULL, " \t"); 580 if (s) 581 t = strtok(NULL, ""); 582 if (!s || !t || strcasecmp(s, "server")) { 583 errtxt = line; 584 err = -1; 585 break; 586 } 587 588 ipn->in_pnext = 0; 589 if (!gethostport(t, num, &ipn->in_inip, 590 &ipn->in_pnext)) { 591 errtxt = line; 592 err = -1; 593 break; 594 } 595 ipn->in_inmsk = 0xffffffff; 596 if (ipn->in_pnext == 0) 597 ipn->in_pnext = ipn->in_pmin; 598 599 l4 = (l4cfg_t *)malloc(sizeof(*l4)); 600 if (!l4) { 601 fprintf(stderr, "%d: out of memory (%d)\n", 602 num, sizeof(*l4)); 603 err = -1; 604 break; 605 } 606 bcopy((char *)&template, (char *)l4, sizeof(*l4)); 607 l4->l4_sin.sin_addr = ipn->in_in[0]; 608 l4->l4_sin.sin_port = ipn->in_pnext; 609 l4->l4_next = l4list; 610 l4list = l4; 611 } else if (!strcasecmp(t, "connect")) { 612 s = strtok(NULL, " \t"); 613 if (s) 614 t = strtok(NULL, "\t"); 615 if (!s || !t) { 616 errtxt = line; 617 err = -1; 618 break; 619 } else if (!strcasecmp(s, "timeout")) { 620 ctimeout = atoi(t); 621 if (opts & OPT_VERBOSE) 622 fprintf(stderr, "connect timeout %d\n", 623 ctimeout); 624 } else if (!strcasecmp(s, "frequency")) { 625 frequency = atoi(t); 626 if (opts & OPT_VERBOSE) 627 fprintf(stderr, 628 "connect frequency %d\n", 629 frequency); 630 } else { 631 errtxt = line; 632 err = -1; 633 break; 634 } 635 } else if (!strcasecmp(t, "probe")) { 636 s = strtok(NULL, " \t"); 637 if (!s) { 638 errtxt = line; 639 err = -1; 640 break; 641 } else if (!strcasecmp(s, "string")) { 642 if (probe) { 643 fprintf(stderr, 644 "%d: probe already set\n", 645 num); 646 err = -1; 647 break; 648 } 649 t = strtok(NULL, ""); 650 if (!t) { 651 fprintf(stderr, 652 "%d: No probe string\n", num); 653 err = -1; 654 break; 655 } 656 657 probe = malloc(strlen(t)); 658 copystr(probe, t); 659 plen = strlen(probe); 660 if (opts & OPT_VERBOSE) 661 fprintf(stderr, "Probe string [%s]\n", 662 probe); 663 } else if (!strcasecmp(s, "file")) { 664 t = strtok(NULL, " \t"); 665 if (!t) { 666 errtxt = line; 667 err = -1; 668 break; 669 } 670 if (probe) { 671 fprintf(stderr, 672 "%d: probe already set\n", 673 num); 674 err = -1; 675 break; 676 } 677 probe = mapfile(t, &plen); 678 if (opts & OPT_VERBOSE) 679 fprintf(stderr, 680 "Probe file %s len %u@%p\n", 681 t, plen, probe); 682 } 683 } else if (!strcasecmp(t, "response")) { 684 s = strtok(NULL, " \t"); 685 if (!s) { 686 errtxt = line; 687 err = -1; 688 break; 689 } else if (!strcasecmp(s, "timeout")) { 690 t = strtok(NULL, " \t"); 691 if (!t) { 692 errtxt = line; 693 err = -1; 694 break; 695 } 696 rtimeout = atoi(t); 697 if (opts & OPT_VERBOSE) 698 fprintf(stderr, 699 "response timeout %d\n", 700 rtimeout); 701 } else if (!strcasecmp(s, "string")) { 702 if (response) { 703 fprintf(stderr, 704 "%d: response already set\n", 705 num); 706 err = -1; 707 break; 708 } 709 response = strdup(strtok(NULL, "")); 710 rlen = strlen(response); 711 template.l4_rsize = rlen; 712 template.l4_rbuf = malloc(rlen); 713 if (opts & OPT_VERBOSE) 714 fprintf(stderr, 715 "Response string [%s]\n", 716 response); 717 } else if (!strcasecmp(s, "file")) { 718 t = strtok(NULL, " \t"); 719 if (!t) { 720 errtxt = line; 721 err = -1; 722 break; 723 } 724 if (response) { 725 fprintf(stderr, 726 "%d: response already set\n", 727 num); 728 err = -1; 729 break; 730 } 731 response = mapfile(t, &rlen); 732 template.l4_rsize = rlen; 733 template.l4_rbuf = malloc(rlen); 734 if (opts & OPT_VERBOSE) 735 fprintf(stderr, 736 "Response file %s len %u@%p\n", 737 t, rlen, response); 738 } 739 } else { 740 errtxt = line; 741 err = -1; 742 break; 743 } 744 } 745 746 if (errtxt) 747 fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt); 748 fclose(fp); 749 return err; 750} 751 752 753void usage(prog) 754char *prog; 755{ 756 fprintf(stderr, "Usage: %s -f <configfile>\n", prog); 757 exit(1); 758} 759 760 761int main(argc, argv) 762int argc; 763char *argv[]; 764{ 765 char *config = NULL; 766 int c; 767 768 while ((c = getopt(argc, argv, "f:nv")) != -1) 769 switch (c) 770 { 771 case 'f' : 772 config = optarg; 773 break; 774 case 'n' : 775 opts |= OPT_DONOTHING; 776 break; 777 case 'v' : 778 opts |= OPT_VERBOSE; 779 break; 780 } 781 782 if (config == NULL) 783 usage(argv[0]); 784 785 if (readconfig(config)) 786 exit(1); 787 788 if (!l4list) { 789 fprintf(stderr, "No remote servers, exiting."); 790 exit(1); 791 } 792 793 if (!(opts & OPT_DONOTHING)) { 794 natfd = open(IPL_NAT, O_RDWR); 795 if (natfd == -1) { 796 perror("open(IPL_NAT)"); 797 exit(1); 798 } 799 } 800 801 if (opts & OPT_VERBOSE) 802 fprintf(stderr, "Starting...\n"); 803 while (runconfig() == 0) 804 ; 805} 806