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