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