l4check.c revision 145519
1170754Sdelphij/*	$FreeBSD: head/contrib/ipfilter/l4check/l4check.c 145519 2005-04-25 18:20:15Z darrenr $	*/
2170754Sdelphij
3170754Sdelphij/*
4170754Sdelphij * (C)Copyright March, 2000 - Darren Reed.
5170754Sdelphij */
6170754Sdelphij#include <sys/types.h>
7170754Sdelphij#include <sys/stat.h>
8170754Sdelphij#include <sys/mman.h>
9170754Sdelphij#include <sys/socket.h>
10170754Sdelphij#include <sys/time.h>
11170754Sdelphij#include <sys/ioctl.h>
12170754Sdelphij
13170754Sdelphij#include <netinet/in.h>
14170754Sdelphij#include <netinet/in_systm.h>
15170754Sdelphij#include <netinet/ip.h>
16170754Sdelphij
17170754Sdelphij#include <net/if.h>
18170754Sdelphij
19170754Sdelphij#include <stdio.h>
20170754Sdelphij#include <netdb.h>
21170754Sdelphij#include <string.h>
22170754Sdelphij#include <ctype.h>
23170754Sdelphij#include <fcntl.h>
24170754Sdelphij#include <errno.h>
25170754Sdelphij#include <stdlib.h>
26170754Sdelphij
27170754Sdelphij#include "ip_compat.h"
28170754Sdelphij#include "ip_fil.h"
29170754Sdelphij#include "ip_nat.h"
30170754Sdelphij
31170754Sdelphij#include "ipf.h"
32170754Sdelphij
33170754Sdelphijextern	char	*optarg;
34170754Sdelphij
35170754Sdelphij
36170754Sdelphijtypedef	struct	l4cfg	{
37170754Sdelphij	struct	l4cfg		*l4_next;
38170754Sdelphij	struct	ipnat		l4_nat;		/* NAT rule */
39170754Sdelphij	struct	sockaddr_in	l4_sin;		/* remote socket to connect */
40170754Sdelphij	time_t			l4_last;	/* when we last connected */
41170754Sdelphij	int			l4_alive;	/* 1 = remote alive */
42170754Sdelphij	int			l4_fd;
43170754Sdelphij	int			l4_rw;		/* 0 = reading, 1 = writing */
44170754Sdelphij	char			*l4_rbuf;	/* read buffer */
45170754Sdelphij	int			l4_rsize;	/* size of buffer */
46170754Sdelphij	int			l4_rlen;	/* how much used */
47170754Sdelphij	char			*l4_wptr;	/* next byte to write */
48170754Sdelphij	int			l4_wlen;	/* length yet to be written */
49170754Sdelphij} l4cfg_t;
50170754Sdelphij
51170754Sdelphij
52170754Sdelphijl4cfg_t *l4list = NULL;
53170754Sdelphijchar *response = NULL;
54170754Sdelphijchar *probe = NULL;
55170754Sdelphijl4cfg_t template;
56170754Sdelphijint frequency = 20;
57170754Sdelphijint ctimeout = 1;
58170754Sdelphijint rtimeout = 1;
59170754Sdelphijsize_t plen = 0;
60170754Sdelphijsize_t rlen = 0;
61170754Sdelphijint natfd = -1;
62170754Sdelphijint opts = 0;
63170754Sdelphij
64170754Sdelphij#if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
65170754Sdelphij# define	strerror(x)	sys_errlist[x]
66170754Sdelphij#endif
67170754Sdelphij
68170754Sdelphij
69170754Sdelphijchar *copystr(dst, src)
70170754Sdelphijchar *dst, *src;
71170754Sdelphij{
72170754Sdelphij	register char *s, *t, c;
73170754Sdelphij	register int esc = 0;
74170754Sdelphij
75170754Sdelphij	for (s = src, t = dst; s && t && (c = *s++); )
76170754Sdelphij		if (esc) {
77170754Sdelphij			esc = 0;
78170754Sdelphij			switch (c)
79170754Sdelphij			{
80170754Sdelphij			case 'n' :
81170754Sdelphij				*t++ = '\n';
82170754Sdelphij				break;
83170754Sdelphij			case 'r' :
84170754Sdelphij				*t++ = '\r';
85170754Sdelphij				break;
86170754Sdelphij			case 't' :
87170754Sdelphij				*t++ = '\t';
88170754Sdelphij				break;
89170754Sdelphij			}
90170754Sdelphij		} else if (c != '\\')
91170754Sdelphij			*t++ = c;
92170754Sdelphij		else
93170754Sdelphij			esc = 1;
94170754Sdelphij	*t = '\0';
95170754Sdelphij	return dst;
96170754Sdelphij}
97170754Sdelphij
98170754Sdelphijvoid addnat(l4)
99170754Sdelphijl4cfg_t *l4;
100170754Sdelphij{
101170754Sdelphij	ipnat_t *ipn = &l4->l4_nat;
102170754Sdelphij
103170754Sdelphij	printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]),
104170754Sdelphij		ipn->in_outmsk, ntohs(ipn->in_pmin));
105170754Sdelphij	printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext));
106170754Sdelphij	if (!(opts & OPT_DONOTHING)) {
107170754Sdelphij		if (ioctl(natfd, SIOCADNAT, &ipn) == -1)
108170754Sdelphij			perror("ioctl(SIOCADNAT)");
109170754Sdelphij	}
110170754Sdelphij}
111170754Sdelphij
112170754Sdelphij
113170754Sdelphijvoid delnat(l4)
114170754Sdelphijl4cfg_t *l4;
115170754Sdelphij{
116170754Sdelphij	ipnat_t *ipn = &l4->l4_nat;
117170754Sdelphij
118170754Sdelphij	printf("Remove NAT rule for %s/%#x,%u -> ",
119170754Sdelphij		inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin);
120170754Sdelphij	printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext);
121170754Sdelphij	if (!(opts & OPT_DONOTHING)) {
122170754Sdelphij		if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
123170754Sdelphij			perror("ioctl(SIOCRMNAT)");
124170754Sdelphij	}
125170754Sdelphij}
126170754Sdelphij
127170754Sdelphij
128170754Sdelphijvoid connectl4(l4)
129170754Sdelphijl4cfg_t *l4;
130170754Sdelphij{
131170754Sdelphij	l4->l4_rw = 1;
132170754Sdelphij	l4->l4_rlen = 0;
133170754Sdelphij	l4->l4_wlen = plen;
134170754Sdelphij	if (!l4->l4_wlen) {
135170754Sdelphij		l4->l4_alive = 1;
136170754Sdelphij		addnat(l4);
137170754Sdelphij	} else
138170754Sdelphij		l4->l4_wptr = probe;
139170754Sdelphij}
140170754Sdelphij
141170754Sdelphij
142170754Sdelphijvoid closel4(l4, dead)
143170754Sdelphijl4cfg_t *l4;
144170754Sdelphijint dead;
145170754Sdelphij{
146170754Sdelphij	close(l4->l4_fd);
147170754Sdelphij	l4->l4_fd = -1;
148170754Sdelphij	l4->l4_rw = -1;
149170754Sdelphij	if (dead && l4->l4_alive) {
150170754Sdelphij		l4->l4_alive = 0;
151170754Sdelphij		delnat(l4);
152170754Sdelphij	}
153170754Sdelphij}
154170754Sdelphij
155170754Sdelphij
156170754Sdelphijvoid connectfd(l4)
157170754Sdelphijl4cfg_t *l4;
158170754Sdelphij{
159170754Sdelphij	if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin,
160170754Sdelphij		    sizeof(l4->l4_sin)) == -1) {
161170754Sdelphij		if (errno == EISCONN) {
162170754Sdelphij			if (opts & OPT_VERBOSE)
163170754Sdelphij				fprintf(stderr, "Connected fd %d\n",
164170754Sdelphij					l4->l4_fd);
165170754Sdelphij			connectl4(l4);
166170754Sdelphij			return;
167170754Sdelphij		}
168170754Sdelphij		if (opts & OPT_VERBOSE)
169170754Sdelphij			fprintf(stderr, "Connect failed fd %d: %s\n",
170170754Sdelphij				l4->l4_fd, strerror(errno));
171170754Sdelphij		closel4(l4, 1);
172170754Sdelphij		return;
173170754Sdelphij	}
174170754Sdelphij	l4->l4_rw = 1;
175170754Sdelphij}
176170754Sdelphij
177170754Sdelphij
178170754Sdelphijvoid writefd(l4)
179170754Sdelphijl4cfg_t *l4;
180170754Sdelphij{
181170754Sdelphij	char buf[80], *ptr;
182170754Sdelphij	int n, i, fd;
183170754Sdelphij
184170754Sdelphij	fd = l4->l4_fd;
185170754Sdelphij
186170754Sdelphij	if (l4->l4_rw == -2) {
187170754Sdelphij		connectfd(l4);
188170754Sdelphij		return;
189170754Sdelphij	}
190170754Sdelphij
191170754Sdelphij	n = l4->l4_wlen;
192170754Sdelphij
193170754Sdelphij	i = send(fd, l4->l4_wptr, n, 0);
194170754Sdelphij	if (i == 0 || i == -1) {
195170754Sdelphij		if (opts & OPT_VERBOSE)
196170754Sdelphij			fprintf(stderr, "Send on fd %d failed: %s\n",
197170754Sdelphij				fd, strerror(errno));
198170754Sdelphij		closel4(l4, 1);
199170754Sdelphij	} else {
200170754Sdelphij		l4->l4_wptr += i;
201170754Sdelphij		l4->l4_wlen -= i;
202170754Sdelphij		if (l4->l4_wlen == 0)
203170754Sdelphij			l4->l4_rw = 0;
204170754Sdelphij		if (opts & OPT_VERBOSE)
205170754Sdelphij			fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd);
206170754Sdelphij	}
207170754Sdelphij}
208170754Sdelphij
209170754Sdelphij
210170754Sdelphijvoid readfd(l4)
211170754Sdelphijl4cfg_t *l4;
212170754Sdelphij{
213170754Sdelphij	char buf[80], *ptr;
214170754Sdelphij	int n, i, fd;
215170754Sdelphij
216170754Sdelphij	fd = l4->l4_fd;
217170754Sdelphij
218170754Sdelphij	if (l4->l4_rw == -2) {
219170754Sdelphij		connectfd(l4);
220170754Sdelphij		return;
221170754Sdelphij	}
222170754Sdelphij
223170754Sdelphij	if (l4->l4_rsize) {
224170754Sdelphij		n = l4->l4_rsize - l4->l4_rlen;
225170754Sdelphij		ptr = l4->l4_rbuf + l4->l4_rlen;
226170754Sdelphij	} else {
227170754Sdelphij		n = sizeof(buf) - 1;
228170754Sdelphij		ptr = buf;
229170754Sdelphij	}
230170754Sdelphij
231170754Sdelphij	if (opts & OPT_VERBOSE)
232170754Sdelphij		fprintf(stderr, "Read %d bytes on fd %d to %p\n",
233170754Sdelphij			n, fd, ptr);
234170754Sdelphij	i = recv(fd, ptr, n, 0);
235170754Sdelphij	if (i == 0 || i == -1) {
236170754Sdelphij		if (opts & OPT_VERBOSE)
237170754Sdelphij			fprintf(stderr, "Read error on fd %d: %s\n",
238170754Sdelphij				fd, (i == 0) ? "EOF" : strerror(errno));
239170754Sdelphij		closel4(l4, 1);
240170754Sdelphij	} else {
241170754Sdelphij		if (ptr == buf)
242170754Sdelphij			ptr[i] = '\0';
243170754Sdelphij		if (opts & OPT_VERBOSE)
244170754Sdelphij			fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
245170754Sdelphij				fd, i, i, i, ptr);
246170754Sdelphij		if (ptr != buf) {
247170754Sdelphij			l4->l4_rlen += i;
248170754Sdelphij			if (l4->l4_rlen >= l4->l4_rsize) {
249170754Sdelphij				if (!strncmp(response, l4->l4_rbuf,
250170754Sdelphij					     l4->l4_rsize)) {
251170754Sdelphij					printf("%d: Good response\n",
252170754Sdelphij						fd);
253170754Sdelphij					if (!l4->l4_alive) {
254170754Sdelphij						l4->l4_alive = 1;
255170754Sdelphij						addnat(l4);
256170754Sdelphij					}
257170754Sdelphij					closel4(l4, 0);
258170754Sdelphij				} else {
259170754Sdelphij					if (opts & OPT_VERBOSE)
260170754Sdelphij						printf("%d: Bad response\n",
261170754Sdelphij							fd);
262170754Sdelphij					closel4(l4, 1);
263170754Sdelphij				}
264170754Sdelphij			}
265170754Sdelphij		} else if (!l4->l4_alive) {
266170754Sdelphij			l4->l4_alive = 1;
267170754Sdelphij			addnat(l4);
268170754Sdelphij			closel4(l4, 0);
269170754Sdelphij		}
270170754Sdelphij	}
271170754Sdelphij}
272170754Sdelphij
273170754Sdelphij
274170754Sdelphijint runconfig()
275170754Sdelphij{
276170754Sdelphij	int fd, opt, res, mfd, i;
277170754Sdelphij	struct timeval tv;
278170754Sdelphij	time_t now, now1;
279170754Sdelphij	fd_set rfd, wfd;
280170754Sdelphij	l4cfg_t *l4;
281170754Sdelphij
282170754Sdelphij	mfd = 0;
283170754Sdelphij	opt = 1;
284170754Sdelphij	now = time(NULL);
285170754Sdelphij
286170754Sdelphij	/*
287170754Sdelphij	 * First, initiate connections that are closed, as required.
288170754Sdelphij	 */
289170754Sdelphij	for (l4 = l4list; l4; l4 = l4->l4_next) {
290170754Sdelphij		if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) {
291170754Sdelphij			l4->l4_last = now;
292170754Sdelphij			fd = socket(AF_INET, SOCK_STREAM, 0);
293170754Sdelphij			if (fd == -1)
294170754Sdelphij				continue;
295170754Sdelphij			setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
296170754Sdelphij				   sizeof(opt));
297170754Sdelphij#ifdef	O_NONBLOCK
298170754Sdelphij			if ((res = fcntl(fd, F_GETFL, 0)) != -1)
299170754Sdelphij				fcntl(fd, F_SETFL, res | O_NONBLOCK);
300170754Sdelphij#endif
301170754Sdelphij			if (opts & OPT_VERBOSE)
302170754Sdelphij				fprintf(stderr,
303170754Sdelphij					"Connecting to %s,%d (fd %d)...",
304170754Sdelphij					inet_ntoa(l4->l4_sin.sin_addr),
305170754Sdelphij					ntohs(l4->l4_sin.sin_port), fd);
306170754Sdelphij			if (connect(fd, (struct sockaddr *)&l4->l4_sin,
307170754Sdelphij				    sizeof(l4->l4_sin)) == -1) {
308170754Sdelphij				if (errno != EINPROGRESS) {
309170754Sdelphij					if (opts & OPT_VERBOSE)
310170754Sdelphij						fprintf(stderr, "failed\n");
311170754Sdelphij					perror("connect");
312170754Sdelphij					close(fd);
313170754Sdelphij					fd = -1;
314170754Sdelphij				} else {
315170754Sdelphij					if (opts & OPT_VERBOSE)
316170754Sdelphij						fprintf(stderr, "waiting\n");
317170754Sdelphij					l4->l4_rw = -2;
318170754Sdelphij				}
319170754Sdelphij			} else {
320170754Sdelphij				if (opts & OPT_VERBOSE)
321170754Sdelphij					fprintf(stderr, "connected\n");
322170754Sdelphij				connectl4(l4);
323170754Sdelphij			}
324170754Sdelphij			l4->l4_fd = fd;
325170754Sdelphij		}
326170754Sdelphij	}
327170754Sdelphij
328170754Sdelphij	/*
329170754Sdelphij	 * Now look for fd's which we're expecting to read/write from.
330170754Sdelphij	 */
331170754Sdelphij	FD_ZERO(&rfd);
332170754Sdelphij	FD_ZERO(&wfd);
333170754Sdelphij	tv.tv_sec = MIN(rtimeout, ctimeout);
334170754Sdelphij	tv.tv_usec = 0;
335170754Sdelphij
336170754Sdelphij	for (l4 = l4list; l4; l4 = l4->l4_next)
337170754Sdelphij		if (l4->l4_rw == 0) {
338170754Sdelphij			if (now - l4->l4_last > rtimeout) {
339170754Sdelphij				if (opts & OPT_VERBOSE)
340170754Sdelphij					fprintf(stderr, "%d: Read timeout\n",
341170754Sdelphij						l4->l4_fd);
342170754Sdelphij				closel4(l4, 1);
343170754Sdelphij				continue;
344170754Sdelphij			}
345170754Sdelphij			if (opts & OPT_VERBOSE)
346170754Sdelphij				fprintf(stderr, "Wait for read on fd %d\n",
347170754Sdelphij					l4->l4_fd);
348170754Sdelphij			FD_SET(l4->l4_fd, &rfd);
349170754Sdelphij			if (l4->l4_fd > mfd)
350170754Sdelphij				mfd = l4->l4_fd;
351170754Sdelphij		} else if ((l4->l4_rw == 1 && l4->l4_wlen) ||
352170754Sdelphij			   l4->l4_rw == -2) {
353170754Sdelphij			if ((l4->l4_rw == -2) &&
354170754Sdelphij			    (now - l4->l4_last > ctimeout)) {
355170754Sdelphij				if (opts & OPT_VERBOSE)
356170754Sdelphij					fprintf(stderr,
357170754Sdelphij						"%d: connect timeout\n",
358170754Sdelphij						l4->l4_fd);
359170754Sdelphij				closel4(l4);
360170754Sdelphij				continue;
361170754Sdelphij			}
362170754Sdelphij			if (opts & OPT_VERBOSE)
363170754Sdelphij				fprintf(stderr, "Wait for write on fd %d\n",
364170754Sdelphij					l4->l4_fd);
365170754Sdelphij			FD_SET(l4->l4_fd, &wfd);
366170754Sdelphij			if (l4->l4_fd > mfd)
367170754Sdelphij				mfd = l4->l4_fd;
368170754Sdelphij		}
369170754Sdelphij
370170754Sdelphij	if (opts & OPT_VERBOSE)
371170754Sdelphij		fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1,
372170754Sdelphij			tv.tv_sec);
373170754Sdelphij	i = select(mfd + 1, &rfd, &wfd, NULL, &tv);
374170754Sdelphij	if (i == -1) {
375170754Sdelphij		perror("select");
376170754Sdelphij		return -1;
377170754Sdelphij	}
378170754Sdelphij
379170754Sdelphij	now1 = time(NULL);
380170754Sdelphij
381170754Sdelphij	for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) {
382170754Sdelphij		if (l4->l4_fd < 0)
383170754Sdelphij			continue;
384170754Sdelphij		if (FD_ISSET(l4->l4_fd, &rfd)) {
385170754Sdelphij			if (opts & OPT_VERBOSE)
386170754Sdelphij				fprintf(stderr, "Ready to read on fd %d\n",
387170754Sdelphij					l4->l4_fd);
388170754Sdelphij			readfd(l4);
389170754Sdelphij			i--;
390170754Sdelphij		}
391170754Sdelphij
392170754Sdelphij		if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) {
393170754Sdelphij			if (opts & OPT_VERBOSE)
394170754Sdelphij				fprintf(stderr, "Ready to write on fd %d\n",
395170754Sdelphij					l4->l4_fd);
396170754Sdelphij			writefd(l4);
397170754Sdelphij			i--;
398170754Sdelphij		}
399170754Sdelphij	}
400170754Sdelphij	return 0;
401170754Sdelphij}
402170754Sdelphij
403170754Sdelphij
404170754Sdelphijint gethostport(str, lnum, ipp, portp)
405170754Sdelphijchar *str;
406170754Sdelphijint lnum;
407170754Sdelphiju_32_t *ipp;
408170754Sdelphiju_short *portp;
409170754Sdelphij{
410170754Sdelphij	struct servent *sp;
411170754Sdelphij	struct hostent *hp;
412170754Sdelphij	char *host, *port;
413170754Sdelphij	struct in_addr ip;
414170754Sdelphij
415170754Sdelphij	host = str;
416170754Sdelphij	port = strchr(host, ',');
417170754Sdelphij	if (port)
418170754Sdelphij		*port++ = '\0';
419170754Sdelphij
420170754Sdelphij#ifdef	HAVE_INET_ATON
421170754Sdelphij	if (ISDIGIT(*host) && inet_aton(host, &ip))
422170754Sdelphij		*ipp = ip.s_addr;
423170754Sdelphij#else
424170754Sdelphij	if (ISDIGIT(*host))
425170754Sdelphij		*ipp = inet_addr(host);
426170754Sdelphij#endif
427170754Sdelphij	else {
428170754Sdelphij		if (!(hp = gethostbyname(host))) {
429170754Sdelphij			fprintf(stderr, "%d: can't resolve hostname: %s\n",
430170754Sdelphij				lnum, host);
431170754Sdelphij			return 0;
432170754Sdelphij		}
433170754Sdelphij		*ipp = *(u_32_t *)hp->h_addr;
434170754Sdelphij	}
435170754Sdelphij
436170754Sdelphij	if (port) {
437170754Sdelphij		if (ISDIGIT(*port))
438170754Sdelphij			*portp = htons(atoi(port));
439170754Sdelphij		else {
440170754Sdelphij			sp = getservbyname(port, "tcp");
441170754Sdelphij			if (sp)
442170754Sdelphij				*portp = sp->s_port;
443170754Sdelphij			else {
444170754Sdelphij				fprintf(stderr, "%d: unknown service %s\n",
445170754Sdelphij					lnum, port);
446170754Sdelphij				return 0;
447170754Sdelphij			}
448170754Sdelphij		}
449170754Sdelphij	} else
450170754Sdelphij		*portp = 0;
451170754Sdelphij	return 1;
452170754Sdelphij}
453170754Sdelphij
454170754Sdelphij
455170754Sdelphijchar *mapfile(file, sizep)
456170754Sdelphijchar *file;
457170754Sdelphijsize_t *sizep;
458170754Sdelphij{
459170754Sdelphij	struct stat sb;
460170754Sdelphij	caddr_t addr;
461170754Sdelphij	int fd;
462170754Sdelphij
463170754Sdelphij	fd = open(file, O_RDONLY);
464170754Sdelphij	if (fd == -1) {
465170754Sdelphij		perror("open(mapfile)");
466170754Sdelphij		return NULL;
467170754Sdelphij	}
468170754Sdelphij
469170754Sdelphij	if (fstat(fd, &sb) == -1) {
470170754Sdelphij		perror("fstat(mapfile)");
471170754Sdelphij		close(fd);
472170754Sdelphij		return NULL;
473170754Sdelphij	}
474170754Sdelphij
475170754Sdelphij	addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
476170754Sdelphij	if (addr == (caddr_t)-1) {
477170754Sdelphij		perror("mmap(mapfile)");
478170754Sdelphij		close(fd);
479170754Sdelphij		return NULL;
480170754Sdelphij	}
481170754Sdelphij	close(fd);
482170754Sdelphij	*sizep = sb.st_size;
483170754Sdelphij	return (char *)addr;
484170754Sdelphij}
485170754Sdelphij
486170754Sdelphij
487170754Sdelphijint readconfig(filename)
488170754Sdelphijchar *filename;
489170754Sdelphij{
490170754Sdelphij	char c, buf[512], *s, *t, *errtxt = NULL, *line;
491170754Sdelphij	int num, err = 0;
492170754Sdelphij	ipnat_t *ipn;
493170754Sdelphij	l4cfg_t *l4;
494170754Sdelphij	FILE *fp;
495170754Sdelphij
496170754Sdelphij	fp = fopen(filename, "r");
497170754Sdelphij	if (!fp) {
498170754Sdelphij		perror("open(configfile)");
499170754Sdelphij		return -1;
500170754Sdelphij	}
501170754Sdelphij
502170754Sdelphij	bzero((char *)&template, sizeof(template));
503170754Sdelphij	template.l4_fd = -1;
504170754Sdelphij	template.l4_rw = -1;
505170754Sdelphij	template.l4_sin.sin_family = AF_INET;
506170754Sdelphij	ipn = &template.l4_nat;
507170754Sdelphij	ipn->in_flags = IPN_TCP|IPN_ROUNDR;
508170754Sdelphij	ipn->in_redir = NAT_REDIRECT;
509170754Sdelphij
510170754Sdelphij	for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
511170754Sdelphij		s = strchr(buf, '\n');
512170754Sdelphij		if  (!s) {
513170754Sdelphij			fprintf(stderr, "%d: line too long\n", num);
514170754Sdelphij			fclose(fp);
515170754Sdelphij			return -1;
516170754Sdelphij		}
517170754Sdelphij
518170754Sdelphij		*s = '\0';
519170754Sdelphij
520170754Sdelphij		/*
521170754Sdelphij		 * lines which are comments
522170754Sdelphij		 */
523170754Sdelphij		s = strchr(buf, '#');
524170754Sdelphij		if (s)
525170754Sdelphij			*s = '\0';
526170754Sdelphij
527170754Sdelphij		/*
528170754Sdelphij		 * Skip leading whitespace
529170754Sdelphij		 */
530170754Sdelphij		for (line = buf; (c = *line) && ISSPACE(c); line++)
531170754Sdelphij			;
532170754Sdelphij		if (!*line)
533170754Sdelphij			continue;
534170754Sdelphij
535170754Sdelphij		if (opts & OPT_VERBOSE)
536170754Sdelphij			fprintf(stderr, "Parsing: [%s]\n", line);
537170754Sdelphij		t = strtok(line, " \t");
538170754Sdelphij		if (!t)
539170754Sdelphij			continue;
540170754Sdelphij		if (!strcasecmp(t, "interface")) {
541170754Sdelphij			s = strtok(NULL, " \t");
542170754Sdelphij			if (s)
543170754Sdelphij				t = strtok(NULL, "\t");
544170754Sdelphij			if (!s || !t) {
545170754Sdelphij				errtxt = line;
546170754Sdelphij				err = -1;
547170754Sdelphij				break;
548170754Sdelphij			}
549170754Sdelphij
550170754Sdelphij			if (!strchr(t, ',')) {
551170754Sdelphij				fprintf(stderr,
552170754Sdelphij					"%d: local address,port missing\n",
553170754Sdelphij					num);
554170754Sdelphij				err = -1;
555170754Sdelphij				break;
556170754Sdelphij			}
557170754Sdelphij
558170754Sdelphij			strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname));
559170754Sdelphij			if (!gethostport(t, num, &ipn->in_outip,
560170754Sdelphij					 &ipn->in_pmin)) {
561170754Sdelphij				errtxt = line;
562170754Sdelphij				err = -1;
563170754Sdelphij				break;
564170754Sdelphij			}
565170754Sdelphij			ipn->in_outmsk = 0xffffffff;
566170754Sdelphij			ipn->in_pmax = ipn->in_pmin;
567170754Sdelphij			if (opts & OPT_VERBOSE)
568170754Sdelphij				fprintf(stderr,
569170754Sdelphij					"Interface %s %s/%#x port %u\n",
570170754Sdelphij					ipn->in_ifname,
571170754Sdelphij					inet_ntoa(ipn->in_out[0]),
572170754Sdelphij					ipn->in_outmsk, ipn->in_pmin);
573170754Sdelphij		} else if (!strcasecmp(t, "remote")) {
574170754Sdelphij			if (!*ipn->in_ifname) {
575170754Sdelphij				fprintf(stderr,
576170754Sdelphij					"%d: ifname not set prior to remote\n",
577170754Sdelphij					num);
578170754Sdelphij				err = -1;
579170754Sdelphij				break;
580170754Sdelphij			}
581170754Sdelphij			s = strtok(NULL, " \t");
582170754Sdelphij			if (s)
583170754Sdelphij				t = strtok(NULL, "");
584170754Sdelphij			if (!s || !t || strcasecmp(s, "server")) {
585170754Sdelphij				errtxt = line;
586170754Sdelphij				err = -1;
587170754Sdelphij				break;
588170754Sdelphij			}
589170754Sdelphij
590170754Sdelphij			ipn->in_pnext = 0;
591170754Sdelphij			if (!gethostport(t, num, &ipn->in_inip,
592170754Sdelphij					 &ipn->in_pnext)) {
593170754Sdelphij				errtxt = line;
594170754Sdelphij				err = -1;
595170754Sdelphij				break;
596170754Sdelphij			}
597170754Sdelphij			ipn->in_inmsk = 0xffffffff;
598170754Sdelphij			if (ipn->in_pnext == 0)
599170754Sdelphij				ipn->in_pnext = ipn->in_pmin;
600170754Sdelphij
601170754Sdelphij			l4 = (l4cfg_t *)malloc(sizeof(*l4));
602170754Sdelphij			if (!l4) {
603170754Sdelphij				fprintf(stderr, "%d: out of memory (%d)\n",
604170754Sdelphij					num, sizeof(*l4));
605170754Sdelphij				err = -1;
606170754Sdelphij				break;
607170754Sdelphij			}
608170754Sdelphij			bcopy((char *)&template, (char *)l4, sizeof(*l4));
609170754Sdelphij			l4->l4_sin.sin_addr = ipn->in_in[0];
610170754Sdelphij			l4->l4_sin.sin_port = ipn->in_pnext;
611170754Sdelphij			l4->l4_next = l4list;
612170754Sdelphij			l4list = l4;
613170754Sdelphij		} else if (!strcasecmp(t, "connect")) {
614170754Sdelphij			s = strtok(NULL, " \t");
615170754Sdelphij			if (s)
616170754Sdelphij				t = strtok(NULL, "\t");
617170754Sdelphij			if (!s || !t) {
618170754Sdelphij				errtxt = line;
619170754Sdelphij				err = -1;
620170754Sdelphij				break;
621170754Sdelphij			} else if (!strcasecmp(s, "timeout")) {
622170754Sdelphij				ctimeout = atoi(t);
623170754Sdelphij				if (opts & OPT_VERBOSE)
624170754Sdelphij					fprintf(stderr, "connect timeout %d\n",
625170754Sdelphij						ctimeout);
626170754Sdelphij			} else if (!strcasecmp(s, "frequency")) {
627170754Sdelphij				frequency = atoi(t);
628170754Sdelphij				if (opts & OPT_VERBOSE)
629170754Sdelphij					fprintf(stderr,
630170754Sdelphij						"connect frequency %d\n",
631170754Sdelphij						frequency);
632170754Sdelphij			} else {
633170754Sdelphij				errtxt = line;
634170754Sdelphij				err = -1;
635170754Sdelphij				break;
636170754Sdelphij			}
637170754Sdelphij		} else if (!strcasecmp(t, "probe")) {
638170754Sdelphij			s = strtok(NULL, " \t");
639170754Sdelphij			if (!s) {
640170754Sdelphij				errtxt = line;
641170754Sdelphij				err = -1;
642170754Sdelphij				break;
643170754Sdelphij			} else if (!strcasecmp(s, "string")) {
644170754Sdelphij				if (probe) {
645170754Sdelphij					fprintf(stderr,
646170754Sdelphij						"%d: probe already set\n",
647170754Sdelphij						num);
648170754Sdelphij					err = -1;
649170754Sdelphij					break;
650170754Sdelphij				}
651170754Sdelphij				t = strtok(NULL, "");
652170754Sdelphij				if (!t) {
653170754Sdelphij					fprintf(stderr,
654170754Sdelphij						"%d: No probe string\n", num);
655170754Sdelphij					err = -1;
656170754Sdelphij					break;
657170754Sdelphij				}
658170754Sdelphij
659170754Sdelphij				probe = malloc(strlen(t));
660170754Sdelphij				copystr(probe, t);
661170754Sdelphij				plen = strlen(probe);
662170754Sdelphij				if (opts & OPT_VERBOSE)
663170754Sdelphij					fprintf(stderr, "Probe string [%s]\n",
664170754Sdelphij						probe);
665170754Sdelphij			} else if (!strcasecmp(s, "file")) {
666170754Sdelphij				t = strtok(NULL, " \t");
667170754Sdelphij				if (!t) {
668170754Sdelphij					errtxt = line;
669170754Sdelphij					err = -1;
670170754Sdelphij					break;
671170754Sdelphij				}
672170754Sdelphij				if (probe) {
673170754Sdelphij					fprintf(stderr,
674170754Sdelphij						"%d: probe already set\n",
675170754Sdelphij						num);
676170754Sdelphij					err = -1;
677170754Sdelphij					break;
678170754Sdelphij				}
679170754Sdelphij				probe = mapfile(t, &plen);
680170754Sdelphij				if (opts & OPT_VERBOSE)
681170754Sdelphij					fprintf(stderr,
682170754Sdelphij						"Probe file %s len %u@%p\n",
683170754Sdelphij						t, plen, probe);
684170754Sdelphij			}
685170754Sdelphij		} else if (!strcasecmp(t, "response")) {
686170754Sdelphij			s = strtok(NULL, " \t");
687170754Sdelphij			if (!s) {
688170754Sdelphij				errtxt = line;
689170754Sdelphij				err = -1;
690170754Sdelphij				break;
691170754Sdelphij			} else if (!strcasecmp(s, "timeout")) {
692170754Sdelphij				t = strtok(NULL, " \t");
693170754Sdelphij				if (!t) {
694170754Sdelphij					errtxt = line;
695170754Sdelphij					err = -1;
696170754Sdelphij					break;
697170754Sdelphij				}
698170754Sdelphij				rtimeout = atoi(t);
699170754Sdelphij				if (opts & OPT_VERBOSE)
700170754Sdelphij					fprintf(stderr,
701170754Sdelphij						"response timeout %d\n",
702170754Sdelphij						rtimeout);
703170754Sdelphij			} else if (!strcasecmp(s, "string")) {
704170754Sdelphij				if (response) {
705170754Sdelphij					fprintf(stderr,
706170754Sdelphij						"%d: response already set\n",
707170754Sdelphij						num);
708170754Sdelphij					err = -1;
709170754Sdelphij					break;
710170754Sdelphij				}
711170754Sdelphij				response = strdup(strtok(NULL, ""));
712170754Sdelphij				rlen = strlen(response);
713170754Sdelphij				template.l4_rsize = rlen;
714170754Sdelphij				template.l4_rbuf = malloc(rlen);
715170754Sdelphij				if (opts & OPT_VERBOSE)
716170754Sdelphij					fprintf(stderr,
717170754Sdelphij						"Response string [%s]\n",
718170754Sdelphij						response);
719170754Sdelphij			} else if (!strcasecmp(s, "file")) {
720170754Sdelphij				t = strtok(NULL, " \t");
721170754Sdelphij				if (!t) {
722170754Sdelphij					errtxt = line;
723170754Sdelphij					err = -1;
724170754Sdelphij					break;
725170754Sdelphij				}
726170754Sdelphij				if (response) {
727170754Sdelphij					fprintf(stderr,
728170754Sdelphij						"%d: response already set\n",
729170754Sdelphij						num);
730170754Sdelphij					err = -1;
731170754Sdelphij					break;
732170754Sdelphij				}
733170754Sdelphij				response = mapfile(t, &rlen);
734170754Sdelphij				template.l4_rsize = rlen;
735170754Sdelphij				template.l4_rbuf = malloc(rlen);
736170754Sdelphij				if (opts & OPT_VERBOSE)
737170754Sdelphij					fprintf(stderr,
738170754Sdelphij						"Response file %s len %u@%p\n",
739170754Sdelphij						t, rlen, response);
740170754Sdelphij			}
741170754Sdelphij		} else {
742170754Sdelphij			errtxt = line;
743170754Sdelphij			err = -1;
744170754Sdelphij			break;
745170754Sdelphij		}
746170754Sdelphij	}
747170754Sdelphij
748170754Sdelphij	if (errtxt)
749170754Sdelphij		fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
750170754Sdelphij	fclose(fp);
751170754Sdelphij	return err;
752170754Sdelphij}
753170754Sdelphij
754170754Sdelphij
755170754Sdelphijvoid usage(prog)
756170754Sdelphijchar *prog;
757170754Sdelphij{
758170754Sdelphij	fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
759170754Sdelphij	exit(1);
760170754Sdelphij}
761170754Sdelphij
762170754Sdelphij
763170754Sdelphijint main(argc, argv)
764170754Sdelphijint argc;
765170754Sdelphijchar *argv[];
766170754Sdelphij{
767170754Sdelphij	char *config = NULL;
768170754Sdelphij	int c;
769170754Sdelphij
770170754Sdelphij	while ((c = getopt(argc, argv, "f:nv")) != -1)
771170754Sdelphij		switch (c)
772170754Sdelphij		{
773170754Sdelphij		case 'f' :
774170754Sdelphij			config = optarg;
775170754Sdelphij			break;
776		case 'n' :
777			opts |= OPT_DONOTHING;
778			break;
779		case 'v' :
780			opts |= OPT_VERBOSE;
781			break;
782		}
783
784	if (config == NULL)
785		usage(argv[0]);
786
787	if (readconfig(config))
788		exit(1);
789
790	if (!l4list) {
791		fprintf(stderr, "No remote servers, exiting.");
792		exit(1);
793	}
794
795	if (!(opts & OPT_DONOTHING)) {
796		natfd = open(IPL_NAT, O_RDWR);
797		if (natfd == -1) {
798			perror("open(IPL_NAT)");
799			exit(1);
800		}
801	}
802
803	if (opts & OPT_VERBOSE)
804		fprintf(stderr, "Starting...\n");
805	while (runconfig() == 0)
806		;
807}
808