Home | History | Annotate | Line # | Download | only in bin
conf.c revision 1.7
      1 /*	$NetBSD: conf.c,v 1.7 2025/02/05 20:09:33 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Christos Zoulas.
      9  *
     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.
     18  *
     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.
     30  */
     31 #ifdef HAVE_CONFIG_H
     32 #include "config.h"
     33 #endif
     34 
     35 #include <sys/cdefs.h>
     36 __RCSID("$NetBSD: conf.c,v 1.7 2025/02/05 20:09:33 christos Exp $");
     37 
     38 #include <stdio.h>
     39 #ifdef HAVE_LIBUTIL_H
     40 #include <libutil.h>
     41 #endif
     42 #ifdef HAVE_UTIL_H
     43 #include <util.h>
     44 #endif
     45 #include <string.h>
     46 #include <ctype.h>
     47 #include <inttypes.h>
     48 #include <netdb.h>
     49 #include <unistd.h>
     50 #include <pwd.h>
     51 #include <syslog.h>
     52 #include <errno.h>
     53 #include <stdlib.h>
     54 #include <limits.h>
     55 #include <ifaddrs.h>
     56 #include <arpa/inet.h>
     57 #include <netinet/in.h>
     58 #include <net/if.h>
     59 #include <net/route.h>
     60 #include <sys/socket.h>
     61 
     62 #include "bl.h"
     63 #include "internal.h"
     64 #include "support.h"
     65 #include "conf.h"
     66 
     67 
     68 struct sockaddr_if {
     69 	uint8_t		sif_len;
     70 	sa_family_t	sif_family;
     71 	in_port_t	sif_port;
     72 	char		sif_name[16];
     73 };
     74 
     75 #define SIF_NAME(a) \
     76     ((const struct sockaddr_if *)(const void *)(a))->sif_name
     77 
     78 static int conf_is_interface(const char *);
     79 
     80 #define FSTAR	-1
     81 #define FEQUAL	-2
     82 
     83 static void
     84 advance(char **p)
     85 {
     86 	char *ep = *p;
     87 	while (*ep && !isspace((unsigned char)*ep))
     88 		ep++;
     89 	while (*ep && isspace((unsigned char)*ep))
     90 		*ep++ = '\0';
     91 	*p = ep;
     92 }
     93 
     94 static int
     95 conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name,
     96     const char *p)
     97 {
     98 	int e;
     99 	intmax_t im;
    100 	int *r = rp;
    101 
    102 	if (strcmp(p, "*") == 0) {
    103 		*r = FSTAR;
    104 		return 0;
    105 	}
    106 	if (strcmp(p, "=") == 0) {
    107 		if (local)
    108 			goto out;
    109 		*r = FEQUAL;
    110 		return 0;
    111 	}
    112 
    113 	im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
    114 	if (e == 0) {
    115 		*r = (int)im;
    116 		return 0;
    117 	}
    118 
    119 	if (f == NULL)
    120 		return -1;
    121 	(*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
    122 	   name,  p);
    123 	return -1;
    124 out:
    125 	(*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
    126 	    __func__, f, l, name);
    127 	return -1;
    128 
    129 }
    130 
    131 static int
    132 conf_getnfail(const char *f, size_t l, bool local, struct conf *c,
    133     const char *p)
    134 {
    135 	return conf_getnum(f, l, local, &c->c_nfail, "nfail", p);
    136 }
    137 
    138 static int
    139 conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
    140 {
    141 	int e;
    142 	char *ep;
    143 	intmax_t tot, im;
    144 
    145 	tot = 0;
    146 	if (strcmp(p, "*") == 0) {
    147 		c->c_duration = FSTAR;
    148 		return 0;
    149 	}
    150 	if (strcmp(p, "=") == 0) {
    151 		if (local)
    152 			goto out;
    153 		c->c_duration = FEQUAL;
    154 		return 0;
    155 	}
    156 again:
    157 	im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
    158 
    159 	if (e == ENOTSUP) {
    160 		switch (*ep) {
    161 		case 'd':
    162 			im *= 24;
    163 			/*FALLTHROUGH*/
    164 		case 'h':
    165 			im *= 60;
    166 			/*FALLTHROUGH*/
    167 		case 'm':
    168 			im *= 60;
    169 			/*FALLTHROUGH*/
    170 		case 's':
    171 			e = 0;
    172 			tot += im;
    173 			if (ep[1] != '\0') {
    174 				p = ep + 2;
    175 				goto again;
    176 			}
    177 			break;
    178 		}
    179 	} else
    180 		tot = im;
    181 
    182 	if (e == 0) {
    183 		c->c_duration = (int)tot;
    184 		return 0;
    185 	}
    186 
    187 	if (f == NULL)
    188 		return -1;
    189 	(*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
    190 	return -1;
    191 out:
    192 	(*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
    193 	    " config", __func__, f, l);
    194 	return -1;
    195 
    196 }
    197 
    198 static int
    199 conf_getport(const char *f, size_t l, bool local, void *r, const char *p)
    200 {
    201 	struct servent *sv;
    202 
    203 	// XXX: Pass in the proto instead
    204 	if ((sv = getservbyname(p, "tcp")) != NULL) {
    205 		*(int *)r = ntohs(sv->s_port);
    206 		return 0;
    207 	}
    208 	if ((sv = getservbyname(p, "udp")) != NULL) {
    209 		*(int *)r = ntohs(sv->s_port);
    210 		return 0;
    211 	}
    212 
    213 	return conf_getnum(f, l, local, r, "service", p);
    214 }
    215 
    216 static int
    217 conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask)
    218 {
    219 	char *d;
    220 	const char *s = *p;
    221 
    222 	if ((d = strchr(s, ':')) != NULL) {
    223 		*d++ = '\0';
    224 		*p = d;
    225 	}
    226 	if ((d = strchr(s, '/')) == NULL) {
    227 		*mask = FSTAR;
    228 		return 0;
    229 	}
    230 
    231 	*d++ = '\0';
    232 	return conf_getnum(f, l, local, mask, "mask", d);
    233 }
    234 
    235 static int
    236 conf_gethostport(const char *f, size_t l, bool local, struct conf *c,
    237     const char *p)
    238 {
    239 	char *d;	// XXX: Ok to write to string.
    240 	in_port_t *port = NULL;
    241 	const char *pstr;
    242 
    243 	if (strcmp(p, "*") == 0) {
    244 		c->c_port = FSTAR;
    245 		c->c_lmask = FSTAR;
    246 		return 0;
    247 	}
    248 
    249 	if ((d = strchr(p, ']')) != NULL) {
    250 		*d++ = '\0';
    251 		pstr = d;
    252 		p++;
    253 	} else
    254 		pstr = p;
    255 
    256 	if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1)
    257 		goto out;
    258 
    259 	if (d) {
    260 		struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
    261 		if (debug)
    262 			(*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
    263 		if (strcmp(p, "*") != 0) {
    264 			if (inet_pton(AF_INET6, p, &sin6->sin6_addr) != 1)
    265 				goto out;
    266 			sin6->sin6_family = AF_INET6;
    267 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
    268 			sin6->sin6_len = sizeof(*sin6);
    269 #endif
    270 			port = &sin6->sin6_port;
    271 		}
    272 		if (!*pstr)
    273 			pstr = "*";
    274 	} else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
    275 		if (pstr == p)
    276 			pstr = "*";
    277 		struct sockaddr_in *sin = (void *)&c->c_ss;
    278 		struct sockaddr_if *sif = (void *)&c->c_ss;
    279 		if (debug)
    280 			(*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
    281 		if (strcmp(p, "*") != 0) {
    282 			if (conf_is_interface(p)) {
    283 				if (!local)
    284 					goto out2;
    285 				if (debug)
    286 					(*lfun)(LOG_DEBUG, "%s: interface %s",
    287 					    __func__, p);
    288 				if (c->c_lmask != FSTAR)
    289 					goto out1;
    290 				sif->sif_family = AF_MAX;
    291 				strlcpy(sif->sif_name, p,
    292 				    sizeof(sif->sif_name));
    293 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
    294 				sif->sif_len = sizeof(*sif);
    295 #endif
    296 				port = &sif->sif_port;
    297 			} else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
    298 			{
    299 				sin->sin_family = AF_INET;
    300 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
    301 				sin->sin_len = sizeof(*sin);
    302 #endif
    303 				port = &sin->sin_port;
    304 			} else
    305 				goto out;
    306 		}
    307 	}
    308 
    309 	if (conf_getport(f, l, local, &c->c_port, pstr) == -1)
    310 		return -1;
    311 
    312 	if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
    313 		*port = htons((in_port_t)c->c_port);
    314 	return 0;
    315 out:
    316 	(*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, p);
    317 	return -1;
    318 out1:
    319 	(*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
    320 	    "interface [%s]", __func__, f, l, c->c_lmask, p);
    321 	return -1;
    322 out2:
    323 	(*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
    324 	    "with remote config [%s]", __func__, f, l, p);
    325 	return -1;
    326 }
    327 
    328 static int
    329 conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c,
    330     const char *p)
    331 {
    332 	if (strcmp(p, "stream") == 0) {
    333 		c->c_proto = IPPROTO_TCP;
    334 		return 0;
    335 	}
    336 	if (strcmp(p, "dgram") == 0) {
    337 		c->c_proto = IPPROTO_UDP;
    338 		return 0;
    339 	}
    340 	return conf_getnum(f, l, local, &c->c_proto, "protocol", p);
    341 }
    342 
    343 static int
    344 conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
    345     const char *p)
    346 {
    347 	if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
    348 		c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
    349 		return 0;
    350 	}
    351 	return conf_getnum(f, l, local, &c->c_family, "family", p);
    352 }
    353 
    354 static int
    355 conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c,
    356     const char *p)
    357 {
    358 	struct passwd *pw;
    359 
    360 	if ((pw = getpwnam(p)) != NULL) {
    361 		c->c_uid = (int)pw->pw_uid;
    362 		return 0;
    363 	}
    364 
    365 	return conf_getnum(f, l, local, &c->c_uid, "user", p);
    366 }
    367 
    368 
    369 static int
    370 conf_getname(const char *f, size_t l, bool local, struct conf *c,
    371     const char *p)
    372 {
    373 	if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1)
    374 		return -1;
    375 
    376 	if (strcmp(p, "*") == 0) {
    377 		strlcpy(c->c_name, rulename, CONFNAMESZ);
    378 		return 0;
    379 	}
    380 
    381 	if (strcmp(p, "=") == 0) {
    382 		if (local)
    383 			goto out;
    384 		c->c_name[0] = '\0';
    385 		return 0;
    386 	}
    387 
    388 	snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
    389 	return 0;
    390 out:
    391 	(*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
    392 	    " config", __func__, f, l);
    393 	return -1;
    394 }
    395 
    396 static int
    397 getvalue(const char *f, size_t l, bool local, void *r, char **p,
    398     int (*fun)(const char *, size_t, bool, struct conf *, const char *))
    399 {
    400 	char *ep = *p;
    401 
    402 	advance(p);
    403 	return (*fun)(f, l, local, r, ep);
    404 }
    405 
    406 
    407 static int
    408 conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
    409 {
    410 	int e;
    411 
    412 	c->c_lineno = l;
    413 
    414 	while (*p && isspace((unsigned char)*p))
    415 		p++;
    416 
    417 	memset(c, 0, sizeof(*c));
    418 	e = getvalue(f, l, local, c, &p, conf_gethostport);
    419 	if (e) return -1;
    420 	e = getvalue(f, l, local, c, &p, conf_getproto);
    421 	if (e) return -1;
    422 	e = getvalue(f, l, local, c, &p, conf_getfamily);
    423 	if (e) return -1;
    424 	e = getvalue(f, l, local, c, &p, conf_getuid);
    425 	if (e) return -1;
    426 	e = getvalue(f, l, local, c, &p, conf_getname);
    427 	if (e) return -1;
    428 	e = getvalue(f, l, local, c, &p, conf_getnfail);
    429 	if (e) return -1;
    430 	e = getvalue(f, l, local, c, &p, conf_getsecs);
    431 	if (e) return -1;
    432 
    433 	return 0;
    434 }
    435 
    436 static int
    437 conf_sort(const void *v1, const void *v2)
    438 {
    439 	const struct conf *c1 = v1;
    440 	const struct conf *c2 = v2;
    441 
    442 #define CMP(a, b, f) \
    443 	if ((a)->f > (b)->f) return -1; \
    444 	else if ((a)->f < (b)->f) return 1
    445 
    446 	CMP(c1, c2, c_ss.ss_family);
    447 	CMP(c1, c2, c_lmask);
    448 	CMP(c1, c2, c_port);
    449 	CMP(c1, c2, c_proto);
    450 	CMP(c1, c2, c_family);
    451 	CMP(c1, c2, c_rmask);
    452 	CMP(c1, c2, c_uid);
    453 #undef CMP
    454 	return 0;
    455 }
    456 
    457 static int
    458 conf_is_interface(const char *name)
    459 {
    460 	const struct ifaddrs *ifa;
    461 
    462 	for (ifa = ifas; ifa; ifa = ifa->ifa_next)
    463 		if (strcmp(ifa->ifa_name, name) == 0)
    464 			return 1;
    465 	return 0;
    466 }
    467 
    468 #define MASK(m)  ((uint32_t)~((1 << (32 - (m))) - 1))
    469 
    470 static int
    471 conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
    472 {
    473 	const uint32_t *a1 = v1;
    474 	const uint32_t *a2 = v2;
    475 	uint32_t m;
    476 	int omask = mask;
    477 
    478 	switch (mask) {
    479 	case FSTAR:
    480 		if (memcmp(v1, v2, len) == 0)
    481 			return 1;
    482 		goto out;
    483 	case FEQUAL:
    484 		(*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
    485 		    mask);
    486 		abort();
    487 	default:
    488 		break;
    489 	}
    490 
    491 	for (size_t i = 0; i < (len >> 2); i++) {
    492 		if (mask > 32) {
    493 			m = htonl((uint32_t)~0);
    494 			mask -= 32;
    495 		} else if (mask) {
    496 			m = htonl(MASK(mask));
    497 			mask = 0;
    498 		} else
    499 			return 1;
    500 		if ((a1[i] & m) != (a2[i] & m))
    501 			goto out;
    502 	}
    503 	return 1;
    504 out:
    505 	if (debug > 1) {
    506 		char b1[256], b2[256];
    507 		blhexdump(b1, sizeof(b1), "a1", v1, len);
    508 		blhexdump(b2, sizeof(b2), "a2", v2, len);
    509 		(*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
    510 		    b1, b2, omask);
    511 	}
    512 	return 0;
    513 }
    514 
    515 /*
    516  * Apply the mask to the given address
    517  */
    518 static void
    519 conf_apply_mask(void *v, size_t len, int mask)
    520 {
    521 	uint32_t *a = v;
    522 	uint32_t m;
    523 
    524 	switch (mask) {
    525 	case FSTAR:
    526 		return;
    527 	case FEQUAL:
    528 		(*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
    529 		    mask);
    530 		abort();
    531 	default:
    532 		break;
    533 	}
    534 	len >>= 2;
    535 
    536 	for (size_t i = 0; i < len; i++) {
    537 		if (mask > 32) {
    538 			m = htonl((uint32_t)~0);
    539 			mask -= 32;
    540 		} else if (mask) {
    541 			m = htonl(MASK(mask));
    542 			mask = 0;
    543 		} else
    544 			m = 0;
    545 		a[i] &= m;
    546 	}
    547 }
    548 
    549 /*
    550  * apply the mask and the port to the address given
    551  */
    552 static void
    553 conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
    554 {
    555 	struct sockaddr_in *sin;
    556 	struct sockaddr_in6 *sin6;
    557 	in_port_t *port;
    558 	void *addr;
    559 	size_t alen;
    560 
    561 	c->c_lmask = c->c_rmask;
    562 	c->c_ss = *ss;
    563 
    564 	if (c->c_ss.ss_family != c->c_family) {
    565 		(*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
    566 		    "%u != %u", __func__, c->c_ss.ss_family, c->c_family);
    567 		abort();
    568 	}
    569 
    570 	switch (c->c_ss.ss_family) {
    571 	case AF_INET:
    572 		sin = (void *)&c->c_ss;
    573 		port = &sin->sin_port;
    574 		addr = &sin->sin_addr;
    575 		alen = sizeof(sin->sin_addr);
    576 		break;
    577 	case AF_INET6:
    578 		sin6 = (void *)&c->c_ss;
    579 		port = &sin6->sin6_port;
    580 		addr = &sin6->sin6_addr;
    581 		alen = sizeof(sin6->sin6_addr);
    582 		break;
    583 	default:
    584 		(*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
    585 		    __func__, c->c_ss.ss_family);
    586 		abort();
    587 	}
    588 
    589 	*port = htons((in_port_t)c->c_port);
    590 	conf_apply_mask(addr, alen, c->c_lmask);
    591 	if (c->c_lmask == FSTAR)
    592 		c->c_lmask = (int)(alen * 8);
    593 	if (debug) {
    594 		char buf[128];
    595 		sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
    596 		(*lfun)(LOG_DEBUG, "Applied address %s", buf);
    597 	}
    598 }
    599 
    600 /*
    601  * Compared two addresses for equality applying the mask
    602  */
    603 static int
    604 conf_inet_eq(const void *v1, const void *v2, int mask)
    605 {
    606 	const struct sockaddr *sa1 = v1;
    607 	const struct sockaddr *sa2 = v2;
    608 	size_t size;
    609 
    610 	if (sa1->sa_family != sa2->sa_family)
    611 		return 0;
    612 
    613 	switch (sa1->sa_family) {
    614 	case AF_INET: {
    615 		const struct sockaddr_in *s1 = v1;
    616 		const struct sockaddr_in *s2 = v2;
    617 		size = sizeof(s1->sin_addr);
    618 		v1 = &s1->sin_addr;
    619 		v2 = &s2->sin_addr;
    620 		break;
    621 	}
    622 
    623 	case AF_INET6: {
    624 		const struct sockaddr_in6 *s1 = v1;
    625 		const struct sockaddr_in6 *s2 = v2;
    626 		size = sizeof(s1->sin6_addr);
    627 		v1 = &s1->sin6_addr;
    628 		v2 = &s2->sin6_addr;
    629 		break;
    630 	}
    631 
    632 	default:
    633 		(*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
    634 		    __func__, sa1->sa_family);
    635 		abort();
    636 	}
    637 
    638 	return conf_amask_eq(v1, v2, size, mask);
    639 }
    640 
    641 static int
    642 conf_addr_in_interface(const struct sockaddr_storage *s1,
    643     const struct sockaddr_storage *s2, int mask)
    644 {
    645 	const char *name = SIF_NAME(s2);
    646 	const struct ifaddrs *ifa;
    647 
    648 	for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
    649 		if ((ifa->ifa_flags & IFF_UP) == 0)
    650 			continue;
    651 
    652 		if (strcmp(ifa->ifa_name, name) != 0)
    653 			continue;
    654 
    655 		if (s1->ss_family != ifa->ifa_addr->sa_family)
    656 			continue;
    657 
    658 		bool eq;
    659 		switch (s1->ss_family) {
    660 		case AF_INET:
    661 		case AF_INET6:
    662 			eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
    663 			break;
    664 		default:
    665 			(*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
    666 			continue;
    667 		}
    668 		if (eq)
    669 			return 1;
    670 	}
    671 	return 0;
    672 }
    673 
    674 static int
    675 conf_addr_eq(const struct sockaddr_storage *s1,
    676     const struct sockaddr_storage *s2, int mask)
    677 {
    678 	switch (s2->ss_family) {
    679 	case 0:
    680 		return 1;
    681 	case AF_MAX:
    682 		return conf_addr_in_interface(s1, s2, mask);
    683 	case AF_INET:
    684 	case AF_INET6:
    685 		return conf_inet_eq(s1, s2, mask);
    686 	default:
    687 		(*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
    688 		    __func__, s1->ss_family);
    689 		abort();
    690 	}
    691 }
    692 
    693 static int
    694 conf_eq(const struct conf *c1, const struct conf *c2)
    695 {
    696 
    697 	if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
    698 		return 0;
    699 
    700 #define CMP(a, b, f) \
    701 	if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
    702 		if (debug > 1) \
    703 			(*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
    704 			    __STRING(f), (a)->f, (b)->f); \
    705 		return 0; \
    706 	}
    707 	CMP(c1, c2, c_port);
    708 	CMP(c1, c2, c_proto);
    709 	CMP(c1, c2, c_family);
    710 	CMP(c1, c2, c_uid);
    711 #undef CMP
    712 	return 1;
    713 }
    714 
    715 static const char *
    716 conf_num(char *b, size_t l, int n)
    717 {
    718 	switch (n) {
    719 	case FSTAR:
    720 		return "*";
    721 	case FEQUAL:
    722 		return "=";
    723 	default:
    724 		snprintf(b, l, "%d", n);
    725 		return b;
    726 	}
    727 }
    728 
    729 static const char *
    730 fmtname(const char *n) {
    731 	size_t l = strlen(rulename);
    732 	if (l == 0)
    733 		return "*";
    734 	if (strncmp(n, rulename, l) == 0) {
    735 		if (n[l] != '\0')
    736 			return n + l;
    737 		else
    738 			return "*";
    739 	} else if (!*n)
    740 		return "=";
    741 	else
    742 		return n;
    743 }
    744 
    745 static void
    746 fmtport(char *b, size_t l, int port)
    747 {
    748 	char buf[128];
    749 
    750 	if (port == FSTAR)
    751 		return;
    752 
    753 	if (b[0] == '\0' || strcmp(b, "*") == 0)
    754 		snprintf(b, l, "%d", port);
    755 	else {
    756 		snprintf(buf, sizeof(buf), ":%d", port);
    757 		strlcat(b, buf, l);
    758 	}
    759 }
    760 
    761 static const char *
    762 fmtmask(char *b, size_t l, int fam, int mask)
    763 {
    764 	char buf[128];
    765 
    766 	switch (mask) {
    767 	case FSTAR:
    768 		return "";
    769 	case FEQUAL:
    770 		if (strcmp(b, "=") == 0)
    771 			return "";
    772 		else {
    773 			strlcat(b, "/=", l);
    774 			return b;
    775 		}
    776 	default:
    777 		break;
    778 	}
    779 
    780 	switch (fam) {
    781 	case AF_INET:
    782 		if (mask == 32)
    783 			return "";
    784 		break;
    785 	case AF_INET6:
    786 		if (mask == 128)
    787 			return "";
    788 		break;
    789 	default:
    790 		break;
    791 	}
    792 
    793 	snprintf(buf, sizeof(buf), "/%d", mask);
    794 	strlcat(b, buf, l);
    795 	return b;
    796 }
    797 
    798 static const char *
    799 conf_namemask(char *b, size_t l, const struct conf *c)
    800 {
    801 	strlcpy(b, fmtname(c->c_name), l);
    802 	fmtmask(b, l, c->c_family, c->c_rmask);
    803 	return b;
    804 }
    805 
    806 const char *
    807 conf_print(char *buf, size_t len, const char *pref, const char *delim,
    808     const struct conf *c)
    809 {
    810 	char ha[128], hb[32], b[5][64];
    811 	int sp;
    812 
    813 #define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
    814 
    815 	switch (c->c_ss.ss_family) {
    816 	case 0:
    817 		snprintf(ha, sizeof(ha), "*");
    818 		break;
    819 	case AF_MAX:
    820 		snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
    821 		break;
    822 	default:
    823 		sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
    824 		break;
    825 	}
    826 
    827 	fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
    828 	fmtport(ha, sizeof(ha), c->c_port);
    829 
    830 	sp = *delim == '\t' ? 20 : -1;
    831 	hb[0] = '\0';
    832 	if (*delim)
    833 		snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
    834 		    "%s%s" "%s%s%s",
    835 		    pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
    836 		    N(1, c->c_family), delim, N(2, c->c_uid), delim,
    837 		    conf_namemask(hb, sizeof(hb), c), delim,
    838 		    N(3, c->c_nfail), delim, N(4, c->c_duration));
    839 	else
    840 		snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
    841 		    "uid:%s, name:%s, nfail:%s, duration:%s", pref,
    842 		    ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
    843 		    conf_namemask(hb, sizeof(hb), c),
    844 		    N(3, c->c_nfail), N(4, c->c_duration));
    845 	return buf;
    846 }
    847 
    848 /*
    849  * Apply the local config match to the result
    850  */
    851 static void
    852 conf_apply(struct conf *c, const struct conf *sc)
    853 {
    854 	char buf[BUFSIZ];
    855 
    856 	if (debug) {
    857 		(*lfun)(LOG_DEBUG, "%s: %s", __func__,
    858 		    conf_print(buf, sizeof(buf), "merge:\t", "", sc));
    859 		(*lfun)(LOG_DEBUG, "%s: %s", __func__,
    860 		    conf_print(buf, sizeof(buf), "to:\t", "", c));
    861 	}
    862 	memcpy(c->c_name, sc->c_name, CONFNAMESZ);
    863 	c->c_uid = sc->c_uid;
    864 	c->c_rmask = sc->c_rmask;
    865 	c->c_nfail = sc->c_nfail;
    866 	c->c_duration = sc->c_duration;
    867 
    868 	if (debug)
    869 		(*lfun)(LOG_DEBUG, "%s: %s", __func__,
    870 		    conf_print(buf, sizeof(buf), "result:\t", "", c));
    871 }
    872 
    873 /*
    874  * Merge a remote configuration to the result
    875  */
    876 static void
    877 conf_merge(struct conf *c, const struct conf *sc)
    878 {
    879 	char buf[BUFSIZ];
    880 
    881 	if (debug) {
    882 		(*lfun)(LOG_DEBUG, "%s: %s", __func__,
    883 		    conf_print(buf, sizeof(buf), "merge:\t", "", sc));
    884 		(*lfun)(LOG_DEBUG, "%s: %s", __func__,
    885 		    conf_print(buf, sizeof(buf), "to:\t", "", c));
    886 	}
    887 
    888 	if (sc->c_name[0])
    889 		memcpy(c->c_name, sc->c_name, CONFNAMESZ);
    890 	if (sc->c_uid != FEQUAL)
    891 		c->c_uid = sc->c_uid;
    892 	if (sc->c_rmask != FEQUAL)
    893 		c->c_lmask = c->c_rmask = sc->c_rmask;
    894 	if (sc->c_nfail != FEQUAL)
    895 		c->c_nfail = sc->c_nfail;
    896 	if (sc->c_duration != FEQUAL)
    897 		c->c_duration = sc->c_duration;
    898 	if (debug)
    899 		(*lfun)(LOG_DEBUG, "%s: %s", __func__,
    900 		    conf_print(buf, sizeof(buf), "result:\t", "", c));
    901 }
    902 
    903 static void
    904 confset_init(struct confset *cs)
    905 {
    906 	cs->cs_c = NULL;
    907 	cs->cs_n = 0;
    908 	cs->cs_m = 0;
    909 }
    910 
    911 static int
    912 confset_grow(struct confset *cs)
    913 {
    914 	void *tc;
    915 
    916 	cs->cs_m += 10;
    917 	tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
    918 	if (tc == NULL) {
    919 		(*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
    920 		return -1;
    921 	}
    922 	cs->cs_c = tc;
    923 	return 0;
    924 }
    925 
    926 static struct conf *
    927 confset_get(struct confset *cs)
    928 {
    929 	return &cs->cs_c[cs->cs_n];
    930 }
    931 
    932 static bool
    933 confset_full(const struct confset *cs)
    934 {
    935 	return cs->cs_n == cs->cs_m;
    936 }
    937 
    938 static void
    939 confset_sort(struct confset *cs)
    940 {
    941 	qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
    942 }
    943 
    944 static void
    945 confset_add(struct confset *cs)
    946 {
    947 	cs->cs_n++;
    948 }
    949 
    950 static void
    951 confset_free(struct confset *cs)
    952 {
    953 	free(cs->cs_c);
    954 	confset_init(cs);
    955 }
    956 
    957 static void
    958 confset_replace(struct confset *dc, struct confset *sc)
    959 {
    960 	struct confset tc;
    961 	tc = *dc;
    962 	*dc = *sc;
    963 	confset_init(sc);
    964 	confset_free(&tc);
    965 }
    966 
    967 static void
    968 confset_list(const struct confset *cs, const char *msg, const char *where)
    969 {
    970 	char buf[BUFSIZ];
    971 
    972 	(*lfun)(LOG_DEBUG, "[%s]", msg);
    973 	(*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
    974 	    where);
    975 	for (size_t i = 0; i < cs->cs_n; i++)
    976 		(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
    977 		    &cs->cs_c[i]));
    978 }
    979 
    980 /*
    981  * Match a configuration against the given list and apply the function
    982  * to it, returning the matched entry number.
    983  */
    984 static size_t
    985 confset_match(const struct confset *cs, struct conf *c,
    986     void (*fun)(struct conf *, const struct conf *))
    987 {
    988 	char buf[BUFSIZ];
    989 	size_t i;
    990 
    991 	for (i = 0; i < cs->cs_n; i++) {
    992 		if (debug)
    993 			(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
    994 			    "check:\t", "", &cs->cs_c[i]));
    995 		if (conf_eq(c, &cs->cs_c[i])) {
    996 			if (debug)
    997 				(*lfun)(LOG_DEBUG, "%s",
    998 				    conf_print(buf, sizeof(buf),
    999 				    "found:\t", "", &cs->cs_c[i]));
   1000 			(*fun)(c, &cs->cs_c[i]);
   1001 			break;
   1002 		}
   1003 	}
   1004 	return i;
   1005 }
   1006 
   1007 #ifdef AF_ROUTE
   1008 static int
   1009 conf_route_perm(int fd) {
   1010 #if defined(RTM_IFANNOUNCE) && defined(RT_ROUNDUP)
   1011 	/*
   1012 	 * Send a routing message that is not supported to check for access
   1013 	 * We expect EOPNOTSUPP for having access, since we are sending a
   1014 	 * request the system does not understand and EACCES if we don't have
   1015 	 * access.
   1016 	 */
   1017 	static struct sockaddr_in sin = {
   1018 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
   1019 		.sin_len = sizeof(sin),
   1020 #endif
   1021 		.sin_family = AF_INET,
   1022 	};
   1023 	char buf[4096];
   1024 	struct rt_msghdr *rtm = (void *)buf;
   1025 	char *cp = (char *)(rtm + 1);
   1026 	size_t l;
   1027 
   1028 #define NEXTADDR(s) \
   1029 	l = RT_ROUNDUP(sizeof(*s)); memmove(cp, s, l); cp += l;
   1030 	memset(buf, 0, sizeof(buf));
   1031 	rtm->rtm_type = RTM_IFANNOUNCE;
   1032 	rtm->rtm_flags = 0;
   1033 	rtm->rtm_addrs = RTA_DST|RTA_GATEWAY;
   1034 	rtm->rtm_version = RTM_VERSION;
   1035 	rtm->rtm_seq = 666;
   1036 	NEXTADDR(&sin);
   1037 	NEXTADDR(&sin);
   1038 	rtm->rtm_msglen = (u_short)((char *)cp - (char *)rtm);
   1039 	if (write(fd, rtm, rtm->rtm_msglen) != -1) {
   1040 		(*lfun)(LOG_ERR, "Writing to routing socket succeeded!");
   1041 		return 0;
   1042 	}
   1043 	switch (errno) {
   1044 	case EACCES:
   1045 		return 0;
   1046 	case EOPNOTSUPP:
   1047 		return 1;
   1048 	default:
   1049 		(*lfun)(LOG_ERR,
   1050 		    "Unexpected error writing to routing socket (%m)");
   1051 		return 0;
   1052 	}
   1053 #else
   1054 	return 0;
   1055 #endif
   1056 }
   1057 #endif
   1058 
   1059 static int
   1060 conf_handle_inet(int fd, const void *lss, struct conf *cr)
   1061 {
   1062 	char buf[BUFSIZ];
   1063 	int proto;
   1064 	socklen_t slen = sizeof(proto);
   1065 
   1066 	if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
   1067 		(*lfun)(LOG_ERR, "getsockopt failed (%m)");
   1068 		return -1;
   1069 	}
   1070 
   1071 	if (debug) {
   1072 		sockaddr_snprintf(buf, sizeof(buf), "%a:%p", lss);
   1073 		(*lfun)(LOG_DEBUG, "listening socket: %s", buf);
   1074 	}
   1075 
   1076 	switch (proto) {
   1077 	case SOCK_STREAM:
   1078 		cr->c_proto = IPPROTO_TCP;
   1079 		break;
   1080 	case SOCK_DGRAM:
   1081 		cr->c_proto = IPPROTO_UDP;
   1082 		break;
   1083 	default:
   1084 		(*lfun)(LOG_ERR, "unsupported protocol %d", proto);
   1085 		return -1;
   1086 	}
   1087 	return 0;
   1088 }
   1089 
   1090 const struct conf *
   1091 conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
   1092     struct conf *cr)
   1093 {
   1094 	socklen_t slen;
   1095 	struct sockaddr_storage lss;
   1096 	size_t i;
   1097 	char buf[BUFSIZ];
   1098 
   1099 	memset(cr, 0, sizeof(*cr));
   1100 	slen = sizeof(lss);
   1101 	memset(&lss, 0, slen);
   1102 	if (getsockname(fd, (void *)&lss, &slen) == -1) {
   1103 		(*lfun)(LOG_ERR, "getsockname failed (%m)");
   1104 		return NULL;
   1105 	}
   1106 
   1107 	switch (lss.ss_family) {
   1108 	case AF_INET:
   1109 		cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
   1110 		if (conf_handle_inet(fd, &lss, cr) == -1)
   1111 			return NULL;
   1112 		break;
   1113 	case AF_INET6:
   1114 		cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
   1115 		if (conf_handle_inet(fd, &lss, cr) == -1)
   1116 			return NULL;
   1117 		break;
   1118 #ifdef AF_ROUTE
   1119 	case AF_ROUTE:
   1120 		if (!conf_route_perm(fd)) {
   1121 			(*lfun)(LOG_ERR,
   1122 			    "permission denied to routing socket (%m)");
   1123 			return NULL;
   1124 		}
   1125 		cr->c_proto = FSTAR;
   1126 		cr->c_port = FSTAR;
   1127 		memcpy(&lss, rss, sizeof(lss));
   1128 		break;
   1129 #endif
   1130 	default:
   1131 		(*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
   1132 		return NULL;
   1133 	}
   1134 
   1135 	cr->c_ss = lss;
   1136 	cr->c_lmask = FSTAR;
   1137 	cr->c_uid = (int)uid;
   1138 	cr->c_family = lss.ss_family;
   1139 	cr->c_name[0] = '\0';
   1140 	cr->c_rmask = FSTAR;
   1141 	cr->c_nfail = FSTAR;
   1142 	cr->c_duration = FSTAR;
   1143 
   1144 	if (debug)
   1145 		(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
   1146 		    "look:\t", "", cr));
   1147 
   1148 	/* match the local config */
   1149 	i = confset_match(&lconf, cr, conf_apply);
   1150 	if (i == lconf.cs_n) {
   1151 		if (debug)
   1152 			(*lfun)(LOG_DEBUG, "not found");
   1153 		return NULL;
   1154 	}
   1155 
   1156 	conf_addr_set(cr, rss);
   1157 	/* match the remote config */
   1158 	confset_match(&rconf, cr, conf_merge);
   1159 	/* to apply the mask */
   1160 	conf_addr_set(cr, &cr->c_ss);
   1161 
   1162 	return cr;
   1163 }
   1164 
   1165 
   1166 void
   1167 conf_parse(const char *f)
   1168 {
   1169 	FILE *fp;
   1170 	char *line;
   1171 	size_t lineno, len;
   1172 	struct confset lc, rc, *cs;
   1173 
   1174 	if ((fp = fopen(f, "r")) == NULL) {
   1175 		(*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f);
   1176 		return;
   1177 	}
   1178 
   1179 	lineno = 0;
   1180 
   1181 	confset_init(&rc);
   1182 	confset_init(&lc);
   1183 	cs = &lc;
   1184 	for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
   1185 	    free(line))
   1186 	{
   1187 		if (!*line)
   1188 			continue;
   1189 		if (strcmp(line, "[local]") == 0) {
   1190 			cs = &lc;
   1191 			continue;
   1192 		}
   1193 		if (strcmp(line, "[remote]") == 0) {
   1194 			cs = &rc;
   1195 			continue;
   1196 		}
   1197 
   1198 		if (confset_full(cs)) {
   1199 			if (confset_grow(cs) == -1) {
   1200 				confset_free(&lc);
   1201 				confset_free(&rc);
   1202 				fclose(fp);
   1203 				free(line);
   1204 				return;
   1205 			}
   1206 		}
   1207 		if (conf_parseline(f, lineno, line, confset_get(cs),
   1208 		    cs == &lc) == -1)
   1209 			continue;
   1210 		confset_add(cs);
   1211 	}
   1212 
   1213 	fclose(fp);
   1214 	confset_sort(&lc);
   1215 	confset_sort(&rc);
   1216 
   1217 	confset_replace(&rconf, &rc);
   1218 	confset_replace(&lconf, &lc);
   1219 
   1220 	if (debug) {
   1221 		confset_list(&lconf, "local", "target");
   1222 		confset_list(&rconf, "remote", "source");
   1223 	}
   1224 }
   1225