etc/services - sync with NetBSD-8
[minix.git] / external / bsd / blacklist / bin / conf.c
blobf9bb5a3aca453ac77fd94f9d3ddce57afabb6ade
1 /* $NetBSD: conf.c,v 1.23 2015/06/03 15:11:40 christos Exp $ */
3 /*-
4 * Copyright (c) 2015 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: conf.c,v 1.23 2015/06/03 15:11:40 christos Exp $");
38 #include <stdio.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <inttypes.h>
42 #include <netdb.h>
43 #include <pwd.h>
44 #include <syslog.h>
45 #include <errno.h>
46 #ifdef HAVE_UTIL_H
47 #include <util.h>
48 #endif
49 #include <stdlib.h>
50 #include <limits.h>
51 #include <ifaddrs.h>
52 #include <arpa/inet.h>
53 #include <netinet/in.h>
54 #include <net/if.h>
55 #include <sys/socket.h>
57 #include "bl.h"
58 #include "internal.h"
59 #include "support.h"
60 #include "conf.h"
63 struct sockaddr_if {
64 uint8_t sif_len;
65 sa_family_t sif_family;
66 in_port_t sif_port;
67 char sif_name[16];
70 #define SIF_NAME(a) \
71 ((const struct sockaddr_if *)(const void *)(a))->sif_name
73 static int conf_is_interface(const char *);
75 #define FSTAR -1
76 #define FEQUAL -2
78 static void
79 advance(char **p)
81 char *ep = *p;
82 while (*ep && !isspace((unsigned char)*ep))
83 ep++;
84 while (*ep && isspace((unsigned char)*ep))
85 *ep++ = '\0';
86 *p = ep;
89 static int
90 getnum(const char *f, size_t l, bool local, void *rp, const char *name,
91 const char *p)
93 int e;
94 intmax_t im;
95 int *r = rp;
97 if (strcmp(p, "*") == 0) {
98 *r = FSTAR;
99 return 0;
101 if (strcmp(p, "=") == 0) {
102 if (local)
103 goto out;
104 *r = FEQUAL;
105 return 0;
108 im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
109 if (e == 0) {
110 *r = (int)im;
111 return 0;
114 if (f == NULL)
115 return -1;
116 (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
117 name, p);
118 return -1;
119 out:
120 (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
121 __func__, f, l, name);
122 return -1;
126 static int
127 getnfail(const char *f, size_t l, bool local, struct conf *c, const char *p)
129 return getnum(f, l, local, &c->c_nfail, "nfail", p);
132 static int
133 getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
135 int e;
136 char *ep;
137 intmax_t tot, im;
139 tot = 0;
140 if (strcmp(p, "*") == 0) {
141 c->c_duration = FSTAR;
142 return 0;
144 if (strcmp(p, "=") == 0) {
145 if (local)
146 goto out;
147 c->c_duration = FEQUAL;
148 return 0;
150 again:
151 im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
153 if (e == ENOTSUP) {
154 switch (*ep) {
155 case 'd':
156 im *= 24;
157 /*FALLTHROUGH*/
158 case 'h':
159 im *= 60;
160 /*FALLTHROUGH*/
161 case 'm':
162 im *= 60;
163 /*FALLTHROUGH*/
164 case 's':
165 e = 0;
166 tot += im;
167 if (ep[1] != '\0') {
168 p = ep + 2;
169 goto again;
171 break;
173 } else
174 tot = im;
176 if (e == 0) {
177 c->c_duration = (int)tot;
178 return 0;
181 if (f == NULL)
182 return -1;
183 (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
184 return -1;
185 out:
186 (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
187 " config", __func__, f, l);
188 return -1;
192 static int
193 getport(const char *f, size_t l, bool local, void *r, const char *p)
195 struct servent *sv;
197 // XXX: Pass in the proto instead
198 if ((sv = getservbyname(p, "tcp")) != NULL) {
199 *(int *)r = ntohs(sv->s_port);
200 return 0;
202 if ((sv = getservbyname(p, "udp")) != NULL) {
203 *(int *)r = ntohs(sv->s_port);
204 return 0;
207 return getnum(f, l, local, r, "service", p);
210 static int
211 getmask(const char *f, size_t l, bool local, const char **p, int *mask)
213 char *d;
214 const char *s = *p;
216 if ((d = strchr(s, ':')) != NULL) {
217 *d++ = '\0';
218 *p = d;
220 if ((d = strchr(s, '/')) == NULL) {
221 *mask = FSTAR;
222 return 0;
225 *d++ = '\0';
226 return getnum(f, l, local, mask, "mask", d);
229 static int
230 gethostport(const char *f, size_t l, bool local, struct conf *c, const char *p)
232 char *d; // XXX: Ok to write to string.
233 in_port_t *port = NULL;
234 const char *pstr;
236 if (strcmp(p, "*") == 0) {
237 c->c_port = FSTAR;
238 c->c_lmask = FSTAR;
239 return 0;
242 if ((d = strchr(p, ']')) != NULL) {
243 *d++ = '\0';
244 pstr = d;
245 p++;
246 } else
247 pstr = p;
249 if (getmask(f, l, local, &pstr, &c->c_lmask) == -1)
250 goto out;
252 if (d) {
253 struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
254 if (debug)
255 (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
256 if (strcmp(p, "*") != 0) {
257 if (inet_pton(AF_INET6, p, &sin6->sin6_addr) == -1)
258 goto out;
259 sin6->sin6_family = AF_INET6;
260 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
261 sin6->sin6_len = sizeof(*sin6);
262 #endif
263 port = &sin6->sin6_port;
265 } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
266 if (pstr == p)
267 pstr = "*";
268 struct sockaddr_in *sin = (void *)&c->c_ss;
269 struct sockaddr_if *sif = (void *)&c->c_ss;
270 if (debug)
271 (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
272 if (strcmp(p, "*") != 0) {
273 if (conf_is_interface(p)) {
274 if (!local)
275 goto out2;
276 if (debug)
277 (*lfun)(LOG_DEBUG, "%s: interface %s",
278 __func__, p);
279 if (c->c_lmask != FSTAR)
280 goto out1;
281 sif->sif_family = AF_MAX;
282 strlcpy(sif->sif_name, p,
283 sizeof(sif->sif_name));
284 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
285 sif->sif_len = sizeof(*sif);
286 #endif
287 port = &sif->sif_port;
288 } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
290 sin->sin_family = AF_INET;
291 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
292 sin->sin_len = sizeof(*sin);
293 #endif
294 port = &sin->sin_port;
295 } else
296 goto out;
300 if (getport(f, l, local, &c->c_port, pstr) == -1)
301 return -1;
303 if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
304 *port = htons((in_port_t)c->c_port);
305 return 0;
306 out:
307 (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, pstr);
308 return -1;
309 out1:
310 (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
311 "interface [%s]", __func__, f, l, c->c_lmask, p);
312 return -1;
313 out2:
314 (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
315 "with remote config [%s]", __func__, f, l, p);
316 return -1;
319 static int
320 getproto(const char *f, size_t l, bool local __unused, struct conf *c,
321 const char *p)
323 if (strcmp(p, "stream") == 0) {
324 c->c_proto = IPPROTO_TCP;
325 return 0;
327 if (strcmp(p, "dgram") == 0) {
328 c->c_proto = IPPROTO_UDP;
329 return 0;
331 return getnum(f, l, local, &c->c_proto, "protocol", p);
334 static int
335 getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
336 const char *p)
338 if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
339 c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
340 return 0;
342 return getnum(f, l, local, &c->c_family, "family", p);
345 static int
346 getuid(const char *f, size_t l, bool local __unused, struct conf *c,
347 const char *p)
349 struct passwd *pw;
351 if ((pw = getpwnam(p)) != NULL) {
352 c->c_uid = (int)pw->pw_uid;
353 return 0;
356 return getnum(f, l, local, &c->c_uid, "user", p);
360 static int
361 getname(const char *f, size_t l, bool local, struct conf *c,
362 const char *p)
364 if (getmask(f, l, local, &p, &c->c_rmask) == -1)
365 return -1;
367 if (strcmp(p, "*") == 0) {
368 strlcpy(c->c_name, rulename, CONFNAMESZ);
369 return 0;
371 if (strcmp(p, "=") == 0) {
372 if (local)
373 goto out;
374 c->c_name[0] = '\0';
375 return 0;
378 snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
379 return 0;
380 out:
381 (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
382 " config", __func__, f, l);
383 return -1;
386 static int
387 getvalue(const char *f, size_t l, bool local, void *r, char **p,
388 int (*fun)(const char *, size_t, bool, struct conf *, const char *))
390 char *ep = *p;
392 advance(p);
393 return (*fun)(f, l, local, r, ep);
397 static int
398 conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
400 int e;
402 while (*p && isspace((unsigned char)*p))
403 p++;
405 memset(c, 0, sizeof(*c));
406 e = getvalue(f, l, local, c, &p, gethostport);
407 if (e) return -1;
408 e = getvalue(f, l, local, c, &p, getproto);
409 if (e) return -1;
410 e = getvalue(f, l, local, c, &p, getfamily);
411 if (e) return -1;
412 e = getvalue(f, l, local, c, &p, getuid);
413 if (e) return -1;
414 e = getvalue(f, l, local, c, &p, getname);
415 if (e) return -1;
416 e = getvalue(f, l, local, c, &p, getnfail);
417 if (e) return -1;
418 e = getvalue(f, l, local, c, &p, getsecs);
419 if (e) return -1;
421 return 0;
424 static int
425 conf_sort(const void *v1, const void *v2)
427 const struct conf *c1 = v1;
428 const struct conf *c2 = v2;
430 #define CMP(a, b, f) \
431 if ((a)->f > (b)->f) return -1; \
432 else if ((a)->f < (b)->f) return 1
434 CMP(c1, c2, c_ss.ss_family);
435 CMP(c1, c2, c_lmask);
436 CMP(c1, c2, c_port);
437 CMP(c1, c2, c_proto);
438 CMP(c1, c2, c_family);
439 CMP(c1, c2, c_rmask);
440 CMP(c1, c2, c_uid);
441 #undef CMP
442 return 0;
445 static int
446 conf_is_interface(const char *name)
448 const struct ifaddrs *ifa;
450 for (ifa = ifas; ifa; ifa = ifa->ifa_next)
451 if (strcmp(ifa->ifa_name, name) == 0)
452 return 1;
453 return 0;
456 #define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1))
458 static int
459 conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
461 const uint32_t *a1 = v1;
462 const uint32_t *a2 = v2;
463 uint32_t m;
464 int omask = mask;
466 len >>= 2;
467 switch (mask) {
468 case FSTAR:
469 if (memcmp(v1, v2, len) == 0)
470 return 1;
471 goto out;
472 case FEQUAL:
474 (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
475 mask);
476 abort();
477 default:
478 break;
481 for (size_t i = 0; i < len; i++) {
482 if (mask > 32) {
483 m = htonl((uint32_t)~0);
484 mask -= 32;
485 } else if (mask) {
486 m = htonl(MASK(mask));
487 mask = 0;
488 } else
489 return 1;
490 if ((a1[i] & m) != (a2[i] & m))
491 goto out;
493 return 1;
494 out:
495 if (debug > 1) {
496 char b1[256], b2[256];
497 len <<= 2;
498 hexdump(b1, sizeof(b1), "a1", v1, len);
499 hexdump(b2, sizeof(b2), "a2", v2, len);
500 (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
501 b1, b2, omask);
503 return 0;
507 * Apply the mask to the given address
509 static void
510 conf_apply_mask(void *v, size_t len, int mask)
512 uint32_t *a = v;
513 uint32_t m;
515 switch (mask) {
516 case FSTAR:
517 return;
518 case FEQUAL:
519 (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
520 mask);
521 abort();
522 default:
523 break;
525 len >>= 2;
527 for (size_t i = 0; i < len; i++) {
528 if (mask > 32) {
529 m = htonl((uint32_t)~0);
530 mask -= 32;
531 } else if (mask) {
532 m = htonl(MASK(mask));
533 mask = 0;
534 } else
535 m = 0;
536 a[i] &= m;
541 * apply the mask and the port to the address given
543 static void
544 conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
546 struct sockaddr_in *sin;
547 struct sockaddr_in6 *sin6;
548 in_port_t *port;
549 void *addr;
550 size_t alen;
552 c->c_lmask = c->c_rmask;
553 c->c_ss = *ss;
555 if (c->c_ss.ss_family != c->c_family) {
556 (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
557 "%u != %u", __func__, c->c_ss.ss_family, c->c_family);
558 abort();
561 switch (c->c_ss.ss_family) {
562 case AF_INET:
563 sin = (void *)&c->c_ss;
564 port = &sin->sin_port;
565 addr = &sin->sin_addr;
566 alen = sizeof(sin->sin_addr);
567 break;
568 case AF_INET6:
569 sin6 = (void *)&c->c_ss;
570 port = &sin6->sin6_port;
571 addr = &sin6->sin6_addr;
572 alen = sizeof(sin6->sin6_addr);
573 break;
574 default:
575 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
576 __func__, c->c_ss.ss_family);
577 abort();
580 *port = htons((in_port_t)c->c_port);
581 conf_apply_mask(addr, alen, c->c_lmask);
582 if (c->c_lmask == FSTAR)
583 c->c_lmask = (int)(alen * 8);
584 if (debug) {
585 char buf[128];
586 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
587 (*lfun)(LOG_DEBUG, "Applied address %s", buf);
592 * Compared two addresses for equality applying the mask
594 static int
595 conf_inet_eq(const void *v1, const void *v2, int mask)
597 const struct sockaddr *sa1 = v1;
598 const struct sockaddr *sa2 = v2;
599 size_t size;
601 if (sa1->sa_family != sa2->sa_family)
602 return 0;
604 switch (sa1->sa_family) {
605 case AF_INET: {
606 const struct sockaddr_in *s1 = v1;
607 const struct sockaddr_in *s2 = v2;
608 size = sizeof(s1->sin_addr);
609 v1 = &s1->sin_addr;
610 v2 = &s2->sin_addr;
611 break;
614 case AF_INET6: {
615 const struct sockaddr_in6 *s1 = v1;
616 const struct sockaddr_in6 *s2 = v2;
617 size = sizeof(s1->sin6_addr);
618 v1 = &s1->sin6_addr;
619 v2 = &s2->sin6_addr;
620 break;
623 default:
624 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
625 __func__, sa1->sa_family);
626 abort();
629 return conf_amask_eq(v1, v2, size, mask);
632 static int
633 conf_addr_in_interface(const struct sockaddr_storage *s1,
634 const struct sockaddr_storage *s2, int mask)
636 const char *name = SIF_NAME(s2);
637 const struct ifaddrs *ifa;
639 for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
640 if ((ifa->ifa_flags & IFF_UP) == 0)
641 continue;
643 if (strcmp(ifa->ifa_name, name) != 0)
644 continue;
646 if (s1->ss_family != ifa->ifa_addr->sa_family)
647 continue;
649 bool eq;
650 switch (s1->ss_family) {
651 case AF_INET:
652 case AF_INET6:
653 eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
654 break;
655 default:
656 (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
657 continue;
659 if (eq)
660 return 1;
662 return 0;
665 static int
666 conf_addr_eq(const struct sockaddr_storage *s1,
667 const struct sockaddr_storage *s2, int mask)
669 switch (s2->ss_family) {
670 case 0:
671 return 1;
672 case AF_MAX:
673 return conf_addr_in_interface(s1, s2, mask);
674 case AF_INET:
675 case AF_INET6:
676 return conf_inet_eq(s1, s2, mask);
677 default:
678 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
679 __func__, s1->ss_family);
680 abort();
684 static int
685 conf_eq(const struct conf *c1, const struct conf *c2)
688 if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
689 return 0;
691 #define CMP(a, b, f) \
692 if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
693 if (debug > 1) \
694 (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
695 __STRING(f), (a)->f, (b)->f); \
696 return 0; \
698 CMP(c1, c2, c_port);
699 CMP(c1, c2, c_proto);
700 CMP(c1, c2, c_family);
701 CMP(c1, c2, c_uid);
702 #undef CMP
703 return 1;
706 static const char *
707 conf_num(char *b, size_t l, int n)
709 switch (n) {
710 case FSTAR:
711 return "*";
712 case FEQUAL:
713 return "=";
714 default:
715 snprintf(b, l, "%d", n);
716 return b;
720 static const char *
721 fmtname(const char *n) {
722 size_t l = strlen(rulename);
723 if (l == 0)
724 return "*";
725 if (strncmp(n, rulename, l) == 0) {
726 if (n[l] != '\0')
727 return n + l;
728 else
729 return "*";
730 } else if (!*n)
731 return "=";
732 else
733 return n;
736 static void
737 fmtport(char *b, size_t l, int port)
739 char buf[128];
741 if (port == FSTAR)
742 return;
744 if (b[0] == '\0' || strcmp(b, "*") == 0)
745 snprintf(b, l, "%d", port);
746 else {
747 snprintf(buf, sizeof(buf), ":%d", port);
748 strlcat(b, buf, l);
752 static const char *
753 fmtmask(char *b, size_t l, int fam, int mask)
755 char buf[128];
757 switch (mask) {
758 case FSTAR:
759 return "";
760 case FEQUAL:
761 if (strcmp(b, "=") == 0)
762 return "";
763 else {
764 strlcat(b, "/=", l);
765 return b;
767 default:
768 break;
771 switch (fam) {
772 case AF_INET:
773 if (mask == 32)
774 return "";
775 break;
776 case AF_INET6:
777 if (mask == 128)
778 return "";
779 break;
780 default:
781 break;
784 snprintf(buf, sizeof(buf), "/%d", mask);
785 strlcat(b, buf, l);
786 return b;
789 static const char *
790 conf_namemask(char *b, size_t l, const struct conf *c)
792 strlcpy(b, fmtname(c->c_name), l);
793 fmtmask(b, l, c->c_family, c->c_rmask);
794 return b;
797 const char *
798 conf_print(char *buf, size_t len, const char *pref, const char *delim,
799 const struct conf *c)
801 char ha[128], hb[32], b[5][64];
802 int sp;
804 #define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
806 switch (c->c_ss.ss_family) {
807 case 0:
808 snprintf(ha, sizeof(ha), "*");
809 break;
810 case AF_MAX:
811 snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
812 break;
813 default:
814 sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
815 break;
818 fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
819 fmtport(ha, sizeof(ha), c->c_port);
821 sp = *delim == '\t' ? 20 : -1;
822 hb[0] = '\0';
823 if (*delim)
824 snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
825 "%s%s" "%s%s%s",
826 pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
827 N(1, c->c_family), delim, N(2, c->c_uid), delim,
828 conf_namemask(hb, sizeof(hb), c), delim,
829 N(3, c->c_nfail), delim, N(4, c->c_duration));
830 else
831 snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
832 "uid:%s, name:%s, nfail:%s, duration:%s", pref,
833 ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
834 conf_namemask(hb, sizeof(hb), c),
835 N(3, c->c_nfail), N(4, c->c_duration));
836 return buf;
840 * Apply the local config match to the result
842 static void
843 conf_apply(struct conf *c, const struct conf *sc)
845 char buf[BUFSIZ];
847 if (debug) {
848 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
849 conf_print(buf, sizeof(buf), "merge:\t", "", sc));
850 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
851 conf_print(buf, sizeof(buf), "to:\t", "", c));
853 memcpy(c->c_name, sc->c_name, CONFNAMESZ);
854 c->c_uid = sc->c_uid;
855 c->c_rmask = sc->c_rmask;
856 c->c_nfail = sc->c_nfail;
857 c->c_duration = sc->c_duration;
859 if (debug)
860 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
861 conf_print(buf, sizeof(buf), "result:\t", "", c));
865 * Merge a remote configuration to the result
867 static void
868 conf_merge(struct conf *c, const struct conf *sc)
870 char buf[BUFSIZ];
872 if (debug) {
873 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
874 conf_print(buf, sizeof(buf), "merge:\t", "", sc));
875 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
876 conf_print(buf, sizeof(buf), "to:\t", "", c));
879 if (sc->c_name[0])
880 memcpy(c->c_name, sc->c_name, CONFNAMESZ);
881 if (sc->c_uid != FEQUAL)
882 c->c_uid = sc->c_uid;
883 if (sc->c_rmask != FEQUAL)
884 c->c_lmask = c->c_rmask = sc->c_rmask;
885 if (sc->c_nfail != FEQUAL)
886 c->c_nfail = sc->c_nfail;
887 if (sc->c_duration != FEQUAL)
888 c->c_duration = sc->c_duration;
889 if (debug)
890 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
891 conf_print(buf, sizeof(buf), "result:\t", "", c));
894 static void
895 confset_init(struct confset *cs)
897 cs->cs_c = NULL;
898 cs->cs_n = 0;
899 cs->cs_m = 0;
902 static int
903 confset_grow(struct confset *cs)
905 void *tc;
907 cs->cs_m += 10;
908 tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
909 if (tc == NULL) {
910 (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
911 return -1;
913 cs->cs_c = tc;
914 return 0;
917 static struct conf *
918 confset_get(struct confset *cs)
920 return &cs->cs_c[cs->cs_n];
923 static bool
924 confset_full(const struct confset *cs)
926 return cs->cs_n == cs->cs_m;
929 static void
930 confset_sort(struct confset *cs)
932 qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
935 static void
936 confset_add(struct confset *cs)
938 cs->cs_n++;
941 static void
942 confset_free(struct confset *cs)
944 free(cs->cs_c);
945 confset_init(cs);
948 static void
949 confset_replace(struct confset *dc, struct confset *sc)
951 struct confset tc;
952 tc = *dc;
953 *dc = *sc;
954 confset_init(sc);
955 confset_free(&tc);
958 static void
959 confset_list(const struct confset *cs, const char *msg, const char *where)
961 char buf[BUFSIZ];
963 (*lfun)(LOG_DEBUG, "[%s]", msg);
964 (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
965 where);
966 for (size_t i = 0; i < cs->cs_n; i++)
967 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
968 &cs->cs_c[i]));
972 * Match a configuration against the given list and apply the function
973 * to it, returning the matched entry number.
975 static size_t
976 confset_match(const struct confset *cs, struct conf *c,
977 void (*fun)(struct conf *, const struct conf *))
979 char buf[BUFSIZ];
980 size_t i;
982 for (i = 0; i < cs->cs_n; i++) {
983 if (debug)
984 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
985 "check:\t", "", &cs->cs_c[i]));
986 if (conf_eq(c, &cs->cs_c[i])) {
987 if (debug)
988 (*lfun)(LOG_DEBUG, "%s",
989 conf_print(buf, sizeof(buf),
990 "found:\t", "", &cs->cs_c[i]));
991 (*fun)(c, &cs->cs_c[i]);
992 break;
995 return i;
998 const struct conf *
999 conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
1000 struct conf *cr)
1002 int proto;
1003 socklen_t slen;
1004 struct sockaddr_storage lss;
1005 size_t i;
1006 char buf[BUFSIZ];
1008 memset(cr, 0, sizeof(*cr));
1009 slen = sizeof(lss);
1010 memset(&lss, 0, slen);
1011 if (getsockname(fd, (void *)&lss, &slen) == -1) {
1012 (*lfun)(LOG_ERR, "getsockname failed (%m)");
1013 return NULL;
1016 slen = sizeof(proto);
1017 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
1018 (*lfun)(LOG_ERR, "getsockopt failed (%m)");
1019 return NULL;
1022 if (debug) {
1023 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&lss);
1024 (*lfun)(LOG_DEBUG, "listening socket: %s", buf);
1027 switch (proto) {
1028 case SOCK_STREAM:
1029 cr->c_proto = IPPROTO_TCP;
1030 break;
1031 case SOCK_DGRAM:
1032 cr->c_proto = IPPROTO_UDP;
1033 break;
1034 default:
1035 (*lfun)(LOG_ERR, "unsupported protocol %d", proto);
1036 return NULL;
1039 switch (lss.ss_family) {
1040 case AF_INET:
1041 cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
1042 break;
1043 case AF_INET6:
1044 cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
1045 break;
1046 default:
1047 (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
1048 return NULL;
1051 cr->c_ss = lss;
1052 cr->c_lmask = FSTAR;
1053 cr->c_uid = (int)uid;
1054 cr->c_family = lss.ss_family;
1055 cr->c_name[0] = '\0';
1056 cr->c_rmask = FSTAR;
1057 cr->c_nfail = FSTAR;
1058 cr->c_duration = FSTAR;
1060 if (debug)
1061 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
1062 "look:\t", "", cr));
1064 /* match the local config */
1065 i = confset_match(&lconf, cr, conf_apply);
1066 if (i == lconf.cs_n) {
1067 if (debug)
1068 (*lfun)(LOG_DEBUG, "not found");
1069 return NULL;
1072 conf_addr_set(cr, rss);
1073 /* match the remote config */
1074 confset_match(&rconf, cr, conf_merge);
1075 /* to apply the mask */
1076 conf_addr_set(cr, &cr->c_ss);
1078 return cr;
1082 void
1083 conf_parse(const char *f)
1085 FILE *fp;
1086 char *line;
1087 size_t lineno, len;
1088 struct confset lc, rc, *cs;
1090 if ((fp = fopen(f, "r")) == NULL) {
1091 (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f);
1092 return;
1095 lineno = 1;
1097 confset_init(&rc);
1098 confset_init(&lc);
1099 cs = &lc;
1100 for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
1101 free(line))
1103 if (!*line)
1104 continue;
1105 if (strcmp(line, "[local]") == 0) {
1106 cs = &lc;
1107 continue;
1109 if (strcmp(line, "[remote]") == 0) {
1110 cs = &rc;
1111 continue;
1114 if (confset_full(cs)) {
1115 if (confset_grow(cs) == -1) {
1116 confset_free(&lc);
1117 confset_free(&rc);
1118 fclose(fp);
1119 return;
1122 if (conf_parseline(f, lineno, line, confset_get(cs),
1123 cs == &lc) == -1)
1124 continue;
1125 confset_add(cs);
1128 fclose(fp);
1129 confset_sort(&lc);
1130 confset_sort(&rc);
1132 confset_replace(&rconf, &rc);
1133 confset_replace(&lconf, &lc);
1135 if (debug) {
1136 confset_list(&lconf, "local", "target");
1137 confset_list(&rconf, "remote", "source");