l4check.c revision 95419
1/*
2 * (C)Copyright March, 2000 - Darren Reed.
3 */
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <sys/mman.h>
7#include <sys/socket.h>
8#include <sys/time.h>
9#include <sys/ioctl.h>
10
11#include <netinet/in.h>
12#include <netinet/in_systm.h>
13#include <netinet/ip.h>
14
15#include <net/if.h>
16
17#include <stdio.h>
18#include <netdb.h>
19#include <string.h>
20#include <ctype.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <stdlib.h>
24
25#include "ip_compat.h"
26#include "ip_fil.h"
27#include "ip_nat.h"
28
29#include "ipf.h"
30
31extern	char	*optarg;
32
33
34typedef	struct	l4cfg	{
35	struct	l4cfg		*l4_next;
36	struct	ipnat		l4_nat;		/* NAT rule */
37	struct	sockaddr_in	l4_sin;		/* remote socket to connect */
38	time_t			l4_last;	/* when we last connected */
39	int			l4_alive;	/* 1 = remote alive */
40	int			l4_fd;
41	int			l4_rw;		/* 0 = reading, 1 = writing */
42	char			*l4_rbuf;	/* read buffer */
43	int			l4_rsize;	/* size of buffer */
44	int			l4_rlen;	/* how much used */
45	char			*l4_wptr;	/* next byte to write */
46	int			l4_wlen;	/* length yet to be written */
47} l4cfg_t;
48
49
50l4cfg_t *l4list = NULL;
51char *response = NULL;
52char *probe = NULL;
53l4cfg_t template;
54int frequency = 20;
55int ctimeout = 1;
56int rtimeout = 1;
57size_t plen = 0;
58size_t rlen = 0;
59int natfd = -1;
60int opts = 0;
61
62#if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
63# define	strerror(x)	sys_errlist[x]
64#endif
65
66
67char *copystr(dst, src)
68char *dst, *src;
69{
70	register char *s, *t, c;
71	register int esc = 0;
72
73	for (s = src, t = dst; s && t && (c = *s++); )
74		if (esc) {
75			esc = 0;
76			switch (c)
77			{
78			case 'n' :
79				*t++ = '\n';
80				break;
81			case 'r' :
82				*t++ = '\r';
83				break;
84			case 't' :
85				*t++ = '\t';
86				break;
87			}
88		} else if (c != '\\')
89			*t++ = c;
90		else
91			esc = 1;
92	*t = '\0';
93	return dst;
94}
95
96void addnat(l4)
97l4cfg_t *l4;
98{
99	ipnat_t *ipn = &l4->l4_nat;
100
101	printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]),
102		ipn->in_outmsk, ntohs(ipn->in_pmin));
103	printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext));
104	if (!(opts & OPT_DONOTHING)) {
105		if (ioctl(natfd, SIOCADNAT, &ipn) == -1)
106			perror("ioctl(SIOCADNAT)");
107	}
108}
109
110
111void delnat(l4)
112l4cfg_t *l4;
113{
114	ipnat_t *ipn = &l4->l4_nat;
115
116	printf("Remove NAT rule for %s/%#x,%u -> ",
117		inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin);
118	printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext);
119	if (!(opts & OPT_DONOTHING)) {
120		if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
121			perror("ioctl(SIOCRMNAT)");
122	}
123}
124
125
126void connectl4(l4)
127l4cfg_t *l4;
128{
129	l4->l4_rw = 1;
130	l4->l4_rlen = 0;
131	l4->l4_wlen = plen;
132	if (!l4->l4_wlen) {
133		l4->l4_alive = 1;
134		addnat(l4);
135	} else
136		l4->l4_wptr = probe;
137}
138
139
140void closel4(l4, dead)
141l4cfg_t *l4;
142int dead;
143{
144	if (l4->l4_fd != -1)
145		close(l4->l4_fd);
146	l4->l4_fd = -1;
147	l4->l4_rw = -1;
148	if (dead && l4->l4_alive) {
149		l4->l4_alive = 0;
150		delnat(l4);
151	}
152}
153
154
155void connectfd(l4)
156l4cfg_t *l4;
157{
158	if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin,
159		    sizeof(l4->l4_sin)) == -1) {
160		if (errno == EISCONN) {
161			if (opts & OPT_VERBOSE)
162				fprintf(stderr, "Connected fd %d\n",
163					l4->l4_fd);
164			connectl4(l4);
165			return;
166		}
167		if (opts & OPT_VERBOSE)
168			fprintf(stderr, "Connect failed fd %d: %s\n",
169				l4->l4_fd, strerror(errno));
170		closel4(l4, 1);
171		return;
172	}
173	l4->l4_rw = 1;
174}
175
176
177void writefd(l4)
178l4cfg_t *l4;
179{
180	char buf[80], *ptr;
181	int n, i, fd;
182
183	fd = l4->l4_fd;
184
185	if (l4->l4_rw == -2) {
186		connectfd(l4);
187		return;
188	}
189
190	n = l4->l4_wlen;
191
192	i = send(fd, l4->l4_wptr, n, 0);
193	if (i == 0 || i == -1) {
194		if (opts & OPT_VERBOSE)
195			fprintf(stderr, "Send on fd %d failed: %s\n",
196				fd, strerror(errno));
197		closel4(l4, 1);
198	} else {
199		l4->l4_wptr += i;
200		l4->l4_wlen -= i;
201		if (l4->l4_wlen == 0)
202			l4->l4_rw = 0;
203		if (opts & OPT_VERBOSE)
204			fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd);
205	}
206}
207
208
209void readfd(l4)
210l4cfg_t *l4;
211{
212	char buf[80], *ptr;
213	int n, i, fd;
214
215	fd = l4->l4_fd;
216
217	if (l4->l4_rw == -2) {
218		connectfd(l4);
219		return;
220	}
221
222	if (l4->l4_rsize) {
223		n = l4->l4_rsize - l4->l4_rlen;
224		ptr = l4->l4_rbuf + l4->l4_rlen;
225	} else {
226		n = sizeof(buf) - 1;
227		ptr = buf;
228	}
229
230	if (opts & OPT_VERBOSE)
231		fprintf(stderr, "Read %d bytes on fd %d to %p\n",
232			n, fd, ptr);
233	i = recv(fd, ptr, n, 0);
234	if (i == 0 || i == -1) {
235		if (opts & OPT_VERBOSE)
236			fprintf(stderr, "Read error on fd %d: %s\n",
237				fd, (i == 0) ? "EOF" : strerror(errno));
238		closel4(l4, 1);
239	} else {
240		if (ptr == buf)
241			ptr[i] = '\0';
242		if (opts & OPT_VERBOSE)
243			fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
244				fd, i, i, i, ptr);
245		if (ptr != buf) {
246			l4->l4_rlen += i;
247			if (l4->l4_rlen >= l4->l4_rsize) {
248				if (!strncmp(response, l4->l4_rbuf,
249					     l4->l4_rsize)) {
250					printf("%d: Good response\n",
251						fd);
252					if (!l4->l4_alive) {
253						l4->l4_alive = 1;
254						addnat(l4);
255					}
256					closel4(l4, 0);
257				} else {
258					if (opts & OPT_VERBOSE)
259						printf("%d: Bad response\n",
260							fd);
261					closel4(l4, 1);
262				}
263			}
264		} else if (!l4->l4_alive) {
265			l4->l4_alive = 1;
266			addnat(l4);
267			closel4(l4, 0);
268		}
269	}
270}
271
272
273int runconfig()
274{
275	int fd, opt, res, mfd, i;
276	struct timeval tv;
277	time_t now, now1;
278	fd_set rfd, wfd;
279	l4cfg_t *l4;
280
281	mfd = 0;
282	opt = 1;
283	now = time(NULL);
284
285	/*
286	 * First, initiate connections that are closed, as required.
287	 */
288	for (l4 = l4list; l4; l4 = l4->l4_next) {
289		if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) {
290			l4->l4_last = now;
291			fd = socket(AF_INET, SOCK_STREAM, 0);
292			if (fd == -1)
293				continue;
294			setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
295				   sizeof(opt));
296#ifdef	O_NONBLOCK
297			if ((res = fcntl(fd, F_GETFL, 0)) != -1)
298				fcntl(fd, F_SETFL, res | O_NONBLOCK);
299#endif
300			if (opts & OPT_VERBOSE)
301				fprintf(stderr,
302					"Connecting to %s,%d (fd %d)...",
303					inet_ntoa(l4->l4_sin.sin_addr),
304					ntohs(l4->l4_sin.sin_port), fd);
305			if (connect(fd, (struct sockaddr *)&l4->l4_sin,
306				    sizeof(l4->l4_sin)) == -1) {
307				if (errno != EINPROGRESS) {
308					if (opts & OPT_VERBOSE)
309						fprintf(stderr, "failed\n");
310					perror("connect");
311					closel4(l4, 1);
312					fd = -1;
313				} else {
314					if (opts & OPT_VERBOSE)
315						fprintf(stderr, "waiting\n");
316					l4->l4_rw = -2;
317				}
318			} else {
319				if (opts & OPT_VERBOSE)
320					fprintf(stderr, "connected\n");
321				connectl4(l4);
322			}
323			l4->l4_fd = fd;
324		}
325	}
326
327	/*
328	 * Now look for fd's which we're expecting to read/write from.
329	 */
330	FD_ZERO(&rfd);
331	FD_ZERO(&wfd);
332	tv.tv_sec = MIN(rtimeout, ctimeout);
333	tv.tv_usec = 0;
334
335	for (l4 = l4list; l4; l4 = l4->l4_next)
336		if (l4->l4_rw == 0) {
337			if (now - l4->l4_last > rtimeout) {
338				if (opts & OPT_VERBOSE)
339					fprintf(stderr, "%d: Read timeout\n",
340						l4->l4_fd);
341				closel4(l4, 1);
342				continue;
343			}
344			if (opts & OPT_VERBOSE)
345				fprintf(stderr, "Wait for read on fd %d\n",
346					l4->l4_fd);
347			FD_SET(l4->l4_fd, &rfd);
348			if (l4->l4_fd > mfd)
349				mfd = l4->l4_fd;
350		} else if ((l4->l4_rw == 1 && l4->l4_wlen) ||
351			   l4->l4_rw == -2) {
352			if ((l4->l4_rw == -2) &&
353			    (now - l4->l4_last > ctimeout)) {
354				if (opts & OPT_VERBOSE)
355					fprintf(stderr,
356						"%d: connect timeout\n",
357						l4->l4_fd);
358				closel4(l4);
359				continue;
360			}
361			if (opts & OPT_VERBOSE)
362				fprintf(stderr, "Wait for write on fd %d\n",
363					l4->l4_fd);
364			FD_SET(l4->l4_fd, &wfd);
365			if (l4->l4_fd > mfd)
366				mfd = l4->l4_fd;
367		}
368
369	if (opts & OPT_VERBOSE)
370		fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1,
371			tv.tv_sec);
372	i = select(mfd + 1, &rfd, &wfd, NULL, &tv);
373	if (i == -1) {
374		perror("select");
375		return -1;
376	}
377
378	now1 = time(NULL);
379
380	for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) {
381		if (l4->l4_fd < 0)
382			continue;
383		if (FD_ISSET(l4->l4_fd, &rfd)) {
384			if (opts & OPT_VERBOSE)
385				fprintf(stderr, "Ready to read on fd %d\n",
386					l4->l4_fd);
387			readfd(l4);
388			i--;
389		}
390
391		if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) {
392			if (opts & OPT_VERBOSE)
393				fprintf(stderr, "Ready to write on fd %d\n",
394					l4->l4_fd);
395			writefd(l4);
396			i--;
397		}
398	}
399	return 0;
400}
401
402
403int gethostport(str, lnum, ipp, portp)
404char *str;
405int lnum;
406u_32_t *ipp;
407u_short *portp;
408{
409	struct servent *sp;
410	struct hostent *hp;
411	char *host, *port;
412	struct in_addr ip;
413
414	host = str;
415	port = strchr(host, ',');
416	if (port)
417		*port++ = '\0';
418
419#ifdef	HAVE_INET_ATON
420	if (isdigit(*host) && inet_aton(host, &ip))
421		*ipp = ip.s_addr;
422#else
423	if (isdigit(*host))
424		*ipp = inet_addr(host);
425#endif
426	else {
427		if (!(hp = gethostbyname(host))) {
428			fprintf(stderr, "%d: can't resolve hostname: %s\n",
429				lnum, host);
430			return 0;
431		}
432		*ipp = *(u_32_t *)hp->h_addr;
433	}
434
435	if (port) {
436		if (isdigit(*port))
437			*portp = htons(atoi(port));
438		else {
439			sp = getservbyname(port, "tcp");
440			if (sp)
441				*portp = sp->s_port;
442			else {
443				fprintf(stderr, "%d: unknown service %s\n",
444					lnum, port);
445				return 0;
446			}
447		}
448	} else
449		*portp = 0;
450	return 1;
451}
452
453
454char *mapfile(file, sizep)
455char *file;
456size_t *sizep;
457{
458	struct stat sb;
459	caddr_t addr;
460	int fd;
461
462	fd = open(file, O_RDONLY);
463	if (fd == -1) {
464		perror("open(mapfile)");
465		return NULL;
466	}
467
468	if (fstat(fd, &sb) == -1) {
469		perror("fstat(mapfile)");
470		close(fd);
471		return NULL;
472	}
473
474	addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
475	if (addr == (caddr_t)-1) {
476		perror("mmap(mapfile)");
477		close(fd);
478		return NULL;
479	}
480	close(fd);
481	*sizep = sb.st_size;
482	return (char *)addr;
483}
484
485
486int readconfig(filename)
487char *filename;
488{
489	char c, buf[512], *s, *t, *errtxt = NULL, *line;
490	int num, err = 0;
491	ipnat_t *ipn;
492	l4cfg_t *l4;
493	FILE *fp;
494
495	fp = fopen(filename, "r");
496	if (!fp) {
497		perror("open(configfile)");
498		return -1;
499	}
500
501	bzero((char *)&template, sizeof(template));
502	template.l4_fd = -1;
503	template.l4_rw = -1;
504	template.l4_sin.sin_family = AF_INET;
505	ipn = &template.l4_nat;
506	ipn->in_flags = IPN_TCP|IPN_ROUNDR;
507	ipn->in_redir = NAT_REDIRECT;
508
509	for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
510		s = strchr(buf, '\n');
511		if  (!s) {
512			fprintf(stderr, "%d: line too long\n", num);
513			fclose(fp);
514			return -1;
515		}
516
517		*s = '\0';
518
519		/*
520		 * lines which are comments
521		 */
522		s = strchr(buf, '#');
523		if (s)
524			*s = '\0';
525
526		/*
527		 * Skip leading whitespace
528		 */
529		for (line = buf; (c = *line) && isspace(c); line++)
530			;
531		if (!*line)
532			continue;
533
534		if (opts & OPT_VERBOSE)
535			fprintf(stderr, "Parsing: [%s]\n", line);
536		t = strtok(line, " \t");
537		if (!t)
538			continue;
539		if (!strcasecmp(t, "interface")) {
540			s = strtok(NULL, " \t");
541			if (s)
542				t = strtok(NULL, "\t");
543			if (!s || !t) {
544				errtxt = line;
545				err = -1;
546				break;
547			}
548
549			if (!strchr(t, ',')) {
550				fprintf(stderr,
551					"%d: local address,port missing\n",
552					num);
553				err = -1;
554				break;
555			}
556
557			strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname));
558			if (!gethostport(t, num, &ipn->in_outip,
559					 &ipn->in_pmin)) {
560				errtxt = line;
561				err = -1;
562				break;
563			}
564			ipn->in_outmsk = 0xffffffff;
565			ipn->in_pmax = ipn->in_pmin;
566			if (opts & OPT_VERBOSE)
567				fprintf(stderr,
568					"Interface %s %s/%#x port %u\n",
569					ipn->in_ifname,
570					inet_ntoa(ipn->in_out[0]),
571					ipn->in_outmsk, ipn->in_pmin);
572		} else if (!strcasecmp(t, "remote")) {
573			if (!*ipn->in_ifname) {
574				fprintf(stderr,
575					"%d: ifname not set prior to remote\n",
576					num);
577				err = -1;
578				break;
579			}
580			s = strtok(NULL, " \t");
581			if (s)
582				t = strtok(NULL, "");
583			if (!s || !t || strcasecmp(s, "server")) {
584				errtxt = line;
585				err = -1;
586				break;
587			}
588
589			ipn->in_pnext = 0;
590			if (!gethostport(t, num, &ipn->in_inip,
591					 &ipn->in_pnext)) {
592				errtxt = line;
593				err = -1;
594				break;
595			}
596			ipn->in_inmsk = 0xffffffff;
597			if (ipn->in_pnext == 0)
598				ipn->in_pnext = ipn->in_pmin;
599
600			l4 = (l4cfg_t *)malloc(sizeof(*l4));
601			if (!l4) {
602				fprintf(stderr, "%d: out of memory (%d)\n",
603					num, sizeof(*l4));
604				err = -1;
605				break;
606			}
607			bcopy((char *)&template, (char *)l4, sizeof(*l4));
608			l4->l4_sin.sin_addr = ipn->in_in[0];
609			l4->l4_sin.sin_port = ipn->in_pnext;
610			l4->l4_next = l4list;
611			l4list = l4;
612		} else if (!strcasecmp(t, "connect")) {
613			s = strtok(NULL, " \t");
614			if (s)
615				t = strtok(NULL, "\t");
616			if (!s || !t) {
617				errtxt = line;
618				err = -1;
619				break;
620			} else if (!strcasecmp(s, "timeout")) {
621				ctimeout = atoi(t);
622				if (opts & OPT_VERBOSE)
623					fprintf(stderr, "connect timeout %d\n",
624						ctimeout);
625			} else if (!strcasecmp(s, "frequency")) {
626				frequency = atoi(t);
627				if (opts & OPT_VERBOSE)
628					fprintf(stderr,
629						"connect frequency %d\n",
630						frequency);
631			} else {
632				errtxt = line;
633				err = -1;
634				break;
635			}
636		} else if (!strcasecmp(t, "probe")) {
637			s = strtok(NULL, " \t");
638			if (!s) {
639				errtxt = line;
640				err = -1;
641				break;
642			} else if (!strcasecmp(s, "string")) {
643				if (probe) {
644					fprintf(stderr,
645						"%d: probe already set\n",
646						num);
647					err = -1;
648					break;
649				}
650				t = strtok(NULL, "");
651				if (!t) {
652					fprintf(stderr,
653						"%d: No probe string\n", num);
654					err = -1;
655					break;
656				}
657
658				probe = malloc(strlen(t));
659				copystr(probe, t);
660				plen = strlen(probe);
661				if (opts & OPT_VERBOSE)
662					fprintf(stderr, "Probe string [%s]\n",
663						probe);
664			} else if (!strcasecmp(s, "file")) {
665				t = strtok(NULL, " \t");
666				if (!t) {
667					errtxt = line;
668					err = -1;
669					break;
670				}
671				if (probe) {
672					fprintf(stderr,
673						"%d: probe already set\n",
674						num);
675					err = -1;
676					break;
677				}
678				probe = mapfile(t, &plen);
679				if (opts & OPT_VERBOSE)
680					fprintf(stderr,
681						"Probe file %s len %u@%p\n",
682						t, plen, probe);
683			}
684		} else if (!strcasecmp(t, "response")) {
685			s = strtok(NULL, " \t");
686			if (!s) {
687				errtxt = line;
688				err = -1;
689				break;
690			} else if (!strcasecmp(s, "timeout")) {
691				t = strtok(NULL, " \t");
692				if (!t) {
693					errtxt = line;
694					err = -1;
695					break;
696				}
697				rtimeout = atoi(t);
698				if (opts & OPT_VERBOSE)
699					fprintf(stderr,
700						"response timeout %d\n",
701						rtimeout);
702			} else if (!strcasecmp(s, "string")) {
703				if (response) {
704					fprintf(stderr,
705						"%d: response already set\n",
706						num);
707					err = -1;
708					break;
709				}
710				response = strdup(strtok(NULL, ""));
711				rlen = strlen(response);
712				template.l4_rsize = rlen;
713				template.l4_rbuf = malloc(rlen);
714				if (opts & OPT_VERBOSE)
715					fprintf(stderr,
716						"Response string [%s]\n",
717						response);
718			} else if (!strcasecmp(s, "file")) {
719				t = strtok(NULL, " \t");
720				if (!t) {
721					errtxt = line;
722					err = -1;
723					break;
724				}
725				if (response) {
726					fprintf(stderr,
727						"%d: response already set\n",
728						num);
729					err = -1;
730					break;
731				}
732				response = mapfile(t, &rlen);
733				template.l4_rsize = rlen;
734				template.l4_rbuf = malloc(rlen);
735				if (opts & OPT_VERBOSE)
736					fprintf(stderr,
737						"Response file %s len %u@%p\n",
738						t, rlen, response);
739			}
740		} else {
741			errtxt = line;
742			err = -1;
743			break;
744		}
745	}
746
747	if (errtxt)
748		fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
749	fclose(fp);
750	return err;
751}
752
753
754void usage(prog)
755char *prog;
756{
757	fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
758	exit(1);
759}
760
761
762int main(argc, argv)
763int argc;
764char *argv[];
765{
766	char *config = NULL;
767	int c;
768
769	while ((c = getopt(argc, argv, "f:nv")) != -1)
770		switch (c)
771		{
772		case 'f' :
773			config = optarg;
774			break;
775		case 'n' :
776			opts |= OPT_DONOTHING;
777			break;
778		case 'v' :
779			opts |= OPT_VERBOSE;
780			break;
781		}
782
783	if (config == NULL)
784		usage(argv[0]);
785
786	if (readconfig(config))
787		exit(1);
788
789	if (!l4list) {
790		fprintf(stderr, "No remote servers, exiting.");
791		exit(1);
792	}
793
794	if (!(opts & OPT_DONOTHING)) {
795		natfd = open(IPL_NAT, O_RDWR);
796		if (natfd == -1) {
797			perror("open(IPL_NAT)");
798			exit(1);
799		}
800	}
801
802	if (opts & OPT_VERBOSE)
803		fprintf(stderr, "Starting...\n");
804	while (runconfig() == 0)
805		;
806}
807