1145519Sdarrenr/*	$FreeBSD$	*/
2145510Sdarrenr
360841Sdarrenr/*
4255332Scy * (C)Copyright (C) 2012 by Darren Reed.
560841Sdarrenr */
660841Sdarrenr#include <sys/types.h>
760841Sdarrenr#include <sys/stat.h>
860841Sdarrenr#include <sys/mman.h>
960841Sdarrenr#include <sys/socket.h>
1060841Sdarrenr#include <sys/time.h>
1160841Sdarrenr#include <sys/ioctl.h>
1260841Sdarrenr
1360841Sdarrenr#include <netinet/in.h>
1460841Sdarrenr#include <netinet/in_systm.h>
1560841Sdarrenr#include <netinet/ip.h>
1660841Sdarrenr
1760841Sdarrenr#include <net/if.h>
1860841Sdarrenr
1960841Sdarrenr#include <stdio.h>
2060841Sdarrenr#include <netdb.h>
2160841Sdarrenr#include <string.h>
2260841Sdarrenr#include <ctype.h>
2360841Sdarrenr#include <fcntl.h>
2460841Sdarrenr#include <errno.h>
2560841Sdarrenr#include <stdlib.h>
2660841Sdarrenr
2760841Sdarrenr#include "ip_compat.h"
2860841Sdarrenr#include "ip_fil.h"
2960841Sdarrenr#include "ip_nat.h"
3060841Sdarrenr
3160841Sdarrenr#include "ipf.h"
3260841Sdarrenr
3360841Sdarrenrextern	char	*optarg;
3460841Sdarrenr
3560841Sdarrenr
3660841Sdarrenrtypedef	struct	l4cfg	{
3760841Sdarrenr	struct	l4cfg		*l4_next;
3860841Sdarrenr	struct	ipnat		l4_nat;		/* NAT rule */
3960841Sdarrenr	struct	sockaddr_in	l4_sin;		/* remote socket to connect */
4060841Sdarrenr	time_t			l4_last;	/* when we last connected */
4160841Sdarrenr	int			l4_alive;	/* 1 = remote alive */
4260841Sdarrenr	int			l4_fd;
4360841Sdarrenr	int			l4_rw;		/* 0 = reading, 1 = writing */
4460841Sdarrenr	char			*l4_rbuf;	/* read buffer */
4560841Sdarrenr	int			l4_rsize;	/* size of buffer */
4660841Sdarrenr	int			l4_rlen;	/* how much used */
4760841Sdarrenr	char			*l4_wptr;	/* next byte to write */
4860841Sdarrenr	int			l4_wlen;	/* length yet to be written */
4960841Sdarrenr} l4cfg_t;
5060841Sdarrenr
5160841Sdarrenr
5260841Sdarrenrl4cfg_t *l4list = NULL;
5360841Sdarrenrchar *response = NULL;
5460841Sdarrenrchar *probe = NULL;
5560841Sdarrenrl4cfg_t template;
5660841Sdarrenrint frequency = 20;
5760841Sdarrenrint ctimeout = 1;
5860841Sdarrenrint rtimeout = 1;
5960841Sdarrenrsize_t plen = 0;
6060841Sdarrenrsize_t rlen = 0;
6160841Sdarrenrint natfd = -1;
6260841Sdarrenrint opts = 0;
6360841Sdarrenr
6460841Sdarrenr#if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
6560841Sdarrenr# define	strerror(x)	sys_errlist[x]
6660841Sdarrenr#endif
6760841Sdarrenr
6860841Sdarrenr
6960841Sdarrenrchar *copystr(dst, src)
70255332Scy	char *dst, *src;
7160841Sdarrenr{
7260841Sdarrenr	register char *s, *t, c;
7360841Sdarrenr	register int esc = 0;
7460841Sdarrenr
7560841Sdarrenr	for (s = src, t = dst; s && t && (c = *s++); )
7660841Sdarrenr		if (esc) {
7760841Sdarrenr			esc = 0;
7860841Sdarrenr			switch (c)
7960841Sdarrenr			{
8060841Sdarrenr			case 'n' :
8160841Sdarrenr				*t++ = '\n';
8260841Sdarrenr				break;
8360841Sdarrenr			case 'r' :
8460841Sdarrenr				*t++ = '\r';
8560841Sdarrenr				break;
8660841Sdarrenr			case 't' :
8760841Sdarrenr				*t++ = '\t';
8860841Sdarrenr				break;
8960841Sdarrenr			}
9060841Sdarrenr		} else if (c != '\\')
9160841Sdarrenr			*t++ = c;
9260841Sdarrenr		else
9360841Sdarrenr			esc = 1;
9460841Sdarrenr	*t = '\0';
9560841Sdarrenr	return dst;
9660841Sdarrenr}
9760841Sdarrenr
9860841Sdarrenrvoid addnat(l4)
99255332Scy	l4cfg_t *l4;
10060841Sdarrenr{
10160841Sdarrenr	ipnat_t *ipn = &l4->l4_nat;
10260841Sdarrenr
103255332Scy	printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]),
10460841Sdarrenr		ipn->in_outmsk, ntohs(ipn->in_pmin));
105255332Scy	printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext));
10660841Sdarrenr	if (!(opts & OPT_DONOTHING)) {
107255332Scy		if (ioctl(natfd, SIOCADNAT, &ipn) == -1)
10860841Sdarrenr			perror("ioctl(SIOCADNAT)");
10960841Sdarrenr	}
11060841Sdarrenr}
11160841Sdarrenr
11260841Sdarrenr
11360841Sdarrenrvoid delnat(l4)
114255332Scy	l4cfg_t *l4;
11560841Sdarrenr{
11660841Sdarrenr	ipnat_t *ipn = &l4->l4_nat;
11760841Sdarrenr
11860841Sdarrenr	printf("Remove NAT rule for %s/%#x,%u -> ",
119255332Scy		inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin);
120255332Scy	printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext);
12160841Sdarrenr	if (!(opts & OPT_DONOTHING)) {
12260841Sdarrenr		if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
12360841Sdarrenr			perror("ioctl(SIOCRMNAT)");
12460841Sdarrenr	}
12560841Sdarrenr}
12660841Sdarrenr
12760841Sdarrenr
12860841Sdarrenrvoid connectl4(l4)
129255332Scy	l4cfg_t *l4;
13060841Sdarrenr{
13160841Sdarrenr	l4->l4_rw = 1;
13260841Sdarrenr	l4->l4_rlen = 0;
13360841Sdarrenr	l4->l4_wlen = plen;
13460841Sdarrenr	if (!l4->l4_wlen) {
13560841Sdarrenr		l4->l4_alive = 1;
13660841Sdarrenr		addnat(l4);
13760841Sdarrenr	} else
13860841Sdarrenr		l4->l4_wptr = probe;
13960841Sdarrenr}
14060841Sdarrenr
14160841Sdarrenr
14260841Sdarrenrvoid closel4(l4, dead)
143255332Scy	l4cfg_t *l4;
144255332Scy	int dead;
14560841Sdarrenr{
146145510Sdarrenr	close(l4->l4_fd);
14760841Sdarrenr	l4->l4_fd = -1;
14860841Sdarrenr	l4->l4_rw = -1;
14960841Sdarrenr	if (dead && l4->l4_alive) {
15060841Sdarrenr		l4->l4_alive = 0;
15160841Sdarrenr		delnat(l4);
15260841Sdarrenr	}
15360841Sdarrenr}
15460841Sdarrenr
15560841Sdarrenr
15660841Sdarrenrvoid connectfd(l4)
157255332Scy	l4cfg_t *l4;
15860841Sdarrenr{
15960841Sdarrenr	if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin,
16060841Sdarrenr		    sizeof(l4->l4_sin)) == -1) {
16160841Sdarrenr		if (errno == EISCONN) {
16260841Sdarrenr			if (opts & OPT_VERBOSE)
16360841Sdarrenr				fprintf(stderr, "Connected fd %d\n",
16460841Sdarrenr					l4->l4_fd);
16560841Sdarrenr			connectl4(l4);
16660841Sdarrenr			return;
16760841Sdarrenr		}
16860841Sdarrenr		if (opts & OPT_VERBOSE)
16960841Sdarrenr			fprintf(stderr, "Connect failed fd %d: %s\n",
17060841Sdarrenr				l4->l4_fd, strerror(errno));
17160841Sdarrenr		closel4(l4, 1);
17260841Sdarrenr		return;
17360841Sdarrenr	}
17460841Sdarrenr	l4->l4_rw = 1;
17560841Sdarrenr}
17660841Sdarrenr
17760841Sdarrenr
17860841Sdarrenrvoid writefd(l4)
179255332Scy	l4cfg_t *l4;
18060841Sdarrenr{
181255332Scy	char buf[80], *ptr;
18260841Sdarrenr	int n, i, fd;
18360841Sdarrenr
18460841Sdarrenr	fd = l4->l4_fd;
18560841Sdarrenr
18660841Sdarrenr	if (l4->l4_rw == -2) {
18760841Sdarrenr		connectfd(l4);
18860841Sdarrenr		return;
18960841Sdarrenr	}
19060841Sdarrenr
19160841Sdarrenr	n = l4->l4_wlen;
19260841Sdarrenr
19360841Sdarrenr	i = send(fd, l4->l4_wptr, n, 0);
19460841Sdarrenr	if (i == 0 || i == -1) {
19560841Sdarrenr		if (opts & OPT_VERBOSE)
19660841Sdarrenr			fprintf(stderr, "Send on fd %d failed: %s\n",
19760841Sdarrenr				fd, strerror(errno));
19860841Sdarrenr		closel4(l4, 1);
19960841Sdarrenr	} else {
20060841Sdarrenr		l4->l4_wptr += i;
20160841Sdarrenr		l4->l4_wlen -= i;
20260841Sdarrenr		if (l4->l4_wlen == 0)
20360841Sdarrenr			l4->l4_rw = 0;
20460841Sdarrenr		if (opts & OPT_VERBOSE)
20560841Sdarrenr			fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd);
20660841Sdarrenr	}
20760841Sdarrenr}
20860841Sdarrenr
20960841Sdarrenr
21060841Sdarrenrvoid readfd(l4)
211255332Scy	l4cfg_t *l4;
21260841Sdarrenr{
21360841Sdarrenr	char buf[80], *ptr;
21460841Sdarrenr	int n, i, fd;
21560841Sdarrenr
21660841Sdarrenr	fd = l4->l4_fd;
21760841Sdarrenr
21860841Sdarrenr	if (l4->l4_rw == -2) {
21960841Sdarrenr		connectfd(l4);
22060841Sdarrenr		return;
22160841Sdarrenr	}
22260841Sdarrenr
22360841Sdarrenr	if (l4->l4_rsize) {
22460841Sdarrenr		n = l4->l4_rsize - l4->l4_rlen;
22560841Sdarrenr		ptr = l4->l4_rbuf + l4->l4_rlen;
22660841Sdarrenr	} else {
22760841Sdarrenr		n = sizeof(buf) - 1;
22860841Sdarrenr		ptr = buf;
22960841Sdarrenr	}
23060841Sdarrenr
23160841Sdarrenr	if (opts & OPT_VERBOSE)
23260841Sdarrenr		fprintf(stderr, "Read %d bytes on fd %d to %p\n",
23360841Sdarrenr			n, fd, ptr);
23460841Sdarrenr	i = recv(fd, ptr, n, 0);
23560841Sdarrenr	if (i == 0 || i == -1) {
23660841Sdarrenr		if (opts & OPT_VERBOSE)
23760841Sdarrenr			fprintf(stderr, "Read error on fd %d: %s\n",
23860841Sdarrenr				fd, (i == 0) ? "EOF" : strerror(errno));
23960841Sdarrenr		closel4(l4, 1);
24060841Sdarrenr	} else {
24160841Sdarrenr		if (ptr == buf)
24260841Sdarrenr			ptr[i] = '\0';
24360841Sdarrenr		if (opts & OPT_VERBOSE)
24460841Sdarrenr			fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
24560841Sdarrenr				fd, i, i, i, ptr);
24660841Sdarrenr		if (ptr != buf) {
24760841Sdarrenr			l4->l4_rlen += i;
24860841Sdarrenr			if (l4->l4_rlen >= l4->l4_rsize) {
24960841Sdarrenr				if (!strncmp(response, l4->l4_rbuf,
25060841Sdarrenr					     l4->l4_rsize)) {
25160841Sdarrenr					printf("%d: Good response\n",
25260841Sdarrenr						fd);
25360841Sdarrenr					if (!l4->l4_alive) {
25460841Sdarrenr						l4->l4_alive = 1;
25560841Sdarrenr						addnat(l4);
25660841Sdarrenr					}
25760841Sdarrenr					closel4(l4, 0);
25860841Sdarrenr				} else {
25960841Sdarrenr					if (opts & OPT_VERBOSE)
26060841Sdarrenr						printf("%d: Bad response\n",
26160841Sdarrenr							fd);
26260841Sdarrenr					closel4(l4, 1);
26360841Sdarrenr				}
26460841Sdarrenr			}
26560841Sdarrenr		} else if (!l4->l4_alive) {
26660841Sdarrenr			l4->l4_alive = 1;
26760841Sdarrenr			addnat(l4);
26860841Sdarrenr			closel4(l4, 0);
26960841Sdarrenr		}
27060841Sdarrenr	}
27160841Sdarrenr}
27260841Sdarrenr
27360841Sdarrenr
27460841Sdarrenrint runconfig()
27560841Sdarrenr{
27660841Sdarrenr	int fd, opt, res, mfd, i;
27760841Sdarrenr	struct timeval tv;
27860841Sdarrenr	time_t now, now1;
27960841Sdarrenr	fd_set rfd, wfd;
28060841Sdarrenr	l4cfg_t *l4;
28160841Sdarrenr
28260841Sdarrenr	mfd = 0;
28360841Sdarrenr	opt = 1;
28460841Sdarrenr	now = time(NULL);
28560841Sdarrenr
28660841Sdarrenr	/*
28760841Sdarrenr	 * First, initiate connections that are closed, as required.
28860841Sdarrenr	 */
28960841Sdarrenr	for (l4 = l4list; l4; l4 = l4->l4_next) {
29060841Sdarrenr		if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) {
29160841Sdarrenr			l4->l4_last = now;
29260841Sdarrenr			fd = socket(AF_INET, SOCK_STREAM, 0);
29360841Sdarrenr			if (fd == -1)
29460841Sdarrenr				continue;
29560841Sdarrenr			setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
29660841Sdarrenr				   sizeof(opt));
29760841Sdarrenr#ifdef	O_NONBLOCK
29860841Sdarrenr			if ((res = fcntl(fd, F_GETFL, 0)) != -1)
29960841Sdarrenr				fcntl(fd, F_SETFL, res | O_NONBLOCK);
30060841Sdarrenr#endif
30160841Sdarrenr			if (opts & OPT_VERBOSE)
30260841Sdarrenr				fprintf(stderr,
30360841Sdarrenr					"Connecting to %s,%d (fd %d)...",
30460841Sdarrenr					inet_ntoa(l4->l4_sin.sin_addr),
30560841Sdarrenr					ntohs(l4->l4_sin.sin_port), fd);
30660841Sdarrenr			if (connect(fd, (struct sockaddr *)&l4->l4_sin,
30760841Sdarrenr				    sizeof(l4->l4_sin)) == -1) {
30860841Sdarrenr				if (errno != EINPROGRESS) {
30960841Sdarrenr					if (opts & OPT_VERBOSE)
31060841Sdarrenr						fprintf(stderr, "failed\n");
31160841Sdarrenr					perror("connect");
312145510Sdarrenr					close(fd);
31360841Sdarrenr					fd = -1;
31460841Sdarrenr				} else {
31560841Sdarrenr					if (opts & OPT_VERBOSE)
31660841Sdarrenr						fprintf(stderr, "waiting\n");
31760841Sdarrenr					l4->l4_rw = -2;
31860841Sdarrenr				}
31960841Sdarrenr			} else {
32060841Sdarrenr				if (opts & OPT_VERBOSE)
32160841Sdarrenr					fprintf(stderr, "connected\n");
32260841Sdarrenr				connectl4(l4);
32360841Sdarrenr			}
32460841Sdarrenr			l4->l4_fd = fd;
32560841Sdarrenr		}
32660841Sdarrenr	}
32760841Sdarrenr
32860841Sdarrenr	/*
32960841Sdarrenr	 * Now look for fd's which we're expecting to read/write from.
33060841Sdarrenr	 */
33160841Sdarrenr	FD_ZERO(&rfd);
33260841Sdarrenr	FD_ZERO(&wfd);
33360841Sdarrenr	tv.tv_sec = MIN(rtimeout, ctimeout);
33460841Sdarrenr	tv.tv_usec = 0;
33560841Sdarrenr
33660841Sdarrenr	for (l4 = l4list; l4; l4 = l4->l4_next)
33760841Sdarrenr		if (l4->l4_rw == 0) {
33860841Sdarrenr			if (now - l4->l4_last > rtimeout) {
33960841Sdarrenr				if (opts & OPT_VERBOSE)
34060841Sdarrenr					fprintf(stderr, "%d: Read timeout\n",
34160841Sdarrenr						l4->l4_fd);
34260841Sdarrenr				closel4(l4, 1);
34360841Sdarrenr				continue;
34460841Sdarrenr			}
34560841Sdarrenr			if (opts & OPT_VERBOSE)
34660841Sdarrenr				fprintf(stderr, "Wait for read on fd %d\n",
34760841Sdarrenr					l4->l4_fd);
34860841Sdarrenr			FD_SET(l4->l4_fd, &rfd);
34960841Sdarrenr			if (l4->l4_fd > mfd)
35060841Sdarrenr				mfd = l4->l4_fd;
35160841Sdarrenr		} else if ((l4->l4_rw == 1 && l4->l4_wlen) ||
35260841Sdarrenr			   l4->l4_rw == -2) {
35360841Sdarrenr			if ((l4->l4_rw == -2) &&
35460841Sdarrenr			    (now - l4->l4_last > ctimeout)) {
35560841Sdarrenr				if (opts & OPT_VERBOSE)
35660841Sdarrenr					fprintf(stderr,
35760841Sdarrenr						"%d: connect timeout\n",
35860841Sdarrenr						l4->l4_fd);
35960841Sdarrenr				closel4(l4);
36060841Sdarrenr				continue;
36160841Sdarrenr			}
36260841Sdarrenr			if (opts & OPT_VERBOSE)
36360841Sdarrenr				fprintf(stderr, "Wait for write on fd %d\n",
36460841Sdarrenr					l4->l4_fd);
36560841Sdarrenr			FD_SET(l4->l4_fd, &wfd);
36660841Sdarrenr			if (l4->l4_fd > mfd)
36760841Sdarrenr				mfd = l4->l4_fd;
36860841Sdarrenr		}
36960841Sdarrenr
37060841Sdarrenr	if (opts & OPT_VERBOSE)
37160841Sdarrenr		fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1,
37260841Sdarrenr			tv.tv_sec);
37360841Sdarrenr	i = select(mfd + 1, &rfd, &wfd, NULL, &tv);
37460841Sdarrenr	if (i == -1) {
37560841Sdarrenr		perror("select");
37660841Sdarrenr		return -1;
37760841Sdarrenr	}
37860841Sdarrenr
37960841Sdarrenr	now1 = time(NULL);
38060841Sdarrenr
38160841Sdarrenr	for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) {
38260841Sdarrenr		if (l4->l4_fd < 0)
38360841Sdarrenr			continue;
38460841Sdarrenr		if (FD_ISSET(l4->l4_fd, &rfd)) {
38560841Sdarrenr			if (opts & OPT_VERBOSE)
38660841Sdarrenr				fprintf(stderr, "Ready to read on fd %d\n",
38760841Sdarrenr					l4->l4_fd);
38860841Sdarrenr			readfd(l4);
38960841Sdarrenr			i--;
39060841Sdarrenr		}
39160841Sdarrenr
39260841Sdarrenr		if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) {
39360841Sdarrenr			if (opts & OPT_VERBOSE)
39460841Sdarrenr				fprintf(stderr, "Ready to write on fd %d\n",
39560841Sdarrenr					l4->l4_fd);
39660841Sdarrenr			writefd(l4);
39760841Sdarrenr			i--;
39860841Sdarrenr		}
39960841Sdarrenr	}
40060841Sdarrenr	return 0;
40160841Sdarrenr}
40260841Sdarrenr
40360841Sdarrenr
40460841Sdarrenrint gethostport(str, lnum, ipp, portp)
405255332Scy	char *str;
406255332Scy	int lnum;
407255332Scy	u_32_t *ipp;
408255332Scy	u_short *portp;
40960841Sdarrenr{
41060841Sdarrenr	struct servent *sp;
41160841Sdarrenr	struct hostent *hp;
41260841Sdarrenr	char *host, *port;
413255332Scy	struct in_addr ip;
41460841Sdarrenr
41560841Sdarrenr	host = str;
41660841Sdarrenr	port = strchr(host, ',');
41760841Sdarrenr	if (port)
41860841Sdarrenr		*port++ = '\0';
41960841Sdarrenr
42060841Sdarrenr#ifdef	HAVE_INET_ATON
421145510Sdarrenr	if (ISDIGIT(*host) && inet_aton(host, &ip))
42260841Sdarrenr		*ipp = ip.s_addr;
42360841Sdarrenr#else
424145510Sdarrenr	if (ISDIGIT(*host))
42560841Sdarrenr		*ipp = inet_addr(host);
42660841Sdarrenr#endif
42760841Sdarrenr	else {
42860841Sdarrenr		if (!(hp = gethostbyname(host))) {
42960841Sdarrenr			fprintf(stderr, "%d: can't resolve hostname: %s\n",
43060841Sdarrenr				lnum, host);
43160841Sdarrenr			return 0;
43260841Sdarrenr		}
43360841Sdarrenr		*ipp = *(u_32_t *)hp->h_addr;
43460841Sdarrenr	}
43560841Sdarrenr
43660841Sdarrenr	if (port) {
437145510Sdarrenr		if (ISDIGIT(*port))
43860841Sdarrenr			*portp = htons(atoi(port));
43960841Sdarrenr		else {
44060841Sdarrenr			sp = getservbyname(port, "tcp");
44160841Sdarrenr			if (sp)
44260841Sdarrenr				*portp = sp->s_port;
44360841Sdarrenr			else {
44460841Sdarrenr				fprintf(stderr, "%d: unknown service %s\n",
44560841Sdarrenr					lnum, port);
44660841Sdarrenr				return 0;
44760841Sdarrenr			}
44860841Sdarrenr		}
44960841Sdarrenr	} else
45060841Sdarrenr		*portp = 0;
45160841Sdarrenr	return 1;
45260841Sdarrenr}
45360841Sdarrenr
45460841Sdarrenr
45560841Sdarrenrchar *mapfile(file, sizep)
456255332Scy	char *file;
457255332Scy	size_t *sizep;
45860841Sdarrenr{
45960841Sdarrenr	struct stat sb;
46060841Sdarrenr	caddr_t addr;
46160841Sdarrenr	int fd;
46260841Sdarrenr
46360841Sdarrenr	fd = open(file, O_RDONLY);
46460841Sdarrenr	if (fd == -1) {
46560841Sdarrenr		perror("open(mapfile)");
46660841Sdarrenr		return NULL;
46760841Sdarrenr	}
46860841Sdarrenr
46960841Sdarrenr	if (fstat(fd, &sb) == -1) {
47060841Sdarrenr		perror("fstat(mapfile)");
47160841Sdarrenr		close(fd);
47260841Sdarrenr		return NULL;
47360841Sdarrenr	}
47460841Sdarrenr
47560841Sdarrenr	addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
47660841Sdarrenr	if (addr == (caddr_t)-1) {
47760841Sdarrenr		perror("mmap(mapfile)");
47860841Sdarrenr		close(fd);
47960841Sdarrenr		return NULL;
48060841Sdarrenr	}
48160841Sdarrenr	close(fd);
48260841Sdarrenr	*sizep = sb.st_size;
48360841Sdarrenr	return (char *)addr;
48460841Sdarrenr}
48560841Sdarrenr
48660841Sdarrenr
48760841Sdarrenrint readconfig(filename)
488255332Scy	char *filename;
48960841Sdarrenr{
49060841Sdarrenr	char c, buf[512], *s, *t, *errtxt = NULL, *line;
49160841Sdarrenr	int num, err = 0;
49260841Sdarrenr	ipnat_t *ipn;
49360841Sdarrenr	l4cfg_t *l4;
49460841Sdarrenr	FILE *fp;
49560841Sdarrenr
49660841Sdarrenr	fp = fopen(filename, "r");
49760841Sdarrenr	if (!fp) {
49860841Sdarrenr		perror("open(configfile)");
49960841Sdarrenr		return -1;
50060841Sdarrenr	}
50160841Sdarrenr
50260841Sdarrenr	bzero((char *)&template, sizeof(template));
50360841Sdarrenr	template.l4_fd = -1;
50460841Sdarrenr	template.l4_rw = -1;
50560841Sdarrenr	template.l4_sin.sin_family = AF_INET;
50660841Sdarrenr	ipn = &template.l4_nat;
50760841Sdarrenr	ipn->in_flags = IPN_TCP|IPN_ROUNDR;
50860841Sdarrenr	ipn->in_redir = NAT_REDIRECT;
50960841Sdarrenr
51060841Sdarrenr	for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
51160841Sdarrenr		s = strchr(buf, '\n');
51260841Sdarrenr		if  (!s) {
51360841Sdarrenr			fprintf(stderr, "%d: line too long\n", num);
51460841Sdarrenr			fclose(fp);
51560841Sdarrenr			return -1;
51660841Sdarrenr		}
51760841Sdarrenr
51860841Sdarrenr		*s = '\0';
51960841Sdarrenr
52060841Sdarrenr		/*
52160841Sdarrenr		 * lines which are comments
52260841Sdarrenr		 */
52360841Sdarrenr		s = strchr(buf, '#');
52460841Sdarrenr		if (s)
52560841Sdarrenr			*s = '\0';
52660841Sdarrenr
52760841Sdarrenr		/*
52860841Sdarrenr		 * Skip leading whitespace
52960841Sdarrenr		 */
530145510Sdarrenr		for (line = buf; (c = *line) && ISSPACE(c); line++)
53160841Sdarrenr			;
53260841Sdarrenr		if (!*line)
53360841Sdarrenr			continue;
53460841Sdarrenr
53560841Sdarrenr		if (opts & OPT_VERBOSE)
53660841Sdarrenr			fprintf(stderr, "Parsing: [%s]\n", line);
53760841Sdarrenr		t = strtok(line, " \t");
53860841Sdarrenr		if (!t)
53960841Sdarrenr			continue;
54060841Sdarrenr		if (!strcasecmp(t, "interface")) {
54160841Sdarrenr			s = strtok(NULL, " \t");
54260841Sdarrenr			if (s)
54360841Sdarrenr				t = strtok(NULL, "\t");
54460841Sdarrenr			if (!s || !t) {
54560841Sdarrenr				errtxt = line;
54660841Sdarrenr				err = -1;
54760841Sdarrenr				break;
54860841Sdarrenr			}
54960841Sdarrenr
55060841Sdarrenr			if (!strchr(t, ',')) {
55160841Sdarrenr				fprintf(stderr,
55260841Sdarrenr					"%d: local address,port missing\n",
55360841Sdarrenr					num);
55460841Sdarrenr				err = -1;
55560841Sdarrenr				break;
55660841Sdarrenr			}
55760841Sdarrenr
558255332Scy			strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname));
55960841Sdarrenr			if (!gethostport(t, num, &ipn->in_outip,
56060841Sdarrenr					 &ipn->in_pmin)) {
56160841Sdarrenr				errtxt = line;
56260841Sdarrenr				err = -1;
56360841Sdarrenr				break;
56460841Sdarrenr			}
56560841Sdarrenr			ipn->in_outmsk = 0xffffffff;
56660841Sdarrenr			ipn->in_pmax = ipn->in_pmin;
56760841Sdarrenr			if (opts & OPT_VERBOSE)
56860841Sdarrenr				fprintf(stderr,
56960841Sdarrenr					"Interface %s %s/%#x port %u\n",
570255332Scy					ipn->in_ifname,
571255332Scy					inet_ntoa(ipn->in_out[0]),
57260841Sdarrenr					ipn->in_outmsk, ipn->in_pmin);
57360841Sdarrenr		} else if (!strcasecmp(t, "remote")) {
574255332Scy			if (!*ipn->in_ifname) {
57560841Sdarrenr				fprintf(stderr,
57660841Sdarrenr					"%d: ifname not set prior to remote\n",
57760841Sdarrenr					num);
57860841Sdarrenr				err = -1;
57960841Sdarrenr				break;
58060841Sdarrenr			}
58160841Sdarrenr			s = strtok(NULL, " \t");
58260841Sdarrenr			if (s)
58360841Sdarrenr				t = strtok(NULL, "");
58460841Sdarrenr			if (!s || !t || strcasecmp(s, "server")) {
58560841Sdarrenr				errtxt = line;
58660841Sdarrenr				err = -1;
58760841Sdarrenr				break;
58860841Sdarrenr			}
58960841Sdarrenr
59060841Sdarrenr			ipn->in_pnext = 0;
59160841Sdarrenr			if (!gethostport(t, num, &ipn->in_inip,
59260841Sdarrenr					 &ipn->in_pnext)) {
59360841Sdarrenr				errtxt = line;
59460841Sdarrenr				err = -1;
59560841Sdarrenr				break;
59660841Sdarrenr			}
59760841Sdarrenr			ipn->in_inmsk = 0xffffffff;
59860841Sdarrenr			if (ipn->in_pnext == 0)
59960841Sdarrenr				ipn->in_pnext = ipn->in_pmin;
60060841Sdarrenr
60160841Sdarrenr			l4 = (l4cfg_t *)malloc(sizeof(*l4));
60260841Sdarrenr			if (!l4) {
60360841Sdarrenr				fprintf(stderr, "%d: out of memory (%d)\n",
60460841Sdarrenr					num, sizeof(*l4));
60560841Sdarrenr				err = -1;
60660841Sdarrenr				break;
60760841Sdarrenr			}
60860841Sdarrenr			bcopy((char *)&template, (char *)l4, sizeof(*l4));
609255332Scy			l4->l4_sin.sin_addr = ipn->in_in[0];
610145510Sdarrenr			l4->l4_sin.sin_port = ipn->in_pnext;
61160841Sdarrenr			l4->l4_next = l4list;
61260841Sdarrenr			l4list = l4;
61360841Sdarrenr		} else if (!strcasecmp(t, "connect")) {
61460841Sdarrenr			s = strtok(NULL, " \t");
61560841Sdarrenr			if (s)
61660841Sdarrenr				t = strtok(NULL, "\t");
617145510Sdarrenr			if (!s || !t) {
61860841Sdarrenr				errtxt = line;
61960841Sdarrenr				err = -1;
62060841Sdarrenr				break;
62160841Sdarrenr			} else if (!strcasecmp(s, "timeout")) {
62260841Sdarrenr				ctimeout = atoi(t);
62360841Sdarrenr				if (opts & OPT_VERBOSE)
62460841Sdarrenr					fprintf(stderr, "connect timeout %d\n",
62560841Sdarrenr						ctimeout);
62660841Sdarrenr			} else if (!strcasecmp(s, "frequency")) {
62760841Sdarrenr				frequency = atoi(t);
62860841Sdarrenr				if (opts & OPT_VERBOSE)
62960841Sdarrenr					fprintf(stderr,
63060841Sdarrenr						"connect frequency %d\n",
63160841Sdarrenr						frequency);
63260841Sdarrenr			} else {
63360841Sdarrenr				errtxt = line;
63460841Sdarrenr				err = -1;
63560841Sdarrenr				break;
63660841Sdarrenr			}
63760841Sdarrenr		} else if (!strcasecmp(t, "probe")) {
63860841Sdarrenr			s = strtok(NULL, " \t");
639145510Sdarrenr			if (!s) {
64060841Sdarrenr				errtxt = line;
64160841Sdarrenr				err = -1;
64260841Sdarrenr				break;
64360841Sdarrenr			} else if (!strcasecmp(s, "string")) {
64460841Sdarrenr				if (probe) {
64560841Sdarrenr					fprintf(stderr,
64660841Sdarrenr						"%d: probe already set\n",
64760841Sdarrenr						num);
64860841Sdarrenr					err = -1;
64960841Sdarrenr					break;
65060841Sdarrenr				}
65160841Sdarrenr				t = strtok(NULL, "");
65260841Sdarrenr				if (!t) {
65360841Sdarrenr					fprintf(stderr,
65460841Sdarrenr						"%d: No probe string\n", num);
65560841Sdarrenr					err = -1;
65660841Sdarrenr					break;
65760841Sdarrenr				}
65860841Sdarrenr
65960841Sdarrenr				probe = malloc(strlen(t));
66060841Sdarrenr				copystr(probe, t);
66160841Sdarrenr				plen = strlen(probe);
66260841Sdarrenr				if (opts & OPT_VERBOSE)
66360841Sdarrenr					fprintf(stderr, "Probe string [%s]\n",
66460841Sdarrenr						probe);
66560841Sdarrenr			} else if (!strcasecmp(s, "file")) {
66660841Sdarrenr				t = strtok(NULL, " \t");
66760841Sdarrenr				if (!t) {
66860841Sdarrenr					errtxt = line;
66960841Sdarrenr					err = -1;
67060841Sdarrenr					break;
67160841Sdarrenr				}
67260841Sdarrenr				if (probe) {
67360841Sdarrenr					fprintf(stderr,
67460841Sdarrenr						"%d: probe already set\n",
67560841Sdarrenr						num);
67660841Sdarrenr					err = -1;
67760841Sdarrenr					break;
67860841Sdarrenr				}
67960841Sdarrenr				probe = mapfile(t, &plen);
68060841Sdarrenr				if (opts & OPT_VERBOSE)
68160841Sdarrenr					fprintf(stderr,
68260841Sdarrenr						"Probe file %s len %u@%p\n",
68360841Sdarrenr						t, plen, probe);
68460841Sdarrenr			}
68560841Sdarrenr		} else if (!strcasecmp(t, "response")) {
68660841Sdarrenr			s = strtok(NULL, " \t");
687145510Sdarrenr			if (!s) {
68860841Sdarrenr				errtxt = line;
68960841Sdarrenr				err = -1;
69060841Sdarrenr				break;
69160841Sdarrenr			} else if (!strcasecmp(s, "timeout")) {
69260841Sdarrenr				t = strtok(NULL, " \t");
693145510Sdarrenr				if (!t) {
69460841Sdarrenr					errtxt = line;
69560841Sdarrenr					err = -1;
69660841Sdarrenr					break;
69760841Sdarrenr				}
69860841Sdarrenr				rtimeout = atoi(t);
69960841Sdarrenr				if (opts & OPT_VERBOSE)
70060841Sdarrenr					fprintf(stderr,
70160841Sdarrenr						"response timeout %d\n",
70260841Sdarrenr						rtimeout);
70360841Sdarrenr			} else if (!strcasecmp(s, "string")) {
70460841Sdarrenr				if (response) {
70560841Sdarrenr					fprintf(stderr,
70660841Sdarrenr						"%d: response already set\n",
70760841Sdarrenr						num);
70860841Sdarrenr					err = -1;
70960841Sdarrenr					break;
71060841Sdarrenr				}
71160841Sdarrenr				response = strdup(strtok(NULL, ""));
71260841Sdarrenr				rlen = strlen(response);
71360841Sdarrenr				template.l4_rsize = rlen;
71460841Sdarrenr				template.l4_rbuf = malloc(rlen);
71560841Sdarrenr				if (opts & OPT_VERBOSE)
71660841Sdarrenr					fprintf(stderr,
71760841Sdarrenr						"Response string [%s]\n",
71860841Sdarrenr						response);
71960841Sdarrenr			} else if (!strcasecmp(s, "file")) {
72060841Sdarrenr				t = strtok(NULL, " \t");
72160841Sdarrenr				if (!t) {
72260841Sdarrenr					errtxt = line;
72360841Sdarrenr					err = -1;
72460841Sdarrenr					break;
72560841Sdarrenr				}
72660841Sdarrenr				if (response) {
72760841Sdarrenr					fprintf(stderr,
72860841Sdarrenr						"%d: response already set\n",
72960841Sdarrenr						num);
73060841Sdarrenr					err = -1;
73160841Sdarrenr					break;
73260841Sdarrenr				}
73360841Sdarrenr				response = mapfile(t, &rlen);
73460841Sdarrenr				template.l4_rsize = rlen;
73560841Sdarrenr				template.l4_rbuf = malloc(rlen);
73660841Sdarrenr				if (opts & OPT_VERBOSE)
73760841Sdarrenr					fprintf(stderr,
73860841Sdarrenr						"Response file %s len %u@%p\n",
73960841Sdarrenr						t, rlen, response);
74060841Sdarrenr			}
74160841Sdarrenr		} else {
74260841Sdarrenr			errtxt = line;
74360841Sdarrenr			err = -1;
74460841Sdarrenr			break;
74560841Sdarrenr		}
74660841Sdarrenr	}
74760841Sdarrenr
74860841Sdarrenr	if (errtxt)
74960841Sdarrenr		fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
75060841Sdarrenr	fclose(fp);
75160841Sdarrenr	return err;
75260841Sdarrenr}
75360841Sdarrenr
75460841Sdarrenr
75560841Sdarrenrvoid usage(prog)
756255332Scy	char *prog;
75760841Sdarrenr{
75860841Sdarrenr	fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
75960841Sdarrenr	exit(1);
76060841Sdarrenr}
76160841Sdarrenr
76260841Sdarrenr
76360841Sdarrenrint main(argc, argv)
764255332Scy	int argc;
765255332Scy	char *argv[];
76660841Sdarrenr{
76760841Sdarrenr	char *config = NULL;
76860841Sdarrenr	int c;
76960841Sdarrenr
77060841Sdarrenr	while ((c = getopt(argc, argv, "f:nv")) != -1)
77160841Sdarrenr		switch (c)
77260841Sdarrenr		{
77360841Sdarrenr		case 'f' :
77460841Sdarrenr			config = optarg;
77560841Sdarrenr			break;
77660841Sdarrenr		case 'n' :
77760841Sdarrenr			opts |= OPT_DONOTHING;
77860841Sdarrenr			break;
77960841Sdarrenr		case 'v' :
78060841Sdarrenr			opts |= OPT_VERBOSE;
78160841Sdarrenr			break;
78260841Sdarrenr		}
78360841Sdarrenr
78460841Sdarrenr	if (config == NULL)
78560841Sdarrenr		usage(argv[0]);
78660841Sdarrenr
78760841Sdarrenr	if (readconfig(config))
78860841Sdarrenr		exit(1);
78960841Sdarrenr
79060841Sdarrenr	if (!l4list) {
79160841Sdarrenr		fprintf(stderr, "No remote servers, exiting.");
79260841Sdarrenr		exit(1);
79360841Sdarrenr	}
79460841Sdarrenr
79560841Sdarrenr	if (!(opts & OPT_DONOTHING)) {
796255332Scy		natfd = open(IPL_NAT, O_RDWR);
79760841Sdarrenr		if (natfd == -1) {
79860841Sdarrenr			perror("open(IPL_NAT)");
79960841Sdarrenr			exit(1);
80060841Sdarrenr		}
80160841Sdarrenr	}
80260841Sdarrenr
80360841Sdarrenr	if (opts & OPT_VERBOSE)
80460841Sdarrenr		fprintf(stderr, "Starting...\n");
80560841Sdarrenr	while (runconfig() == 0)
80660841Sdarrenr		;
80760841Sdarrenr}
808