4 * (C)Copyright (C) 2012 by Darren Reed.
9 #include <sys/socket.h>
11 #include <sys/ioctl.h>
13 #include <netinet/in.h>
14 #include <netinet/in_systm.h>
15 #include <netinet/ip.h>
27 #include "ip_compat.h"
36 typedef struct l4cfg {
37 struct l4cfg *l4_next;
38 struct ipnat l4_nat; /* NAT rule */
39 struct sockaddr_in l4_sin; /* remote socket to connect */
40 time_t l4_last; /* when we last connected */
41 int l4_alive; /* 1 = remote alive */
43 int l4_rw; /* 0 = reading, 1 = writing */
44 char *l4_rbuf; /* read buffer */
45 int l4_rsize; /* size of buffer */
46 int l4_rlen; /* how much used */
47 char *l4_wptr; /* next byte to write */
48 int l4_wlen; /* length yet to be written */
52 l4cfg_t *l4list = NULL;
53 char *response = NULL;
64 #if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
65 # define strerror(x) sys_errlist[x]
69 char *copystr(dst, src)
72 register char *s, *t, c;
75 for (s = src, t = dst; s && t && (c = *s++); )
101 ipnat_t *ipn = &l4->l4_nat;
103 printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]),
104 ipn->in_outmsk, ntohs(ipn->in_pmin));
105 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext));
106 if (!(opts & OPT_DONOTHING)) {
107 if (ioctl(natfd, SIOCADNAT, &ipn) == -1)
108 perror("ioctl(SIOCADNAT)");
116 ipnat_t *ipn = &l4->l4_nat;
118 printf("Remove NAT rule for %s/%#x,%u -> ",
119 inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin);
120 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext);
121 if (!(opts & OPT_DONOTHING)) {
122 if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
123 perror("ioctl(SIOCRMNAT)");
142 void closel4(l4, dead)
149 if (dead && l4->l4_alive) {
159 if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin,
160 sizeof(l4->l4_sin)) == -1) {
161 if (errno == EISCONN) {
162 if (opts & OPT_VERBOSE)
163 fprintf(stderr, "Connected fd %d\n",
168 if (opts & OPT_VERBOSE)
169 fprintf(stderr, "Connect failed fd %d: %s\n",
170 l4->l4_fd, strerror(errno));
186 if (l4->l4_rw == -2) {
193 i = send(fd, l4->l4_wptr, n, 0);
194 if (i == 0 || i == -1) {
195 if (opts & OPT_VERBOSE)
196 fprintf(stderr, "Send on fd %d failed: %s\n",
197 fd, strerror(errno));
202 if (l4->l4_wlen == 0)
204 if (opts & OPT_VERBOSE)
205 fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd);
218 if (l4->l4_rw == -2) {
224 n = l4->l4_rsize - l4->l4_rlen;
225 ptr = l4->l4_rbuf + l4->l4_rlen;
231 if (opts & OPT_VERBOSE)
232 fprintf(stderr, "Read %d bytes on fd %d to %p\n",
234 i = recv(fd, ptr, n, 0);
235 if (i == 0 || i == -1) {
236 if (opts & OPT_VERBOSE)
237 fprintf(stderr, "Read error on fd %d: %s\n",
238 fd, (i == 0) ? "EOF" : strerror(errno));
243 if (opts & OPT_VERBOSE)
244 fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
248 if (l4->l4_rlen >= l4->l4_rsize) {
249 if (!strncmp(response, l4->l4_rbuf,
251 printf("%d: Good response\n",
259 if (opts & OPT_VERBOSE)
260 printf("%d: Bad response\n",
265 } else if (!l4->l4_alive) {
276 int fd, opt, res, mfd, i;
287 * First, initiate connections that are closed, as required.
289 for (l4 = l4list; l4; l4 = l4->l4_next) {
290 if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) {
292 fd = socket(AF_INET, SOCK_STREAM, 0);
295 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
298 if ((res = fcntl(fd, F_GETFL, 0)) != -1)
299 fcntl(fd, F_SETFL, res | O_NONBLOCK);
301 if (opts & OPT_VERBOSE)
303 "Connecting to %s,%d (fd %d)...",
304 inet_ntoa(l4->l4_sin.sin_addr),
305 ntohs(l4->l4_sin.sin_port), fd);
306 if (connect(fd, (struct sockaddr *)&l4->l4_sin,
307 sizeof(l4->l4_sin)) == -1) {
308 if (errno != EINPROGRESS) {
309 if (opts & OPT_VERBOSE)
310 fprintf(stderr, "failed\n");
315 if (opts & OPT_VERBOSE)
316 fprintf(stderr, "waiting\n");
320 if (opts & OPT_VERBOSE)
321 fprintf(stderr, "connected\n");
329 * Now look for fd's which we're expecting to read/write from.
333 tv.tv_sec = MIN(rtimeout, ctimeout);
336 for (l4 = l4list; l4; l4 = l4->l4_next)
337 if (l4->l4_rw == 0) {
338 if (now - l4->l4_last > rtimeout) {
339 if (opts & OPT_VERBOSE)
340 fprintf(stderr, "%d: Read timeout\n",
345 if (opts & OPT_VERBOSE)
346 fprintf(stderr, "Wait for read on fd %d\n",
348 FD_SET(l4->l4_fd, &rfd);
351 } else if ((l4->l4_rw == 1 && l4->l4_wlen) ||
353 if ((l4->l4_rw == -2) &&
354 (now - l4->l4_last > ctimeout)) {
355 if (opts & OPT_VERBOSE)
357 "%d: connect timeout\n",
362 if (opts & OPT_VERBOSE)
363 fprintf(stderr, "Wait for write on fd %d\n",
365 FD_SET(l4->l4_fd, &wfd);
370 if (opts & OPT_VERBOSE)
371 fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1,
373 i = select(mfd + 1, &rfd, &wfd, NULL, &tv);
381 for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) {
384 if (FD_ISSET(l4->l4_fd, &rfd)) {
385 if (opts & OPT_VERBOSE)
386 fprintf(stderr, "Ready to read on fd %d\n",
392 if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) {
393 if (opts & OPT_VERBOSE)
394 fprintf(stderr, "Ready to write on fd %d\n",
404 int gethostport(str, lnum, ipp, portp)
416 port = strchr(host, ',');
420 #ifdef HAVE_INET_ATON
421 if (ISDIGIT(*host) && inet_aton(host, &ip))
425 *ipp = inet_addr(host);
428 if (!(hp = gethostbyname(host))) {
429 fprintf(stderr, "%d: can't resolve hostname: %s\n",
433 *ipp = *(u_32_t *)hp->h_addr;
438 *portp = htons(atoi(port));
440 sp = getservbyname(port, "tcp");
444 fprintf(stderr, "%d: unknown service %s\n",
455 char *mapfile(file, sizep)
463 fd = open(file, O_RDONLY);
465 perror("open(mapfile)");
469 if (fstat(fd, &sb) == -1) {
470 perror("fstat(mapfile)");
475 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
476 if (addr == (caddr_t)-1) {
477 perror("mmap(mapfile)");
487 int readconfig(filename)
490 char c, buf[512], *s, *t, *errtxt = NULL, *line;
496 fp = fopen(filename, "r");
498 perror("open(configfile)");
502 bzero((char *)&template, sizeof(template));
505 template.l4_sin.sin_family = AF_INET;
506 ipn = &template.l4_nat;
507 ipn->in_flags = IPN_TCP|IPN_ROUNDR;
508 ipn->in_redir = NAT_REDIRECT;
510 for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
511 s = strchr(buf, '\n');
513 fprintf(stderr, "%d: line too long\n", num);
521 * lines which are comments
523 s = strchr(buf, '#');
528 * Skip leading whitespace
530 for (line = buf; (c = *line) && ISSPACE(c); line++)
535 if (opts & OPT_VERBOSE)
536 fprintf(stderr, "Parsing: [%s]\n", line);
537 t = strtok(line, " \t");
540 if (!strcasecmp(t, "interface")) {
541 s = strtok(NULL, " \t");
543 t = strtok(NULL, "\t");
550 if (!strchr(t, ',')) {
552 "%d: local address,port missing\n",
558 strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname));
559 if (!gethostport(t, num, &ipn->in_outip,
565 ipn->in_outmsk = 0xffffffff;
566 ipn->in_pmax = ipn->in_pmin;
567 if (opts & OPT_VERBOSE)
569 "Interface %s %s/%#x port %u\n",
571 inet_ntoa(ipn->in_out[0]),
572 ipn->in_outmsk, ipn->in_pmin);
573 } else if (!strcasecmp(t, "remote")) {
574 if (!*ipn->in_ifname) {
576 "%d: ifname not set prior to remote\n",
581 s = strtok(NULL, " \t");
583 t = strtok(NULL, "");
584 if (!s || !t || strcasecmp(s, "server")) {
591 if (!gethostport(t, num, &ipn->in_inip,
597 ipn->in_inmsk = 0xffffffff;
598 if (ipn->in_pnext == 0)
599 ipn->in_pnext = ipn->in_pmin;
601 l4 = (l4cfg_t *)malloc(sizeof(*l4));
603 fprintf(stderr, "%d: out of memory (%d)\n",
608 bcopy((char *)&template, (char *)l4, sizeof(*l4));
609 l4->l4_sin.sin_addr = ipn->in_in[0];
610 l4->l4_sin.sin_port = ipn->in_pnext;
611 l4->l4_next = l4list;
613 } else if (!strcasecmp(t, "connect")) {
614 s = strtok(NULL, " \t");
616 t = strtok(NULL, "\t");
621 } else if (!strcasecmp(s, "timeout")) {
623 if (opts & OPT_VERBOSE)
624 fprintf(stderr, "connect timeout %d\n",
626 } else if (!strcasecmp(s, "frequency")) {
628 if (opts & OPT_VERBOSE)
630 "connect frequency %d\n",
637 } else if (!strcasecmp(t, "probe")) {
638 s = strtok(NULL, " \t");
643 } else if (!strcasecmp(s, "string")) {
646 "%d: probe already set\n",
651 t = strtok(NULL, "");
654 "%d: No probe string\n", num);
659 probe = malloc(strlen(t));
661 plen = strlen(probe);
662 if (opts & OPT_VERBOSE)
663 fprintf(stderr, "Probe string [%s]\n",
665 } else if (!strcasecmp(s, "file")) {
666 t = strtok(NULL, " \t");
674 "%d: probe already set\n",
679 probe = mapfile(t, &plen);
680 if (opts & OPT_VERBOSE)
682 "Probe file %s len %u@%p\n",
685 } else if (!strcasecmp(t, "response")) {
686 s = strtok(NULL, " \t");
691 } else if (!strcasecmp(s, "timeout")) {
692 t = strtok(NULL, " \t");
699 if (opts & OPT_VERBOSE)
701 "response timeout %d\n",
703 } else if (!strcasecmp(s, "string")) {
706 "%d: response already set\n",
711 response = strdup(strtok(NULL, ""));
712 rlen = strlen(response);
713 template.l4_rsize = rlen;
714 template.l4_rbuf = malloc(rlen);
715 if (opts & OPT_VERBOSE)
717 "Response string [%s]\n",
719 } else if (!strcasecmp(s, "file")) {
720 t = strtok(NULL, " \t");
728 "%d: response already set\n",
733 response = mapfile(t, &rlen);
734 template.l4_rsize = rlen;
735 template.l4_rbuf = malloc(rlen);
736 if (opts & OPT_VERBOSE)
738 "Response file %s len %u@%p\n",
749 fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
758 fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
770 while ((c = getopt(argc, argv, "f:nv")) != -1)
777 opts |= OPT_DONOTHING;
787 if (readconfig(config))
791 fprintf(stderr, "No remote servers, exiting.");
795 if (!(opts & OPT_DONOTHING)) {
796 natfd = open(IPL_NAT, O_RDWR);
798 perror("open(IPL_NAT)");
803 if (opts & OPT_VERBOSE)
804 fprintf(stderr, "Starting...\n");
805 while (runconfig() == 0)