summaryrefslogtreecommitdiffstats
path: root/l4check/l4check.c
diff options
context:
space:
mode:
Diffstat (limited to 'l4check/l4check.c')
-rw-r--r--l4check/l4check.c822
1 files changed, 822 insertions, 0 deletions
diff --git a/l4check/l4check.c b/l4check/l4check.c
new file mode 100644
index 0000000..a096fff
--- /dev/null
+++ b/l4check/l4check.c
@@ -0,0 +1,822 @@
+/*
+ * (C)Copyright March, 2000 - Darren Reed.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <net/if.h>
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "ip_compat.h"
+#include "ip_fil.h"
+#include "ip_nat.h"
+#include "ipl.h"
+
+#include "ipf.h"
+
+extern char *optarg;
+
+
+typedef struct l4cfg {
+ struct l4cfg *l4_next;
+ struct ipnat l4_nat; /* NAT rule */
+ struct sockaddr_in l4_sin; /* remote socket to connect */
+ time_t l4_last; /* when we last connected */
+ int l4_alive; /* 1 = remote alive */
+ int l4_fd;
+ int l4_rw; /* 0 = reading, 1 = writing */
+ char *l4_rbuf; /* read buffer */
+ int l4_rsize; /* size of buffer */
+ int l4_rlen; /* how much used */
+ char *l4_wptr; /* next byte to write */
+ int l4_wlen; /* length yet to be written */
+} l4cfg_t;
+
+
+l4cfg_t *l4list = NULL;
+char *response = NULL;
+char *probe = NULL;
+l4cfg_t template;
+int frequency = 20;
+int ctimeout = 1;
+int rtimeout = 1;
+size_t plen = 0;
+size_t rlen = 0;
+int natfd = -1;
+int opts = 0;
+
+#if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
+# define strerror(x) sys_errlist[x]
+#endif
+
+
+char *copystr(dst, src)
+char *dst, *src;
+{
+ register char *s, *t, c;
+ register int esc = 0;
+
+ for (s = src, t = dst; s && t && (c = *s++); )
+ if (esc) {
+ esc = 0;
+ switch (c)
+ {
+ case 'n' :
+ *t++ = '\n';
+ break;
+ case 'r' :
+ *t++ = '\r';
+ break;
+ case 't' :
+ *t++ = '\t';
+ break;
+ }
+ } else if (c != '\\')
+ *t++ = c;
+ else
+ esc = 1;
+ *t = '\0';
+ return dst;
+}
+
+void addnat(l4)
+l4cfg_t *l4;
+{
+
+ ipnat_t *ipn = &l4->l4_nat;
+
+ printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0].in4),
+ ipn->in_outmsk, ntohs(ipn->in_pmin));
+ printf("%s,%u\n", inet_ntoa(ipn->in_in[0].in4), ntohs(ipn->in_pnext));
+ if (!(opts & OPT_DONOTHING)) {
+ ipfobj_t obj;
+
+ bzero(&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = sizeof(*ipn);
+ obj.ipfo_ptr = ipn;
+
+ if (ioctl(natfd, SIOCADNAT, &obj) == -1)
+ perror("ioctl(SIOCADNAT)");
+ }
+}
+
+
+void delnat(l4)
+l4cfg_t *l4;
+{
+ ipnat_t *ipn = &l4->l4_nat;
+
+ printf("Remove NAT rule for %s/%#x,%u -> ",
+ inet_ntoa(ipn->in_out[0].in4), ipn->in_outmsk, ipn->in_pmin);
+ printf("%s,%u\n", inet_ntoa(ipn->in_in[0].in4), ipn->in_pnext);
+ if (!(opts & OPT_DONOTHING)) {
+ ipfobj_t obj;
+
+ bzero(&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = sizeof(*ipn);
+ obj.ipfo_ptr = ipn;
+
+ if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
+ perror("ioctl(SIOCRMNAT)");
+ }
+}
+
+
+void connectl4(l4)
+l4cfg_t *l4;
+{
+ l4->l4_rw = 1;
+ l4->l4_rlen = 0;
+ l4->l4_wlen = plen;
+ if (!l4->l4_wlen) {
+ l4->l4_alive = 1;
+ addnat(l4);
+ } else
+ l4->l4_wptr = probe;
+}
+
+
+void closel4(l4, dead)
+l4cfg_t *l4;
+int dead;
+{
+ close(l4->l4_fd);
+ l4->l4_fd = -1;
+ l4->l4_rw = -1;
+ if (dead && l4->l4_alive) {
+ l4->l4_alive = 0;
+ delnat(l4);
+ }
+}
+
+
+void connectfd(l4)
+l4cfg_t *l4;
+{
+ if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin,
+ sizeof(l4->l4_sin)) == -1) {
+ if (errno == EISCONN) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Connected fd %d\n",
+ l4->l4_fd);
+ connectl4(l4);
+ return;
+ }
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Connect failed fd %d: %s\n",
+ l4->l4_fd, strerror(errno));
+ closel4(l4, 1);
+ return;
+ }
+ l4->l4_rw = 1;
+}
+
+
+void writefd(l4)
+l4cfg_t *l4;
+{
+ int n, i, fd;
+
+ fd = l4->l4_fd;
+
+ if (l4->l4_rw == -2) {
+ connectfd(l4);
+ return;
+ }
+
+ n = l4->l4_wlen;
+
+ i = send(fd, l4->l4_wptr, n, 0);
+ if (i == 0 || i == -1) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Send on fd %d failed: %s\n",
+ fd, strerror(errno));
+ closel4(l4, 1);
+ } else {
+ l4->l4_wptr += i;
+ l4->l4_wlen -= i;
+ if (l4->l4_wlen == 0)
+ l4->l4_rw = 0;
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd);
+ }
+}
+
+
+void readfd(l4)
+l4cfg_t *l4;
+{
+ char buf[80], *ptr;
+ int n, i, fd;
+
+ fd = l4->l4_fd;
+
+ if (l4->l4_rw == -2) {
+ connectfd(l4);
+ return;
+ }
+
+ if (l4->l4_rsize) {
+ n = l4->l4_rsize - l4->l4_rlen;
+ ptr = l4->l4_rbuf + l4->l4_rlen;
+ } else {
+ n = sizeof(buf) - 1;
+ ptr = buf;
+ }
+
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Read %d bytes on fd %d to %p\n",
+ n, fd, ptr);
+ i = recv(fd, ptr, n, 0);
+ if (i == 0 || i == -1) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Read error on fd %d: %s\n",
+ fd, (i == 0) ? "EOF" : strerror(errno));
+ closel4(l4, 1);
+ } else {
+ if (ptr == buf)
+ ptr[i] = '\0';
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
+ fd, i, i, i, ptr);
+ if (ptr != buf) {
+ l4->l4_rlen += i;
+ if (l4->l4_rlen >= l4->l4_rsize) {
+ if (!strncmp(response, l4->l4_rbuf,
+ l4->l4_rsize)) {
+ printf("%d: Good response\n",
+ fd);
+ if (!l4->l4_alive) {
+ l4->l4_alive = 1;
+ addnat(l4);
+ }
+ closel4(l4, 0);
+ } else {
+ if (opts & OPT_VERBOSE)
+ printf("%d: Bad response\n",
+ fd);
+ closel4(l4, 1);
+ }
+ }
+ } else if (!l4->l4_alive) {
+ l4->l4_alive = 1;
+ addnat(l4);
+ closel4(l4, 0);
+ }
+ }
+}
+
+
+int runconfig()
+{
+ int fd, opt, res, mfd, i;
+ struct timeval tv;
+ time_t now, now1;
+ fd_set rfd, wfd;
+ l4cfg_t *l4;
+
+ mfd = 0;
+ opt = 1;
+ now = time(NULL);
+
+ /*
+ * First, initiate connections that are closed, as required.
+ */
+ for (l4 = l4list; l4; l4 = l4->l4_next) {
+ if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) {
+ l4->l4_last = now;
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1)
+ continue;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
+ sizeof(opt));
+#ifdef O_NONBLOCK
+ if ((res = fcntl(fd, F_GETFL, 0)) != -1)
+ fcntl(fd, F_SETFL, res | O_NONBLOCK);
+#endif
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "Connecting to %s,%d (fd %d)...",
+ inet_ntoa(l4->l4_sin.sin_addr),
+ ntohs(l4->l4_sin.sin_port), fd);
+ if (connect(fd, (struct sockaddr *)&l4->l4_sin,
+ sizeof(l4->l4_sin)) == -1) {
+ if (errno != EINPROGRESS) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "failed\n");
+ perror("connect");
+ close(fd);
+ fd = -1;
+ } else {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "waiting\n");
+ l4->l4_rw = -2;
+ }
+ } else {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "connected\n");
+ connectl4(l4);
+ }
+ l4->l4_fd = fd;
+ }
+ }
+
+ /*
+ * Now look for fd's which we're expecting to read/write from.
+ */
+ FD_ZERO(&rfd);
+ FD_ZERO(&wfd);
+ tv.tv_sec = MIN(rtimeout, ctimeout);
+ tv.tv_usec = 0;
+
+ for (l4 = l4list; l4; l4 = l4->l4_next)
+ if (l4->l4_rw == 0) {
+ if (now - l4->l4_last > rtimeout) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "%d: Read timeout\n",
+ l4->l4_fd);
+ closel4(l4, 1);
+ continue;
+ }
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Wait for read on fd %d\n",
+ l4->l4_fd);
+ FD_SET(l4->l4_fd, &rfd);
+ if (l4->l4_fd > mfd)
+ mfd = l4->l4_fd;
+ } else if ((l4->l4_rw == 1 && l4->l4_wlen) ||
+ l4->l4_rw == -2) {
+ if ((l4->l4_rw == -2) &&
+ (now - l4->l4_last > ctimeout)) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "%d: connect timeout\n",
+ l4->l4_fd);
+ closel4(l4);
+ continue;
+ }
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Wait for write on fd %d\n",
+ l4->l4_fd);
+ FD_SET(l4->l4_fd, &wfd);
+ if (l4->l4_fd > mfd)
+ mfd = l4->l4_fd;
+ }
+
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1,
+ tv.tv_sec);
+ i = select(mfd + 1, &rfd, &wfd, NULL, &tv);
+ if (i == -1) {
+ perror("select");
+ return -1;
+ }
+
+ now1 = time(NULL);
+
+ for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) {
+ if (l4->l4_fd < 0)
+ continue;
+ if (FD_ISSET(l4->l4_fd, &rfd)) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Ready to read on fd %d\n",
+ l4->l4_fd);
+ readfd(l4);
+ i--;
+ }
+
+ if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) {
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Ready to write on fd %d\n",
+ l4->l4_fd);
+ writefd(l4);
+ i--;
+ }
+ }
+ return 0;
+}
+
+
+int gethostport(str, lnum, ipp, portp)
+char *str;
+int lnum;
+u_32_t *ipp;
+u_short *portp;
+{
+ struct servent *sp;
+ struct hostent *hp;
+ char *host, *port;
+
+ host = str;
+ port = strchr(host, ',');
+ if (port)
+ *port++ = '\0';
+
+#ifdef HAVE_INET_ATON
+ if (ISDIGIT(*host) && inet_aton(host, &ip))
+ *ipp = ip.s_addr;
+#else
+ if (ISDIGIT(*host))
+ *ipp = inet_addr(host);
+#endif
+ else {
+ if (!(hp = gethostbyname(host))) {
+ fprintf(stderr, "%d: can't resolve hostname: %s\n",
+ lnum, host);
+ return 0;
+ }
+ *ipp = *(u_32_t *)hp->h_addr;
+ }
+
+ if (port) {
+ if (ISDIGIT(*port))
+ *portp = htons(atoi(port));
+ else {
+ sp = getservbyname(port, "tcp");
+ if (sp)
+ *portp = sp->s_port;
+ else {
+ fprintf(stderr, "%d: unknown service %s\n",
+ lnum, port);
+ return 0;
+ }
+ }
+ } else
+ *portp = 0;
+ return 1;
+}
+
+
+char *mapfile(file, sizep)
+char *file;
+size_t *sizep;
+{
+ struct stat sb;
+ caddr_t addr;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ perror("open(mapfile)");
+ return NULL;
+ }
+
+ if (fstat(fd, &sb) == -1) {
+ perror("fstat(mapfile)");
+ close(fd);
+ return NULL;
+ }
+
+ addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (addr == (caddr_t)-1) {
+ perror("mmap(mapfile)");
+ close(fd);
+ return NULL;
+ }
+ close(fd);
+ *sizep = sb.st_size;
+ return (char *)addr;
+}
+
+
+int readconfig(filename)
+char *filename;
+{
+ char c, buf[512], *s, *t, *errtxt = NULL, *line;
+ int num, err = 0;
+ ipnat_t *ipn;
+ l4cfg_t *l4;
+ FILE *fp;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ perror("open(configfile)");
+ return -1;
+ }
+
+ bzero((char *)&template, sizeof(template));
+ template.l4_fd = -1;
+ template.l4_rw = -1;
+ template.l4_sin.sin_family = AF_INET;
+ ipn = &template.l4_nat;
+ ipn->in_flags = IPN_TCP|IPN_ROUNDR;
+ ipn->in_redir = NAT_REDIRECT;
+
+ for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
+ s = strchr(buf, '\n');
+ if (!s) {
+ fprintf(stderr, "%d: line too long\n", num);
+ fclose(fp);
+ return -1;
+ }
+
+ *s = '\0';
+
+ /*
+ * lines which are comments
+ */
+ s = strchr(buf, '#');
+ if (s)
+ *s = '\0';
+
+ /*
+ * Skip leading whitespace
+ */
+ for (line = buf; (c = *line) && ISSPACE(c); line++)
+ ;
+ if (!*line)
+ continue;
+
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Parsing: [%s]\n", line);
+ t = strtok(line, " \t");
+ if (!t)
+ continue;
+ if (!strcasecmp(t, "interface")) {
+ s = strtok(NULL, " \t");
+ if (s)
+ t = strtok(NULL, "\t");
+ if (!s || !t) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+
+ if (!strchr(t, ',')) {
+ fprintf(stderr,
+ "%d: local address,port missing\n",
+ num);
+ err = -1;
+ break;
+ }
+
+ strncpy(ipn->in_ifnames[0], s, LIFNAMSIZ);
+ strncpy(ipn->in_ifnames[1], s, LIFNAMSIZ);
+ if (!gethostport(t, num, &ipn->in_outip,
+ &ipn->in_pmin)) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ ipn->in_outmsk = 0xffffffff;
+ ipn->in_pmax = ipn->in_pmin;
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "Interface %s %s/%#x port %u\n",
+ ipn->in_ifnames[0],
+ inet_ntoa(ipn->in_out[0].in4),
+ ipn->in_outmsk, ipn->in_pmin);
+ } else if (!strcasecmp(t, "remote")) {
+ if (!*ipn->in_ifnames[0]) {
+ fprintf(stderr,
+ "%d: ifname not set prior to remote\n",
+ num);
+ err = -1;
+ break;
+ }
+ s = strtok(NULL, " \t");
+ if (s)
+ t = strtok(NULL, "");
+ if (!s || !t || strcasecmp(s, "server")) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+
+ ipn->in_pnext = 0;
+ if (!gethostport(t, num, &ipn->in_inip,
+ &ipn->in_pnext)) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ ipn->in_inmsk = 0xffffffff;
+ if (ipn->in_pnext == 0)
+ ipn->in_pnext = ipn->in_pmin;
+
+ l4 = (l4cfg_t *)malloc(sizeof(*l4));
+ if (!l4) {
+ fprintf(stderr, "%d: out of memory (%d)\n",
+ num, sizeof(*l4));
+ err = -1;
+ break;
+ }
+ bcopy((char *)&template, (char *)l4, sizeof(*l4));
+ l4->l4_sin.sin_addr = ipn->in_in[0].in4;
+ l4->l4_sin.sin_port = ipn->in_pnext;
+ l4->l4_next = l4list;
+ l4list = l4;
+ } else if (!strcasecmp(t, "connect")) {
+ s = strtok(NULL, " \t");
+ if (s)
+ t = strtok(NULL, "\t");
+ if (!s || !t) {
+ errtxt = line;
+ err = -1;
+ break;
+ } else if (!strcasecmp(s, "timeout")) {
+ ctimeout = atoi(t);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "connect timeout %d\n",
+ ctimeout);
+ } else if (!strcasecmp(s, "frequency")) {
+ frequency = atoi(t);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "connect frequency %d\n",
+ frequency);
+ } else {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ } else if (!strcasecmp(t, "probe")) {
+ s = strtok(NULL, " \t");
+ if (!s) {
+ errtxt = line;
+ err = -1;
+ break;
+ } else if (!strcasecmp(s, "string")) {
+ if (probe) {
+ fprintf(stderr,
+ "%d: probe already set\n",
+ num);
+ err = -1;
+ break;
+ }
+ t = strtok(NULL, "");
+ if (!t) {
+ fprintf(stderr,
+ "%d: No probe string\n", num);
+ err = -1;
+ break;
+ }
+
+ probe = malloc(strlen(t));
+ copystr(probe, t);
+ plen = strlen(probe);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Probe string [%s]\n",
+ probe);
+ } else if (!strcasecmp(s, "file")) {
+ t = strtok(NULL, " \t");
+ if (!t) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ if (probe) {
+ fprintf(stderr,
+ "%d: probe already set\n",
+ num);
+ err = -1;
+ break;
+ }
+ probe = mapfile(t, &plen);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "Probe file %s len %u@%p\n",
+ t, plen, probe);
+ }
+ } else if (!strcasecmp(t, "response")) {
+ s = strtok(NULL, " \t");
+ if (!s) {
+ errtxt = line;
+ err = -1;
+ break;
+ } else if (!strcasecmp(s, "timeout")) {
+ t = strtok(NULL, " \t");
+ if (!t) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ rtimeout = atoi(t);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "response timeout %d\n",
+ rtimeout);
+ } else if (!strcasecmp(s, "string")) {
+ if (response) {
+ fprintf(stderr,
+ "%d: response already set\n",
+ num);
+ err = -1;
+ break;
+ }
+ response = strdup(strtok(NULL, ""));
+ rlen = strlen(response);
+ template.l4_rsize = rlen;
+ template.l4_rbuf = malloc(rlen);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "Response string [%s]\n",
+ response);
+ } else if (!strcasecmp(s, "file")) {
+ t = strtok(NULL, " \t");
+ if (!t) {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ if (response) {
+ fprintf(stderr,
+ "%d: response already set\n",
+ num);
+ err = -1;
+ break;
+ }
+ response = mapfile(t, &rlen);
+ template.l4_rsize = rlen;
+ template.l4_rbuf = malloc(rlen);
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr,
+ "Response file %s len %u@%p\n",
+ t, rlen, response);
+ }
+ } else {
+ errtxt = line;
+ err = -1;
+ break;
+ }
+ }
+
+ if (errtxt)
+ fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
+ fclose(fp);
+ return err;
+}
+
+
+void usage(prog)
+char *prog;
+{
+ fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
+ exit(1);
+}
+
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ char *config = NULL;
+ int c;
+
+ while ((c = getopt(argc, argv, "f:nv")) != -1)
+ switch (c)
+ {
+ case 'f' :
+ config = optarg;
+ break;
+ case 'n' :
+ opts |= OPT_DONOTHING;
+ break;
+ case 'v' :
+ opts |= OPT_VERBOSE;
+ break;
+ }
+
+ if (config == NULL)
+ usage(argv[0]);
+
+ if (readconfig(config))
+ exit(1);
+
+ if (!l4list) {
+ fprintf(stderr, "No remote servers, exiting.");
+ exit(1);
+ }
+
+ if (!(opts & OPT_DONOTHING)) {
+ natfd = open(IPNAT_NAME, O_RDWR);
+ if (natfd == -1) {
+ perror("open(IPL_NAT)");
+ exit(1);
+ }
+ }
+
+ if (opts & OPT_VERBOSE)
+ fprintf(stderr, "Starting...\n");
+ while (runconfig() == 0)
+ ;
+
+ exit(1);
+}
OpenPOWER on IntegriCloud