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