xref: /freebsd/contrib/blocklist/bin/conf.c (revision 48e64ca13d4f36795ac718911b805e3e9a726f1b)
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
advance(char ** p)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
conf_getnum(const char * f,size_t l,bool local,void * rp,const char * name,const char * p)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
conf_getnfail(const char * f,size_t l,bool local,struct conf * c,const char * p)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
conf_getsecs(const char * f,size_t l,bool local,struct conf * c,const char * p)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
conf_getport(const char * f,size_t l,bool local,void * r,const char * p)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
conf_getmask(const char * f,size_t l,bool local,const char ** p,int * mask)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
conf_gethostport(const char * f,size_t l,bool local,struct conf * c,const char * p)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
conf_getproto(const char * f,size_t l,bool local __unused,struct conf * c,const char * p)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
conf_getfamily(const char * f,size_t l,bool local __unused,struct conf * c,const char * p)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
conf_getuid(const char * f,size_t l,bool local __unused,struct conf * c,const char * p)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
conf_getname(const char * f,size_t l,bool local,struct conf * c,const char * p)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
getvalue(const char * f,size_t l,bool local,void * r,char ** p,int (* fun)(const char *,size_t,bool,struct conf *,const char *))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
conf_parseline(const char * f,size_t l,char * p,struct conf * c,bool local)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
conf_sort(const void * v1,const void * v2)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
conf_is_interface(const char * name)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
conf_amask_eq(const void * v1,const void * v2,size_t len,int mask)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
conf_apply_mask(void * v,size_t len,int mask)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
conf_addr_set(struct conf * c,const struct sockaddr_storage * ss)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
conf_inet_eq(const void * v1,const void * v2,int mask)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
conf_addr_in_interface(const struct sockaddr_storage * s1,const struct sockaddr_storage * s2,int mask)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
conf_addr_eq(const struct sockaddr_storage * s1,const struct sockaddr_storage * s2,int mask)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
conf_eq(const struct conf * c1,const struct conf * c2)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
conf_match(const struct conf * c1,const struct conf * c2)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 *
conf_num(char * b,size_t l,int n)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 *
fmtname(const char * n)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
fmtport(char * b,size_t l,int port)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 *
fmtmask(char * b,size_t l,int fam,int mask)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 *
conf_namemask(char * b,size_t l,const struct conf * c)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 *
conf_print(char * buf,size_t len,const char * pref,const char * delim,const struct conf * c)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
conf_apply(struct conf * c,const struct conf * sc)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
conf_merge(struct conf * c,const struct conf * sc)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
confset_init(struct confset * cs)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
confset_grow(struct confset * cs)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 *
confset_get(struct confset * cs)949 confset_get(struct confset *cs)
950 {
951 	return &cs->cs_c[cs->cs_n];
952 }
953 
954 static bool
confset_full(const struct confset * cs)955 confset_full(const struct confset *cs)
956 {
957 	return cs->cs_n == cs->cs_m;
958 }
959 
960 static void
confset_sort(struct confset * cs)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
confset_add(struct confset * cs)967 confset_add(struct confset *cs)
968 {
969 	cs->cs_n++;
970 }
971 
972 static void
confset_free(struct confset * cs)973 confset_free(struct confset *cs)
974 {
975 	free(cs->cs_c);
976 	confset_init(cs);
977 }
978 
979 static void
confset_merge(struct confset * dc,struct confset * sc)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
confset_list(const struct confset * cs,const char * msg,const char * where)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
confset_match(const struct confset * cs,struct conf * c,void (* fun)(struct conf *,const struct conf *))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
conf_route_perm(int fd)1072 conf_route_perm(int fd) {
1073 #if defined(RTM_IFANNOUNCE) && defined(SA_SIZE)
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 = SA_SIZE(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
conf_handle_inet(int fd,const void * lss,struct conf * cr)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 *
conf_find(int fd,uid_t uid,const struct sockaddr_storage * rss,struct conf * cr)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
conf_parsefile(FILE * fp,const char * config_file)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
conf_parsedir(DIR * dir,const char * config_path)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
conf_parse(const char * config_path)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