1/*	$FreeBSD$	*/
2
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>
12
13#include <netinet/in.h>
14#include <netinet/in_systm.h>
15#include <netinet/ip.h>
16
17#include <net/if.h>
18
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>
26
27#include "ip_compat.h"
28#include "ip_fil.h"
29#include "ip_nat.h"
30#include "ipl.h"
31
32#include "ipf.h"
33
34extern	char	*optarg;
35
36
37typedef	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;
51
52
53l4cfg_t *l4list = NULL;
54char *response = NULL;
55char *probe = NULL;
56l4cfg_t template;
57int frequency = 20;
58int ctimeout = 1;
59int rtimeout = 1;
60size_t plen = 0;
61size_t rlen = 0;
62int natfd = -1;
63int opts = 0;
64
65#if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
66# define	strerror(x)	sys_errlist[x]
67#endif
68
69
70char *copystr(dst, src)
71char *dst, *src;
72{
73	register char *s, *t, c;
74	register int esc = 0;
75
76	for (s = src, t = dst; s && t && (c = *s++); )
77		if (esc) {
78			esc = 0;
79			switch (c)
80			{
81			case 'n' :
82				*t++ = '\n';
83				break;
84			case 'r' :
85				*t++ = '\r';
86				break;
87			case 't' :
88				*t++ = '\t';
89				break;
90			}
91		} else if (c != '\\')
92			*t++ = c;
93		else
94			esc = 1;
95	*t = '\0';
96	return dst;
97}
98
99void addnat(l4)
100l4cfg_t *l4;
101{
102
103	ipnat_t *ipn = &l4->l4_nat;
104
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;
110
111		bzero(&obj, sizeof(obj));
112		obj.ipfo_rev = IPFILTER_VERSION;
113		obj.ipfo_size = sizeof(*ipn);
114		obj.ipfo_ptr = ipn;
115
116		if (ioctl(natfd, SIOCADNAT, &obj) == -1)
117			perror("ioctl(SIOCADNAT)");
118	}
119}
120
121
122void delnat(l4)
123l4cfg_t *l4;
124{
125	ipnat_t *ipn = &l4->l4_nat;
126
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;
132
133		bzero(&obj, sizeof(obj));
134		obj.ipfo_rev = IPFILTER_VERSION;
135		obj.ipfo_size = sizeof(*ipn);
136		obj.ipfo_ptr = ipn;
137
138		if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
139			perror("ioctl(SIOCRMNAT)");
140	}
141}
142
143
144void connectl4(l4)
145l4cfg_t *l4;
146{
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;
155}
156
157
158void closel4(l4, dead)
159l4cfg_t *l4;
160int dead;
161{
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);
168	}
169}
170
171
172void connectfd(l4)
173l4cfg_t *l4;
174{
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;
183		}
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;
189	}
190	l4->l4_rw = 1;
191}
192
193
194void writefd(l4)
195l4cfg_t *l4;
196{
197	int n, i, fd;
198
199	fd = l4->l4_fd;
200
201	if (l4->l4_rw == -2) {
202		connectfd(l4);
203		return;
204	}
205
206	n = l4->l4_wlen;
207
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);
221	}
222}
223
224
225void readfd(l4)
226l4cfg_t *l4;
227{
228	char buf[80], *ptr;
229	int n, i, fd;
230
231	fd = l4->l4_fd;
232
233	if (l4->l4_rw == -2) {
234		connectfd(l4);
235		return;
236	}
237
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;
244	}
245
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);
271					}
272					closel4(l4, 0);
273				} else {
274					if (opts & OPT_VERBOSE)
275						printf("%d: Bad response\n",
276							fd);
277					closel4(l4, 1);
278				}
279			}
280		} else if (!l4->l4_alive) {
281			l4->l4_alive = 1;
282			addnat(l4);
283			closel4(l4, 0);
284		}
285	}
286}
287
288
289int runconfig()
290{
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;
296
297	mfd = 0;
298	opt = 1;
299	now = time(NULL);
300
301	/*
302	 * First, initiate connections that are closed, as required.
303	 */
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;
333				}
334			} else {
335				if (opts & OPT_VERBOSE)
336					fprintf(stderr, "connected\n");
337				connectl4(l4);
338			}
339			l4->l4_fd = fd;
340		}
341	}
342
343	/*
344	 * Now look for fd's which we're expecting to read/write from.
345	 */
346	FD_ZERO(&rfd);
347	FD_ZERO(&wfd);
348	tv.tv_sec = MIN(rtimeout, ctimeout);
349	tv.tv_usec = 0;
350
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;
359			}
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;
376			}
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;
383		}
384
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;
392	}
393
394	now1 = time(NULL);
395
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--;
405		}
406
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--;
413		}
414	}
415	return 0;
416}
417
418
419int gethostport(str, lnum, ipp, portp)
420char *str;
421int lnum;
422u_32_t *ipp;
423u_short *portp;
424{
425	struct servent *sp;
426	struct hostent *hp;
427	char *host, *port;
428
429	host = str;
430	port = strchr(host, ',');
431	if (port)
432		*port++ = '\0';
433
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;
446		}
447		*ipp = *(u_32_t *)hp->h_addr;
448	}
449
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;
461			}
462		}
463	} else
464		*portp = 0;
465	return 1;
466}
467
468
469char *mapfile(file, sizep)
470char *file;
471size_t *sizep;
472{
473	struct stat sb;
474	caddr_t addr;
475	int fd;
476
477	fd = open(file, O_RDONLY);
478	if (fd == -1) {
479		perror("open(mapfile)");
480		return NULL;
481	}
482
483	if (fstat(fd, &sb) == -1) {
484		perror("fstat(mapfile)");
485		close(fd);
486		return NULL;
487	}
488
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;
494	}
495	close(fd);
496	*sizep = sb.st_size;
497	return (char *)addr;
498}
499
500
501int readconfig(filename)
502char *filename;
503{
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;
509
510	fp = fopen(filename, "r");
511	if (!fp) {
512		perror("open(configfile)");
513		return -1;
514	}
515
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;
523
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;
530		}
531
532		*s = '\0';
533
534		/*
535		 * lines which are comments
536		 */
537		s = strchr(buf, '#');
538		if (s)
539			*s = '\0';
540
541		/*
542		 * Skip leading whitespace
543		 */
544		for (line = buf; (c = *line) && ISSPACE(c); line++)
545			;
546		if (!*line)
547			continue;
548
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;
562			}
563
564			if (!strchr(t, ',')) {
565				fprintf(stderr,
566					"%d: local address,port missing\n",
567					num);
568				err = -1;
569				break;
570			}
571
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;
579			}
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;
595			}
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;
603			}
604
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;
611			}
612			ipn->in_inmsk = 0xffffffff;
613			if (ipn->in_pnext == 0)
614				ipn->in_pnext = ipn->in_pmin;
615
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;
622			}
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;
651			}
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;
665				}
666				t = strtok(NULL, "");
667				if (!t) {
668					fprintf(stderr,
669						"%d: No probe string\n", num);
670					err = -1;
671					break;
672				}
673
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;
686				}
687				if (probe) {
688					fprintf(stderr,
689						"%d: probe already set\n",
690						num);
691					err = -1;
692					break;
693				}
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);
699			}
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;
712				}
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;
725				}
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;
740				}
741				if (response) {
742					fprintf(stderr,
743						"%d: response already set\n",
744						num);
745					err = -1;
746					break;
747				}
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);
755			}
756		} else {
757			errtxt = line;
758			err = -1;
759			break;
760		}
761	}
762
763	if (errtxt)
764		fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
765	fclose(fp);
766	return err;
767}
768
769
770void usage(prog)
771char *prog;
772{
773	fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
774	exit(1);
775}
776
777
778int main(argc, argv)
779int argc;
780char *argv[];
781{
782	char *config = NULL;
783	int c;
784
785	while ((c = getopt(argc, argv, "f:nv")) != -1)
786		switch (c)
787		{
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;
797		}
798
799	if (config == NULL)
800		usage(argv[0]);
801
802	if (readconfig(config))
803		exit(1);
804
805	if (!l4list) {
806		fprintf(stderr, "No remote servers, exiting.");
807		exit(1);
808	}
809
810	if (!(opts & OPT_DONOTHING)) {
811		natfd = open(IPNAT_NAME, O_RDWR);
812		if (natfd == -1) {
813			perror("open(IPL_NAT)");
814			exit(1);
815		}
816	}
817
818	if (opts & OPT_VERBOSE)
819		fprintf(stderr, "Starting...\n");
820	while (runconfig() == 0)
821		;
822
823	exit(1);
824}
825