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