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