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