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