1
2 /*
3 * (C)Copyright (C) 2012 by Darren Reed.
4 */
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <sys/mman.h>
8 #include <sys/socket.h>
9 #include <sys/time.h>
10 #include <sys/ioctl.h>
11
12 #include <netinet/in.h>
13 #include <netinet/in_systm.h>
14 #include <netinet/ip.h>
15
16 #include <net/if.h>
17
18 #include <stdio.h>
19 #include <netdb.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <stdlib.h>
25
26 #include "ip_compat.h"
27 #include "ip_fil.h"
28 #include "ip_nat.h"
29
30 #include "ipf.h"
31
32 extern char *optarg;
33
34
35 typedef struct l4cfg {
36 struct l4cfg *l4_next;
37 struct ipnat l4_nat; /* NAT rule */
38 struct sockaddr_in l4_sin; /* remote socket to connect */
39 time_t l4_last; /* when we last connected */
40 int l4_alive; /* 1 = remote alive */
41 int l4_fd;
42 int l4_rw; /* 0 = reading, 1 = writing */
43 char *l4_rbuf; /* read buffer */
44 int l4_rsize; /* size of buffer */
45 int l4_rlen; /* how much used */
46 char *l4_wptr; /* next byte to write */
47 int l4_wlen; /* length yet to be written */
48 } l4cfg_t;
49
50
51 l4cfg_t *l4list = NULL;
52 char *response = NULL;
53 char *probe = NULL;
54 l4cfg_t template;
55 int frequency = 20;
56 int ctimeout = 1;
57 int rtimeout = 1;
58 size_t plen = 0;
59 size_t rlen = 0;
60 int natfd = -1;
61 int opts = 0;
62
63 #if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
64 # define strerror(x) sys_errlist[x]
65 #endif
66
67
68 char *
copystr(char * dst,char * src)69 copystr(char *dst, char *src)
70 {
71 register char *s, *t, c;
72 register int esc = 0;
73
74 for (s = src, t = dst; s && t && (c = *s++); )
75 if (esc) {
76 esc = 0;
77 switch (c)
78 {
79 case 'n' :
80 *t++ = '\n';
81 break;
82 case 'r' :
83 *t++ = '\r';
84 break;
85 case 't' :
86 *t++ = '\t';
87 break;
88 }
89 } else if (c != '\\')
90 *t++ = c;
91 else
92 esc = 1;
93 *t = '\0';
94 return(dst);
95 }
96
97 void
addnat(l4cfg_t * l4)98 addnat(l4cfg_t *l4)
99 {
100 ipnat_t *ipn = &l4->l4_nat;
101
102 printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]),
103 ipn->in_outmsk, ntohs(ipn->in_pmin));
104 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext));
105 if (!(opts & OPT_DONOTHING)) {
106 if (ioctl(natfd, SIOCADNAT, &ipn) == -1)
107 perror("ioctl(SIOCADNAT)");
108 }
109 }
110
111
112 void
delnat(l4cfg_t * l4)113 delnat(l4cfg_t *l4)
114 {
115 ipnat_t *ipn = &l4->l4_nat;
116
117 printf("Remove NAT rule for %s/%#x,%u -> ",
118 inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin);
119 printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext);
120 if (!(opts & OPT_DONOTHING)) {
121 if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
122 perror("ioctl(SIOCRMNAT)");
123 }
124 }
125
126
127 void
connectl4(l4cfg_t * l4)128 connectl4(l4cfg_t *l4)
129 {
130 l4->l4_rw = 1;
131 l4->l4_rlen = 0;
132 l4->l4_wlen = plen;
133 if (!l4->l4_wlen) {
134 l4->l4_alive = 1;
135 addnat(l4);
136 } else
137 l4->l4_wptr = probe;
138 }
139
140
141 void
closel4(l4cfg_t * l4,int dead)142 closel4(l4cfg_t *l4, int 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
154 void
connectfd(l4cfg_t * l4)155 connectfd(l4cfg_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
176 void
writefd(l4cfg_t * l4)177 writefd(l4cfg_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
readfd(l4cfg_t * l4)208 void readfd(l4cfg_t *l4)
209 {
210 char buf[80], *ptr;
211 int n, i, fd;
212
213 fd = l4->l4_fd;
214
215 if (l4->l4_rw == -2) {
216 connectfd(l4);
217 return;
218 }
219
220 if (l4->l4_rsize) {
221 n = l4->l4_rsize - l4->l4_rlen;
222 ptr = l4->l4_rbuf + l4->l4_rlen;
223 } else {
224 n = sizeof(buf) - 1;
225 ptr = buf;
226 }
227
228 if (opts & OPT_VERBOSE)
229 fprintf(stderr, "Read %d bytes on fd %d to %p\n",
230 n, fd, ptr);
231 i = recv(fd, ptr, n, 0);
232 if (i == 0 || i == -1) {
233 if (opts & OPT_VERBOSE)
234 fprintf(stderr, "Read error on fd %d: %s\n",
235 fd, (i == 0) ? "EOF" : strerror(errno));
236 closel4(l4, 1);
237 } else {
238 if (ptr == buf)
239 ptr[i] = '\0';
240 if (opts & OPT_VERBOSE)
241 fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
242 fd, i, i, i, ptr);
243 if (ptr != buf) {
244 l4->l4_rlen += i;
245 if (l4->l4_rlen >= l4->l4_rsize) {
246 if (!strncmp(response, l4->l4_rbuf,
247 l4->l4_rsize)) {
248 printf("%d: Good response\n",
249 fd);
250 if (!l4->l4_alive) {
251 l4->l4_alive = 1;
252 addnat(l4);
253 }
254 closel4(l4, 0);
255 } else {
256 if (opts & OPT_VERBOSE)
257 printf("%d: Bad response\n",
258 fd);
259 closel4(l4, 1);
260 }
261 }
262 } else if (!l4->l4_alive) {
263 l4->l4_alive = 1;
264 addnat(l4);
265 closel4(l4, 0);
266 }
267 }
268 }
269
270
271 int
runconfig(void)272 runconfig(void)
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
402 int
gethostport(char * str,int lnum,u_32_t * ipp,u_short * portp)403 gethostport(char *str, int lnum, u_32_t *ipp, u_short *portp)
404 {
405 struct servent *sp;
406 struct hostent *hp;
407 char *host, *port;
408 struct in_addr ip;
409
410 host = str;
411 port = strchr(host, ',');
412 if (port)
413 *port++ = '\0';
414
415 #ifdef HAVE_INET_ATON
416 if (ISDIGIT(*host) && inet_aton(host, &ip))
417 *ipp = ip.s_addr;
418 #else
419 if (ISDIGIT(*host))
420 *ipp = inet_addr(host);
421 #endif
422 else {
423 if (!(hp = gethostbyname(host))) {
424 fprintf(stderr, "%d: can't resolve hostname: %s\n",
425 lnum, host);
426 return(0);
427 }
428 *ipp = *(u_32_t *)hp->h_addr;
429 }
430
431 if (port) {
432 if (ISDIGIT(*port))
433 *portp = htons(atoi(port));
434 else {
435 sp = getservbyname(port, "tcp");
436 if (sp)
437 *portp = sp->s_port;
438 else {
439 fprintf(stderr, "%d: unknown service %s\n",
440 lnum, port);
441 return(0);
442 }
443 }
444 } else
445 *portp = 0;
446 return(1);
447 }
448
449
450 char *
mapfile(char * file,size_t * sizep)451 mapfile(char *file, size_t *sizep)
452 {
453 struct stat sb;
454 caddr_t addr;
455 int fd;
456
457 fd = open(file, O_RDONLY);
458 if (fd == -1) {
459 perror("open(mapfile)");
460 return(NULL);
461 }
462
463 if (fstat(fd, &sb) == -1) {
464 perror("fstat(mapfile)");
465 close(fd);
466 return(NULL);
467 }
468
469 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
470 if (addr == (caddr_t)-1) {
471 perror("mmap(mapfile)");
472 close(fd);
473 return(NULL);
474 }
475 close(fd);
476 *sizep = sb.st_size;
477 return(char *)addr;
478 }
479
480
481 int
readconfig(char * filename)482 readconfig(char *filename)
483 {
484 char c, buf[512], *s, *t, *errtxt = NULL, *line;
485 int num, err = 0;
486 ipnat_t *ipn;
487 l4cfg_t *l4;
488 FILE *fp;
489
490 fp = fopen(filename, "r");
491 if (!fp) {
492 perror("open(configfile)");
493 return(-1);
494 }
495
496 bzero((char *)&template, sizeof(template));
497 template.l4_fd = -1;
498 template.l4_rw = -1;
499 template.l4_sin.sin_family = AF_INET;
500 ipn = &template.l4_nat;
501 ipn->in_flags = IPN_TCP|IPN_ROUNDR;
502 ipn->in_redir = NAT_REDIRECT;
503
504 for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
505 s = strchr(buf, '\n');
506 if (!s) {
507 fprintf(stderr, "%d: line too long\n", num);
508 fclose(fp);
509 return(-1);
510 }
511
512 *s = '\0';
513
514 /*
515 * lines which are comments
516 */
517 s = strchr(buf, '#');
518 if (s)
519 *s = '\0';
520
521 /*
522 * Skip leading whitespace
523 */
524 for (line = buf; (c = *line) && ISSPACE(c); line++)
525 ;
526 if (!*line)
527 continue;
528
529 if (opts & OPT_VERBOSE)
530 fprintf(stderr, "Parsing: [%s]\n", line);
531 t = strtok(line, " \t");
532 if (!t)
533 continue;
534 if (!strcasecmp(t, "interface")) {
535 s = strtok(NULL, " \t");
536 if (s)
537 t = strtok(NULL, "\t");
538 if (!s || !t) {
539 errtxt = line;
540 err = -1;
541 break;
542 }
543
544 if (!strchr(t, ',')) {
545 fprintf(stderr,
546 "%d: local address,port missing\n",
547 num);
548 err = -1;
549 break;
550 }
551
552 strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname));
553 if (!gethostport(t, num, &ipn->in_outip,
554 &ipn->in_pmin)) {
555 errtxt = line;
556 err = -1;
557 break;
558 }
559 ipn->in_outmsk = 0xffffffff;
560 ipn->in_pmax = ipn->in_pmin;
561 if (opts & OPT_VERBOSE)
562 fprintf(stderr,
563 "Interface %s %s/%#x port %u\n",
564 ipn->in_ifname,
565 inet_ntoa(ipn->in_out[0]),
566 ipn->in_outmsk, ipn->in_pmin);
567 } else if (!strcasecmp(t, "remote")) {
568 if (!*ipn->in_ifname) {
569 fprintf(stderr,
570 "%d: ifname not set prior to remote\n",
571 num);
572 err = -1;
573 break;
574 }
575 s = strtok(NULL, " \t");
576 if (s)
577 t = strtok(NULL, "");
578 if (!s || !t || strcasecmp(s, "server")) {
579 errtxt = line;
580 err = -1;
581 break;
582 }
583
584 ipn->in_pnext = 0;
585 if (!gethostport(t, num, &ipn->in_inip,
586 &ipn->in_pnext)) {
587 errtxt = line;
588 err = -1;
589 break;
590 }
591 ipn->in_inmsk = 0xffffffff;
592 if (ipn->in_pnext == 0)
593 ipn->in_pnext = ipn->in_pmin;
594
595 l4 = (l4cfg_t *)malloc(sizeof(*l4));
596 if (!l4) {
597 fprintf(stderr, "%d: out of memory (%d)\n",
598 num, sizeof(*l4));
599 err = -1;
600 break;
601 }
602 bcopy((char *)&template, (char *)l4, sizeof(*l4));
603 l4->l4_sin.sin_addr = ipn->in_in[0];
604 l4->l4_sin.sin_port = ipn->in_pnext;
605 l4->l4_next = l4list;
606 l4list = l4;
607 } else if (!strcasecmp(t, "connect")) {
608 s = strtok(NULL, " \t");
609 if (s)
610 t = strtok(NULL, "\t");
611 if (!s || !t) {
612 errtxt = line;
613 err = -1;
614 break;
615 } else if (!strcasecmp(s, "timeout")) {
616 ctimeout = atoi(t);
617 if (opts & OPT_VERBOSE)
618 fprintf(stderr, "connect timeout %d\n",
619 ctimeout);
620 } else if (!strcasecmp(s, "frequency")) {
621 frequency = atoi(t);
622 if (opts & OPT_VERBOSE)
623 fprintf(stderr,
624 "connect frequency %d\n",
625 frequency);
626 } else {
627 errtxt = line;
628 err = -1;
629 break;
630 }
631 } else if (!strcasecmp(t, "probe")) {
632 s = strtok(NULL, " \t");
633 if (!s) {
634 errtxt = line;
635 err = -1;
636 break;
637 } else if (!strcasecmp(s, "string")) {
638 if (probe) {
639 fprintf(stderr,
640 "%d: probe already set\n",
641 num);
642 err = -1;
643 break;
644 }
645 t = strtok(NULL, "");
646 if (!t) {
647 fprintf(stderr,
648 "%d: No probe string\n", num);
649 err = -1;
650 break;
651 }
652
653 probe = malloc(strlen(t));
654 copystr(probe, t);
655 plen = strlen(probe);
656 if (opts & OPT_VERBOSE)
657 fprintf(stderr, "Probe string [%s]\n",
658 probe);
659 } else if (!strcasecmp(s, "file")) {
660 t = strtok(NULL, " \t");
661 if (!t) {
662 errtxt = line;
663 err = -1;
664 break;
665 }
666 if (probe) {
667 fprintf(stderr,
668 "%d: probe already set\n",
669 num);
670 err = -1;
671 break;
672 }
673 probe = mapfile(t, &plen);
674 if (opts & OPT_VERBOSE)
675 fprintf(stderr,
676 "Probe file %s len %u@%p\n",
677 t, plen, probe);
678 }
679 } else if (!strcasecmp(t, "response")) {
680 s = strtok(NULL, " \t");
681 if (!s) {
682 errtxt = line;
683 err = -1;
684 break;
685 } else if (!strcasecmp(s, "timeout")) {
686 t = strtok(NULL, " \t");
687 if (!t) {
688 errtxt = line;
689 err = -1;
690 break;
691 }
692 rtimeout = atoi(t);
693 if (opts & OPT_VERBOSE)
694 fprintf(stderr,
695 "response timeout %d\n",
696 rtimeout);
697 } else if (!strcasecmp(s, "string")) {
698 if (response) {
699 fprintf(stderr,
700 "%d: response already set\n",
701 num);
702 err = -1;
703 break;
704 }
705 response = strdup(strtok(NULL, ""));
706 rlen = strlen(response);
707 template.l4_rsize = rlen;
708 template.l4_rbuf = malloc(rlen);
709 if (opts & OPT_VERBOSE)
710 fprintf(stderr,
711 "Response string [%s]\n",
712 response);
713 } else if (!strcasecmp(s, "file")) {
714 t = strtok(NULL, " \t");
715 if (!t) {
716 errtxt = line;
717 err = -1;
718 break;
719 }
720 if (response) {
721 fprintf(stderr,
722 "%d: response already set\n",
723 num);
724 err = -1;
725 break;
726 }
727 response = mapfile(t, &rlen);
728 template.l4_rsize = rlen;
729 template.l4_rbuf = malloc(rlen);
730 if (opts & OPT_VERBOSE)
731 fprintf(stderr,
732 "Response file %s len %u@%p\n",
733 t, rlen, response);
734 }
735 } else {
736 errtxt = line;
737 err = -1;
738 break;
739 }
740 }
741
742 if (errtxt)
743 fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
744 fclose(fp);
745 return(err);
746 }
747
748
749 void
usage(char * prog)750 usage(char *prog)
751 {
752 fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
753 exit(1);
754 }
755
756
757 int
main(int argc,char * argv[])758 main(int argc, char *argv[])
759 {
760 char *config = NULL;
761 int c;
762
763 while ((c = getopt(argc, argv, "f:nv")) != -1)
764 switch (c)
765 {
766 case 'f' :
767 config = optarg;
768 break;
769 case 'n' :
770 opts |= OPT_DONOTHING;
771 break;
772 case 'v' :
773 opts |= OPT_VERBOSE;
774 break;
775 }
776
777 if (config == NULL)
778 usage(argv[0]);
779
780 if (readconfig(config))
781 exit(1);
782
783 if (!l4list) {
784 fprintf(stderr, "No remote servers, exiting.");
785 exit(1);
786 }
787
788 if (!(opts & OPT_DONOTHING)) {
789 natfd = open(IPL_NAT, O_RDWR);
790 if (natfd == -1) {
791 perror("open(IPL_NAT)");
792 exit(1);
793 }
794 }
795
796 if (opts & OPT_VERBOSE)
797 fprintf(stderr, "Starting...\n");
798 while (runconfig() == 0)
799 ;
800 }
801