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