aboutsummaryrefslogtreecommitdiff
path: root/bin/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/conf.c')
-rw-r--r--bin/conf.c1360
1 files changed, 0 insertions, 1360 deletions
diff --git a/bin/conf.c b/bin/conf.c
deleted file mode 100644
index 3c6fadb5fa45..000000000000
--- a/bin/conf.c
+++ /dev/null
@@ -1,1360 +0,0 @@
-/* $NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $ */
-
-/*-
- * Copyright (c) 2015 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Christos Zoulas.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef HAVE_SYS_CDEFS_H
-#include <sys/cdefs.h>
-#endif
-__RCSID("$NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $");
-
-#include <stdio.h>
-#ifdef HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-#include <string.h>
-#include <ctype.h>
-#include <inttypes.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <syslog.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <limits.h>
-#include <ifaddrs.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <net/route.h>
-#include <sys/socket.h>
-#include <dirent.h>
-
-#include "bl.h"
-#include "internal.h"
-#include "support.h"
-#include "conf.h"
-
-
-struct sockaddr_if {
- uint8_t sif_len;
- sa_family_t sif_family;
- in_port_t sif_port;
- char sif_name[16];
-};
-
-#define SIF_NAME(a) \
- ((const struct sockaddr_if *)(const void *)(a))->sif_name
-
-static int conf_is_interface(const char *);
-
-#define FSTAR -1
-#define FEQUAL -2
-
-static void
-advance(char **p)
-{
- char *ep = *p;
- while (*ep && !isspace((unsigned char)*ep))
- ep++;
- while (*ep && isspace((unsigned char)*ep))
- *ep++ = '\0';
- *p = ep;
-}
-
-static int
-conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name,
- const char *p)
-{
- int e;
- intmax_t im;
- int *r = rp;
-
- if (strcmp(p, "*") == 0) {
- *r = FSTAR;
- return 0;
- }
- if (strcmp(p, "=") == 0) {
- if (local)
- goto out;
- *r = FEQUAL;
- return 0;
- }
-
- im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
- if (e == 0) {
- *r = (int)im;
- return 0;
- }
-
- if (f == NULL)
- return -1;
- (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
- name, p);
- return -1;
-out:
- (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
- __func__, f, l, name);
- return -1;
-
-}
-
-static int
-conf_getnfail(const char *f, size_t l, bool local, struct conf *c,
- const char *p)
-{
- return conf_getnum(f, l, local, &c->c_nfail, "nfail", p);
-}
-
-static int
-conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
-{
- int e;
- char *ep;
- intmax_t tot, im;
-
- tot = 0;
- if (strcmp(p, "*") == 0) {
- c->c_duration = FSTAR;
- return 0;
- }
- if (strcmp(p, "=") == 0) {
- if (local)
- goto out;
- c->c_duration = FEQUAL;
- return 0;
- }
-again:
- im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
-
- if (e == ENOTSUP) {
- switch (*ep) {
- case 'd':
- im *= 24;
- /*FALLTHROUGH*/
- case 'h':
- im *= 60;
- /*FALLTHROUGH*/
- case 'm':
- im *= 60;
- /*FALLTHROUGH*/
- case 's':
- e = 0;
- tot += im;
- if (ep[1] != '\0') {
- p = ep + 2;
- goto again;
- }
- break;
- }
- } else
- tot = im;
-
- if (e == 0) {
- c->c_duration = (int)tot;
- return 0;
- }
-
- if (f == NULL)
- return -1;
- (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
- return -1;
-out:
- (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
- " config", __func__, f, l);
- return -1;
-
-}
-
-static int
-conf_getport(const char *f, size_t l, bool local, void *r, const char *p)
-{
- struct servent *sv;
-
- // XXX: Pass in the proto instead
- if ((sv = getservbyname(p, "tcp")) != NULL) {
- *(int *)r = ntohs(sv->s_port);
- return 0;
- }
- if ((sv = getservbyname(p, "udp")) != NULL) {
- *(int *)r = ntohs(sv->s_port);
- return 0;
- }
-
- return conf_getnum(f, l, local, r, "service", p);
-}
-
-static int
-conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask)
-{
- char *d;
- const char *s = *p;
-
- if ((d = strchr(s, ':')) != NULL) {
- *d++ = '\0';
- *p = d;
- }
- if ((d = strchr(s, '/')) == NULL) {
- *mask = FSTAR;
- return 0;
- }
-
- *d++ = '\0';
- return conf_getnum(f, l, local, mask, "mask", d);
-}
-
-static int
-conf_gethostport(const char *f, size_t l, bool local, struct conf *c,
- const char *p)
-{
- char *d; // XXX: Ok to write to string.
- in_port_t *port = NULL;
- const char *pstr;
-
- if (strcmp(p, "*") == 0) {
- c->c_port = FSTAR;
- c->c_lmask = FSTAR;
- return 0;
- }
-
- if ((d = strchr(p, ']')) != NULL) {
- *d++ = '\0';
- pstr = d;
- p++;
- } else
- pstr = p;
-
- if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1)
- goto out;
-
- if (d) {
- struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
- if (debug)
- (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
- if (strcmp(p, "*") != 0) {
- if (inet_pton(AF_INET6, p, &sin6->sin6_addr) != 1)
- goto out;
- sin6->sin6_family = AF_INET6;
-#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
- sin6->sin6_len = sizeof(*sin6);
-#endif
- port = &sin6->sin6_port;
- }
- if (!*pstr)
- pstr = "*";
- } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
- if (pstr == p)
- pstr = "*";
- struct sockaddr_in *sin = (void *)&c->c_ss;
- struct sockaddr_if *sif = (void *)&c->c_ss;
- if (debug)
- (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
- if (strcmp(p, "*") != 0) {
- if (conf_is_interface(p)) {
- if (!local)
- goto out2;
- if (debug)
- (*lfun)(LOG_DEBUG, "%s: interface %s",
- __func__, p);
- if (c->c_lmask != FSTAR)
- goto out1;
- sif->sif_family = AF_MAX;
- strlcpy(sif->sif_name, p,
- sizeof(sif->sif_name));
-#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
- sif->sif_len = sizeof(*sif);
-#endif
- port = &sif->sif_port;
- } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
- {
- sin->sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
- sin->sin_len = sizeof(*sin);
-#endif
- port = &sin->sin_port;
- } else
- goto out;
- }
- }
-
- if (conf_getport(f, l, local, &c->c_port, pstr) == -1)
- return -1;
-
- if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
- *port = htons((in_port_t)c->c_port);
- return 0;
-out:
- (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, p);
- return -1;
-out1:
- (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
- "interface [%s]", __func__, f, l, c->c_lmask, p);
- return -1;
-out2:
- (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
- "with remote config [%s]", __func__, f, l, p);
- return -1;
-}
-
-static int
-conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c,
- const char *p)
-{
- if (strcmp(p, "stream") == 0) {
- c->c_proto = IPPROTO_TCP;
- return 0;
- }
- if (strcmp(p, "dgram") == 0) {
- c->c_proto = IPPROTO_UDP;
- return 0;
- }
- return conf_getnum(f, l, local, &c->c_proto, "protocol", p);
-}
-
-static int
-conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
- const char *p)
-{
- if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
- c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
- return 0;
- }
- return conf_getnum(f, l, local, &c->c_family, "family", p);
-}
-
-static int
-conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c,
- const char *p)
-{
- struct passwd *pw;
-
- if ((pw = getpwnam(p)) != NULL) {
- c->c_uid = (int)pw->pw_uid;
- return 0;
- }
-
- return conf_getnum(f, l, local, &c->c_uid, "user", p);
-}
-
-
-static int
-conf_getname(const char *f, size_t l, bool local, struct conf *c,
- const char *p)
-{
- if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1)
- return -1;
-
- if (strcmp(p, "*") == 0) {
- strlcpy(c->c_name, rulename, CONFNAMESZ);
- return 0;
- }
-
- if (strcmp(p, "=") == 0) {
- if (local)
- goto out;
- c->c_name[0] = '\0';
- return 0;
- }
-
- snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
- return 0;
-out:
- (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
- " config", __func__, f, l);
- return -1;
-}
-
-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 *))
-{
- char *ep = *p;
-
- advance(p);
- return (*fun)(f, l, local, r, ep);
-}
-
-
-static int
-conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
-{
- int e;
-
- c->c_lineno = l;
-
- while (*p && isspace((unsigned char)*p))
- p++;
-
- memset(c, 0, sizeof(*c));
- e = getvalue(f, l, local, c, &p, conf_gethostport);
- if (e) return -1;
- e = getvalue(f, l, local, c, &p, conf_getproto);
- if (e) return -1;
- e = getvalue(f, l, local, c, &p, conf_getfamily);
- if (e) return -1;
- e = getvalue(f, l, local, c, &p, conf_getuid);
- if (e) return -1;
- e = getvalue(f, l, local, c, &p, conf_getname);
- if (e) return -1;
- e = getvalue(f, l, local, c, &p, conf_getnfail);
- if (e) return -1;
- e = getvalue(f, l, local, c, &p, conf_getsecs);
- if (e) return -1;
-
- return 0;
-}
-
-static int
-conf_sort(const void *v1, const void *v2)
-{
- const struct conf *c1 = v1;
- const struct conf *c2 = v2;
-
-#define CMP(a, b, f) \
- if ((a)->f > (b)->f) return -1; \
- else if ((a)->f < (b)->f) return 1
-
- CMP(c1, c2, c_ss.ss_family);
- CMP(c1, c2, c_lmask);
- CMP(c1, c2, c_port);
- CMP(c1, c2, c_proto);
- CMP(c1, c2, c_family);
- CMP(c1, c2, c_rmask);
- CMP(c1, c2, c_uid);
-#undef CMP
- return 0;
-}
-
-static int
-conf_is_interface(const char *name)
-{
- const struct ifaddrs *ifa;
-
- for (ifa = ifas; ifa; ifa = ifa->ifa_next)
- if (strcmp(ifa->ifa_name, name) == 0)
- return 1;
- return 0;
-}
-
-#define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1))
-
-static int
-conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
-{
- const uint32_t *a1 = v1;
- const uint32_t *a2 = v2;
- uint32_t m;
- int omask = mask;
-
- switch (mask) {
- case FSTAR:
- if (memcmp(v1, v2, len) == 0)
- return 1;
- goto out;
- case FEQUAL:
- (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
- mask);
- abort();
- default:
- break;
- }
-
- for (size_t i = 0; i < (len >> 2); i++) {
- if (mask > 32) {
- m = htonl((uint32_t)~0);
- mask -= 32;
- } else if (mask) {
- m = htonl(MASK(mask));
- mask = 0;
- } else
- return 1;
- if ((a1[i] & m) != (a2[i] & m))
- goto out;
- }
- return 1;
-out:
- if (debug > 1) {
- char b1[256], b2[256];
- blhexdump(b1, sizeof(b1), "a1", v1, len);
- blhexdump(b2, sizeof(b2), "a2", v2, len);
- (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
- b1, b2, omask);
- }
- return 0;
-}
-
-/*
- * Apply the mask to the given address
- */
-static void
-conf_apply_mask(void *v, size_t len, int mask)
-{
- uint32_t *a = v;
- uint32_t m;
-
- switch (mask) {
- case FSTAR:
- return;
- case FEQUAL:
- (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
- mask);
- abort();
- default:
- break;
- }
- len >>= 2;
-
- for (size_t i = 0; i < len; i++) {
- if (mask > 32) {
- m = htonl((uint32_t)~0);
- mask -= 32;
- } else if (mask) {
- m = htonl(MASK(mask));
- mask = 0;
- } else
- m = 0;
- a[i] &= m;
- }
-}
-
-/*
- * apply the mask and the port to the address given
- */
-static void
-conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
-{
- struct sockaddr_in *sin;
- struct sockaddr_in6 *sin6;
- in_port_t *port;
- void *addr;
- size_t alen;
-
- c->c_lmask = c->c_rmask;
- c->c_ss = *ss;
-
- if (c->c_ss.ss_family != c->c_family) {
- (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
- "%u != %u", __func__, c->c_ss.ss_family, c->c_family);
- abort();
- }
-
- switch (c->c_ss.ss_family) {
- case AF_INET:
- sin = (void *)&c->c_ss;
- port = &sin->sin_port;
- addr = &sin->sin_addr;
- alen = sizeof(sin->sin_addr);
- break;
- case AF_INET6:
- sin6 = (void *)&c->c_ss;
- port = &sin6->sin6_port;
- addr = &sin6->sin6_addr;
- alen = sizeof(sin6->sin6_addr);
- break;
- default:
- (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
- __func__, c->c_ss.ss_family);
- abort();
- }
-
- *port = htons((in_port_t)c->c_port);
- conf_apply_mask(addr, alen, c->c_lmask);
- if (c->c_lmask == FSTAR)
- c->c_lmask = (int)(alen * 8);
- if (debug) {
- char buf[128];
- sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
- (*lfun)(LOG_DEBUG, "Applied address %s", buf);
- }
-}
-
-/*
- * Compared two addresses for equality applying the mask
- */
-static int
-conf_inet_eq(const void *v1, const void *v2, int mask)
-{
- const struct sockaddr *sa1 = v1;
- const struct sockaddr *sa2 = v2;
- size_t size;
-
- if (sa1->sa_family != sa2->sa_family)
- return 0;
-
- switch (sa1->sa_family) {
- case AF_INET: {
- const struct sockaddr_in *s1 = v1;
- const struct sockaddr_in *s2 = v2;
- size = sizeof(s1->sin_addr);
- v1 = &s1->sin_addr;
- v2 = &s2->sin_addr;
- break;
- }
-
- case AF_INET6: {
- const struct sockaddr_in6 *s1 = v1;
- const struct sockaddr_in6 *s2 = v2;
- size = sizeof(s1->sin6_addr);
- v1 = &s1->sin6_addr;
- v2 = &s2->sin6_addr;
- break;
- }
-
- default:
- (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
- __func__, sa1->sa_family);
- abort();
- }
-
- return conf_amask_eq(v1, v2, size, mask);
-}
-
-static int
-conf_addr_in_interface(const struct sockaddr_storage *s1,
- const struct sockaddr_storage *s2, int mask)
-{
- const char *name = SIF_NAME(s2);
- const struct ifaddrs *ifa;
-
- for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
- if ((ifa->ifa_flags & IFF_UP) == 0)
- continue;
-
- if (strcmp(ifa->ifa_name, name) != 0)
- continue;
-
- if (s1->ss_family != ifa->ifa_addr->sa_family)
- continue;
-
- bool eq;
- switch (s1->ss_family) {
- case AF_INET:
- case AF_INET6:
- eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
- break;
- default:
- (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
- continue;
- }
- if (eq)
- return 1;
- }
- return 0;
-}
-
-static int
-conf_addr_eq(const struct sockaddr_storage *s1,
- const struct sockaddr_storage *s2, int mask)
-{
- switch (s2->ss_family) {
- case 0:
- return 1;
- case AF_MAX:
- return conf_addr_in_interface(s1, s2, mask);
- case AF_INET:
- case AF_INET6:
- return conf_inet_eq(s1, s2, mask);
- default:
- (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
- __func__, s1->ss_family);
- abort();
- }
-}
-
-static int
-conf_eq(const struct conf *c1, const struct conf *c2)
-{
- if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, FSTAR))
- return 0;
-
-#define CMP(a, b, f) \
- if ((a)->f != (b)->f) \
- return 0;
-
- CMP(c1, c2, c_port);
- CMP(c1, c2, c_proto);
- CMP(c1, c2, c_family);
- CMP(c1, c2, c_uid);
-#undef CMP
-
- return 1;
-}
-
-static int
-conf_match(const struct conf *c1, const struct conf *c2)
-{
-
- if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
- return 0;
-
-#define CMP(a, b, f) \
- if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
- if (debug > 1) \
- (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
- __STRING(f), (a)->f, (b)->f); \
- return 0; \
- }
- CMP(c1, c2, c_port);
- CMP(c1, c2, c_proto);
- CMP(c1, c2, c_family);
- CMP(c1, c2, c_uid);
-#undef CMP
- return 1;
-}
-
-static const char *
-conf_num(char *b, size_t l, int n)
-{
- switch (n) {
- case FSTAR:
- return "*";
- case FEQUAL:
- return "=";
- default:
- snprintf(b, l, "%d", n);
- return b;
- }
-}
-
-static const char *
-fmtname(const char *n) {
- size_t l = strlen(rulename);
- if (l == 0)
- return "*";
- if (strncmp(n, rulename, l) == 0) {
- if (n[l] != '\0')
- return n + l;
- else
- return "*";
- } else if (!*n)
- return "=";
- else
- return n;
-}
-
-static void
-fmtport(char *b, size_t l, int port)
-{
- char buf[128];
-
- if (port == FSTAR)
- return;
-
- if (b[0] == '\0' || strcmp(b, "*") == 0)
- snprintf(b, l, "%d", port);
- else {
- snprintf(buf, sizeof(buf), ":%d", port);
- strlcat(b, buf, l);
- }
-}
-
-static const char *
-fmtmask(char *b, size_t l, int fam, int mask)
-{
- char buf[128];
-
- switch (mask) {
- case FSTAR:
- return "";
- case FEQUAL:
- if (strcmp(b, "=") == 0)
- return "";
- else {
- strlcat(b, "/=", l);
- return b;
- }
- default:
- break;
- }
-
- switch (fam) {
- case AF_INET:
- if (mask == 32)
- return "";
- break;
- case AF_INET6:
- if (mask == 128)
- return "";
- break;
- default:
- break;
- }
-
- snprintf(buf, sizeof(buf), "/%d", mask);
- strlcat(b, buf, l);
- return b;
-}
-
-static const char *
-conf_namemask(char *b, size_t l, const struct conf *c)
-{
- strlcpy(b, fmtname(c->c_name), l);
- fmtmask(b, l, c->c_family, c->c_rmask);
- return b;
-}
-
-const char *
-conf_print(char *buf, size_t len, const char *pref, const char *delim,
- const struct conf *c)
-{
- char ha[128], hb[32], b[5][64];
- int sp;
-
-#define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
-
- switch (c->c_ss.ss_family) {
- case 0:
- snprintf(ha, sizeof(ha), "*");
- break;
- case AF_MAX:
- snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
- break;
- default:
- sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
- break;
- }
-
- fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
- fmtport(ha, sizeof(ha), c->c_port);
-
- sp = *delim == '\t' ? 20 : -1;
- hb[0] = '\0';
- if (*delim)
- snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
- "%s%s" "%s%s%s",
- pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
- N(1, c->c_family), delim, N(2, c->c_uid), delim,
- conf_namemask(hb, sizeof(hb), c), delim,
- N(3, c->c_nfail), delim, N(4, c->c_duration));
- else
- snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
- "uid:%s, name:%s, nfail:%s, duration:%s", pref,
- ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
- conf_namemask(hb, sizeof(hb), c),
- N(3, c->c_nfail), N(4, c->c_duration));
- return buf;
-}
-
-/*
- * Apply the local config match to the result
- */
-static void
-conf_apply(struct conf *c, const struct conf *sc)
-{
- char buf[BUFSIZ];
-
- if (debug) {
- (*lfun)(LOG_DEBUG, "%s: %s", __func__,
- conf_print(buf, sizeof(buf), "merge:\t", "", sc));
- (*lfun)(LOG_DEBUG, "%s: %s", __func__,
- conf_print(buf, sizeof(buf), "to:\t", "", c));
- }
- memcpy(c->c_name, sc->c_name, CONFNAMESZ);
- c->c_uid = sc->c_uid;
- c->c_rmask = sc->c_rmask;
- c->c_nfail = sc->c_nfail;
- c->c_duration = sc->c_duration;
-
- if (debug)
- (*lfun)(LOG_DEBUG, "%s: %s", __func__,
- conf_print(buf, sizeof(buf), "result:\t", "", c));
-}
-
-/*
- * Merge a remote configuration to the result
- */
-static void
-conf_merge(struct conf *c, const struct conf *sc)
-{
- char buf[BUFSIZ];
-
- if (debug) {
- (*lfun)(LOG_DEBUG, "%s: %s", __func__,
- conf_print(buf, sizeof(buf), "merge:\t", "", sc));
- (*lfun)(LOG_DEBUG, "%s: %s", __func__,
- conf_print(buf, sizeof(buf), "to:\t", "", c));
- }
-
- if (sc->c_name[0])
- memcpy(c->c_name, sc->c_name, CONFNAMESZ);
- if (sc->c_uid != FEQUAL)
- c->c_uid = sc->c_uid;
- if (sc->c_rmask != FEQUAL)
- c->c_lmask = c->c_rmask = sc->c_rmask;
- if (sc->c_nfail != FEQUAL)
- c->c_nfail = sc->c_nfail;
- if (sc->c_duration != FEQUAL)
- c->c_duration = sc->c_duration;
- if (debug)
- (*lfun)(LOG_DEBUG, "%s: %s", __func__,
- conf_print(buf, sizeof(buf), "result:\t", "", c));
-}
-
-static void
-confset_init(struct confset *cs)
-{
- cs->cs_c = NULL;
- cs->cs_n = 0;
- cs->cs_m = 0;
-}
-
-static int
-confset_grow(struct confset *cs)
-{
- void *tc;
-
- cs->cs_m += 10;
- tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
- if (tc == NULL) {
- (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
- return -1;
- }
- cs->cs_c = tc;
- return 0;
-}
-
-static struct conf *
-confset_get(struct confset *cs)
-{
- return &cs->cs_c[cs->cs_n];
-}
-
-static bool
-confset_full(const struct confset *cs)
-{
- return cs->cs_n == cs->cs_m;
-}
-
-static void
-confset_sort(struct confset *cs)
-{
- qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
-}
-
-static void
-confset_add(struct confset *cs)
-{
- cs->cs_n++;
-}
-
-static void
-confset_free(struct confset *cs)
-{
- free(cs->cs_c);
- confset_init(cs);
-}
-
-static void
-confset_merge(struct confset *dc, struct confset *sc)
-{
- size_t i, j;
- char buf[BUFSIZ];
-
- /* Check each rule of the src confset (sc) */
- for (i = 0; i < sc->cs_n; i++) {
- /* Compare to each rule in the dest confset (dc) */
- for (j = 0; j < dc->cs_n; j++) {
- if (conf_eq(&dc->cs_c[j], &sc->cs_c[i])) {
- break;
- }
- }
-
- if (j == dc->cs_n) {
- /* This is a new rule to add to the dest confset. */
- if (confset_full(dc) && confset_grow(dc) == -1)
- return;
-
- *confset_get(dc) = sc->cs_c[i];
- confset_add(dc);
- continue;
- }
-
- /* We had a match above. */
- /*
- * Check whether the rule from the src confset is more
- * restrictive than the existing one. Adjust the
- * existing rule if necessary.
- */
- if (sc->cs_c[i].c_nfail == dc->cs_c[j].c_nfail &&
- sc->cs_c[i].c_duration && dc->cs_c[j].c_duration) {
- (*lfun)(LOG_DEBUG, "skipping existing rule: %s",
- conf_print(buf, sizeof (buf), "", "\t", &sc->cs_c[i]));
- continue;
- }
-
- if (sc->cs_c[i].c_nfail < dc->cs_c[j].c_nfail)
- dc->cs_c[j].c_nfail = sc->cs_c[i].c_nfail;
-
- if (sc->cs_c[i].c_duration > dc->cs_c[j].c_duration)
- dc->cs_c[j].c_duration = sc->cs_c[i].c_duration;
-
- (*lfun)(LOG_DEBUG, "adjusted existing rule: %s",
- conf_print(buf, sizeof (buf), "", "\t", &dc->cs_c[j]));
- }
-
- confset_free(sc);
-}
-
-static void
-confset_list(const struct confset *cs, const char *msg, const char *where)
-{
- char buf[BUFSIZ];
-
- (*lfun)(LOG_DEBUG, "[%s]", msg);
- (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
- where);
- for (size_t i = 0; i < cs->cs_n; i++)
- (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
- &cs->cs_c[i]));
-}
-
-/*
- * Match a configuration against the given list and apply the function
- * to it, returning the matched entry number.
- */
-static size_t
-confset_match(const struct confset *cs, struct conf *c,
- void (*fun)(struct conf *, const struct conf *))
-{
- char buf[BUFSIZ];
- size_t i;
-
- for (i = 0; i < cs->cs_n; i++) {
- if (debug)
- (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
- "check:\t", "", &cs->cs_c[i]));
- if (conf_match(c, &cs->cs_c[i])) {
- if (debug)
- (*lfun)(LOG_DEBUG, "%s",
- conf_print(buf, sizeof(buf),
- "found:\t", "", &cs->cs_c[i]));
- (*fun)(c, &cs->cs_c[i]);
- break;
- }
- }
- return i;
-}
-
-#ifdef AF_ROUTE
-static int
-conf_route_perm(int fd) {
-#if defined(RTM_IFANNOUNCE) && defined(RT_ROUNDUP)
- /*
- * Send a routing message that is not supported to check for access
- * We expect EOPNOTSUPP for having access, since we are sending a
- * request the system does not understand and EACCES if we don't have
- * access.
- */
- static struct sockaddr_in sin = {
-#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
- .sin_len = sizeof(sin),
-#endif
- .sin_family = AF_INET,
- };
- char buf[4096];
- struct rt_msghdr *rtm = (void *)buf;
- char *cp = (char *)(rtm + 1);
- size_t l;
-
-#define NEXTADDR(s) \
- l = RT_ROUNDUP(sizeof(*s)); memmove(cp, s, l); cp += l;
- memset(buf, 0, sizeof(buf));
- rtm->rtm_type = RTM_IFANNOUNCE;
- rtm->rtm_flags = 0;
- rtm->rtm_addrs = RTA_DST|RTA_GATEWAY;
- rtm->rtm_version = RTM_VERSION;
- rtm->rtm_seq = 666;
- NEXTADDR(&sin);
- NEXTADDR(&sin);
- rtm->rtm_msglen = (u_short)((char *)cp - (char *)rtm);
- if (write(fd, rtm, rtm->rtm_msglen) != -1) {
- (*lfun)(LOG_ERR, "Writing to routing socket succeeded!");
- return 0;
- }
- switch (errno) {
- case EACCES:
- return 0;
- case EOPNOTSUPP:
- return 1;
- default:
- (*lfun)(LOG_ERR,
- "Unexpected error writing to routing socket (%m)");
- return 0;
- }
-#else
- return 0;
-#endif
-}
-#endif
-
-static int
-conf_handle_inet(int fd, const void *lss, struct conf *cr)
-{
- char buf[BUFSIZ];
- int proto;
- socklen_t slen = sizeof(proto);
-
- if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
- (*lfun)(LOG_ERR, "getsockopt failed (%m)");
- return -1;
- }
-
- if (debug) {
- sockaddr_snprintf(buf, sizeof(buf), "%a:%p", lss);
- (*lfun)(LOG_DEBUG, "listening socket: %s", buf);
- }
-
- switch (proto) {
- case SOCK_STREAM:
- cr->c_proto = IPPROTO_TCP;
- break;
- case SOCK_DGRAM:
- cr->c_proto = IPPROTO_UDP;
- break;
- default:
- (*lfun)(LOG_ERR, "unsupported protocol %d", proto);
- return -1;
- }
- return 0;
-}
-
-const struct conf *
-conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
- struct conf *cr)
-{
- socklen_t slen;
- struct sockaddr_storage lss;
- size_t i;
- char buf[BUFSIZ];
-
- memset(cr, 0, sizeof(*cr));
- slen = sizeof(lss);
- memset(&lss, 0, slen);
- if (getsockname(fd, (void *)&lss, &slen) == -1) {
- (*lfun)(LOG_ERR, "getsockname failed (%m)");
- return NULL;
- }
-
- switch (lss.ss_family) {
- case AF_INET:
- cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
- if (conf_handle_inet(fd, &lss, cr) == -1)
- return NULL;
- break;
- case AF_INET6:
- cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
- if (conf_handle_inet(fd, &lss, cr) == -1)
- return NULL;
- break;
-#ifdef AF_ROUTE
- case AF_ROUTE:
- if (!conf_route_perm(fd)) {
- (*lfun)(LOG_ERR,
- "permission denied to routing socket (%m)");
- return NULL;
- }
- cr->c_proto = FSTAR;
- cr->c_port = FSTAR;
- memcpy(&lss, rss, sizeof(lss));
- break;
-#endif
- default:
- (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
- return NULL;
- }
-
- cr->c_ss = lss;
- cr->c_lmask = FSTAR;
- cr->c_uid = (int)uid;
- cr->c_family = lss.ss_family;
- cr->c_name[0] = '\0';
- cr->c_rmask = FSTAR;
- cr->c_nfail = FSTAR;
- cr->c_duration = FSTAR;
-
- if (debug)
- (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
- "look:\t", "", cr));
-
- /* match the local config */
- i = confset_match(&lconf, cr, conf_apply);
- if (i == lconf.cs_n) {
- if (debug)
- (*lfun)(LOG_DEBUG, "not found");
- return NULL;
- }
-
- conf_addr_set(cr, rss);
- /* match the remote config */
- confset_match(&rconf, cr, conf_merge);
- /* to apply the mask */
- conf_addr_set(cr, &cr->c_ss);
-
- return cr;
-}
-
-static void
-conf_parsefile(FILE *fp, const char *config_file)
-{
- char *line;
- size_t lineno, len;
- struct confset lc, rc, *cs;
-
- lineno = 0;
-
- confset_init(&rc);
- confset_init(&lc);
- cs = &lc;
- for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
- free(line))
- {
- if (!*line)
- continue;
- if (strcmp(line, "[local]") == 0) {
- cs = &lc;
- continue;
- }
- if (strcmp(line, "[remote]") == 0) {
- cs = &rc;
- continue;
- }
-
- if (confset_full(cs)) {
- if (confset_grow(cs) == -1) {
- confset_free(&lc);
- confset_free(&rc);
- free(line);
- return;
- }
- }
- if (conf_parseline(config_file, lineno, line, confset_get(cs),
- cs == &lc) == -1)
- continue;
- confset_add(cs);
- }
-
- confset_merge(&rconf, &rc);
- confset_merge(&lconf, &lc);
-}
-
-
-static void
-conf_parsedir(DIR *dir, const char *config_path)
-{
- long path_max;
- struct dirent *dent;
- char *path;
- FILE *fp;
-
- if ((path_max = pathconf(config_path, _PC_PATH_MAX)) == -1)
- path_max = 2048;
-
- if ((path = malloc((size_t)path_max)) == NULL) {
- (*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
- __func__);
- return;
- }
-
- while ((dent = readdir(dir)) != NULL) {
- if (strcmp(dent->d_name, ".") == 0 ||
- strcmp(dent->d_name, "..") == 0)
- continue;
-
- (void) snprintf(path, (size_t)path_max, "%s/%s", config_path,
- dent->d_name);
- if ((fp = fopen(path, "r")) == NULL) {
- (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
- path);
- continue;
- }
- conf_parsefile(fp, path);
- fclose(fp);
- }
-
- free(path);
-}
-
-void
-conf_parse(const char *config_path)
-{
- char *path;
- DIR *dir;
- FILE *fp;
-
- if ((dir = opendir(config_path)) != NULL) {
- /*
- * If config_path is a directory, parse the configuration files
- * in the directory. Then we're done here.
- */
- conf_parsedir(dir, config_path);
- closedir(dir);
- goto out;
- } else if ((fp = fopen(config_path, "r")) != NULL) {
- /* If config_path is a file, parse it. */
- conf_parsefile(fp, config_path);
- fclose(fp);
- }
-
- /*
- * Append ".d" to config_path, and if that is a directory, parse the
- * configuration files in the directory.
- */
- if (asprintf(&path, "%s.d", config_path) < 0) {
- (*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
- __func__);
- goto out;
- }
-
- if ((dir = opendir(path)) != NULL) {
- conf_parsedir(dir, path);
- closedir(dir);
- }
- free(path);
-
-out:
- if (dir == NULL && fp == NULL) {
- (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
- config_path);
- return;
- }
-
- confset_sort(&lconf);
- confset_sort(&rconf);
-
- if (debug) {
- confset_list(&lconf, "local", "target");
- confset_list(&rconf, "remote", "source");
- }
-}