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