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