148e64ca1SJose Luis Duran /* $NetBSD: blocklistd.c,v 1.10 2025/03/26 17:09:35 christos Exp $ */
248e64ca1SJose Luis Duran
348e64ca1SJose Luis Duran /*-
448e64ca1SJose Luis Duran * Copyright (c) 2015 The NetBSD Foundation, Inc.
548e64ca1SJose Luis Duran * All rights reserved.
648e64ca1SJose Luis Duran *
748e64ca1SJose Luis Duran * This code is derived from software contributed to The NetBSD Foundation
848e64ca1SJose Luis Duran * by Christos Zoulas.
948e64ca1SJose Luis Duran *
1048e64ca1SJose Luis Duran * Redistribution and use in source and binary forms, with or without
1148e64ca1SJose Luis Duran * modification, are permitted provided that the following conditions
1248e64ca1SJose Luis Duran * are met:
1348e64ca1SJose Luis Duran * 1. Redistributions of source code must retain the above copyright
1448e64ca1SJose Luis Duran * notice, this list of conditions and the following disclaimer.
1548e64ca1SJose Luis Duran * 2. Redistributions in binary form must reproduce the above copyright
1648e64ca1SJose Luis Duran * notice, this list of conditions and the following disclaimer in the
1748e64ca1SJose Luis Duran * documentation and/or other materials provided with the distribution.
1848e64ca1SJose Luis Duran *
1948e64ca1SJose Luis Duran * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2048e64ca1SJose Luis Duran * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2148e64ca1SJose Luis Duran * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2248e64ca1SJose Luis Duran * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2348e64ca1SJose Luis Duran * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2448e64ca1SJose Luis Duran * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2548e64ca1SJose Luis Duran * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2648e64ca1SJose Luis Duran * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2748e64ca1SJose Luis Duran * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2848e64ca1SJose Luis Duran * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2948e64ca1SJose Luis Duran * POSSIBILITY OF SUCH DAMAGE.
3048e64ca1SJose Luis Duran */
3148e64ca1SJose Luis Duran #ifdef HAVE_CONFIG_H
3248e64ca1SJose Luis Duran #include "config.h"
3348e64ca1SJose Luis Duran #endif
3448e64ca1SJose Luis Duran
3548e64ca1SJose Luis Duran #ifdef HAVE_SYS_CDEFS_H
3648e64ca1SJose Luis Duran #include <sys/cdefs.h>
3748e64ca1SJose Luis Duran #endif
3848e64ca1SJose Luis Duran __RCSID("$NetBSD: blocklistd.c,v 1.10 2025/03/26 17:09:35 christos Exp $");
3948e64ca1SJose Luis Duran
4048e64ca1SJose Luis Duran #include <sys/types.h>
4148e64ca1SJose Luis Duran #include <sys/socket.h>
4248e64ca1SJose Luis Duran #include <sys/queue.h>
4348e64ca1SJose Luis Duran
4448e64ca1SJose Luis Duran #ifdef HAVE_LIBUTIL_H
4548e64ca1SJose Luis Duran #include <libutil.h>
4648e64ca1SJose Luis Duran #endif
4748e64ca1SJose Luis Duran #ifdef HAVE_UTIL_H
4848e64ca1SJose Luis Duran #include <util.h>
4948e64ca1SJose Luis Duran #endif
5048e64ca1SJose Luis Duran #include <string.h>
5148e64ca1SJose Luis Duran #include <signal.h>
5248e64ca1SJose Luis Duran #include <netdb.h>
5348e64ca1SJose Luis Duran #include <stdio.h>
5448e64ca1SJose Luis Duran #include <stdbool.h>
5548e64ca1SJose Luis Duran #include <string.h>
5648e64ca1SJose Luis Duran #include <inttypes.h>
5748e64ca1SJose Luis Duran #include <syslog.h>
5848e64ca1SJose Luis Duran #include <ctype.h>
5948e64ca1SJose Luis Duran #include <limits.h>
6048e64ca1SJose Luis Duran #include <errno.h>
6148e64ca1SJose Luis Duran #include <poll.h>
6248e64ca1SJose Luis Duran #include <fcntl.h>
6348e64ca1SJose Luis Duran #include <err.h>
6448e64ca1SJose Luis Duran #include <stdlib.h>
6548e64ca1SJose Luis Duran #include <unistd.h>
6648e64ca1SJose Luis Duran #include <time.h>
6748e64ca1SJose Luis Duran #include <ifaddrs.h>
6848e64ca1SJose Luis Duran #include <netinet/in.h>
6948e64ca1SJose Luis Duran
7048e64ca1SJose Luis Duran #include "bl.h"
7148e64ca1SJose Luis Duran #include "internal.h"
7248e64ca1SJose Luis Duran #include "conf.h"
7348e64ca1SJose Luis Duran #include "run.h"
7448e64ca1SJose Luis Duran #include "state.h"
7548e64ca1SJose Luis Duran #include "support.h"
7648e64ca1SJose Luis Duran
7748e64ca1SJose Luis Duran static const char *configfile = _PATH_BLCONF;
7848e64ca1SJose Luis Duran static DB *state;
7948e64ca1SJose Luis Duran static const char *dbfile = _PATH_BLSTATE;
8048e64ca1SJose Luis Duran static sig_atomic_t readconf;
8148e64ca1SJose Luis Duran static sig_atomic_t done;
8248e64ca1SJose Luis Duran static int vflag;
8348e64ca1SJose Luis Duran
8448e64ca1SJose Luis Duran static void
sigusr1(int n __unused)8548e64ca1SJose Luis Duran sigusr1(int n __unused)
8648e64ca1SJose Luis Duran {
8748e64ca1SJose Luis Duran debug++;
8848e64ca1SJose Luis Duran }
8948e64ca1SJose Luis Duran
9048e64ca1SJose Luis Duran static void
sigusr2(int n __unused)9148e64ca1SJose Luis Duran sigusr2(int n __unused)
9248e64ca1SJose Luis Duran {
9348e64ca1SJose Luis Duran debug--;
9448e64ca1SJose Luis Duran }
9548e64ca1SJose Luis Duran
9648e64ca1SJose Luis Duran static void
sighup(int n __unused)9748e64ca1SJose Luis Duran sighup(int n __unused)
9848e64ca1SJose Luis Duran {
9948e64ca1SJose Luis Duran readconf++;
10048e64ca1SJose Luis Duran }
10148e64ca1SJose Luis Duran
10248e64ca1SJose Luis Duran static void
sigdone(int n __unused)10348e64ca1SJose Luis Duran sigdone(int n __unused)
10448e64ca1SJose Luis Duran {
10548e64ca1SJose Luis Duran done++;
10648e64ca1SJose Luis Duran }
10748e64ca1SJose Luis Duran
10848e64ca1SJose Luis Duran static __dead void
usage(int c)10948e64ca1SJose Luis Duran usage(int c)
11048e64ca1SJose Luis Duran {
11148e64ca1SJose Luis Duran if (c != '?')
11248e64ca1SJose Luis Duran warnx("Unknown option `%c'", (char)c);
11348e64ca1SJose Luis Duran fprintf(stderr, "Usage: %s [-vdfr] [-c <config>] [-R <rulename>] "
11448e64ca1SJose Luis Duran "[-P <sockpathsfile>] [-C <controlprog>] [-D <dbfile>] "
11548e64ca1SJose Luis Duran "[-s <sockpath>] [-t <timeout>]\n", getprogname());
11648e64ca1SJose Luis Duran exit(EXIT_FAILURE);
11748e64ca1SJose Luis Duran }
11848e64ca1SJose Luis Duran
11948e64ca1SJose Luis Duran static int
getremoteaddress(bl_info_t * bi,struct sockaddr_storage * rss,socklen_t * rsl)12048e64ca1SJose Luis Duran getremoteaddress(bl_info_t *bi, struct sockaddr_storage *rss, socklen_t *rsl)
12148e64ca1SJose Luis Duran {
12248e64ca1SJose Luis Duran *rsl = sizeof(*rss);
12348e64ca1SJose Luis Duran memset(rss, 0, *rsl);
12448e64ca1SJose Luis Duran
12548e64ca1SJose Luis Duran if (getpeername(bi->bi_fd, (void *)rss, rsl) != -1)
12648e64ca1SJose Luis Duran return 0;
12748e64ca1SJose Luis Duran
12848e64ca1SJose Luis Duran if (errno != ENOTCONN) {
12948e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "getpeername failed (%m)");
13048e64ca1SJose Luis Duran return -1;
13148e64ca1SJose Luis Duran }
13248e64ca1SJose Luis Duran
13348e64ca1SJose Luis Duran if (bi->bi_slen == 0) {
13448e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "unconnected socket with no peer in message");
13548e64ca1SJose Luis Duran return -1;
13648e64ca1SJose Luis Duran }
13748e64ca1SJose Luis Duran
13848e64ca1SJose Luis Duran switch (bi->bi_ss.ss_family) {
13948e64ca1SJose Luis Duran case AF_INET:
14048e64ca1SJose Luis Duran *rsl = sizeof(struct sockaddr_in);
14148e64ca1SJose Luis Duran break;
14248e64ca1SJose Luis Duran case AF_INET6:
14348e64ca1SJose Luis Duran *rsl = sizeof(struct sockaddr_in6);
14448e64ca1SJose Luis Duran break;
14548e64ca1SJose Luis Duran default:
14648e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "bad client passed socket family %u",
14748e64ca1SJose Luis Duran (unsigned)bi->bi_ss.ss_family);
14848e64ca1SJose Luis Duran return -1;
14948e64ca1SJose Luis Duran }
15048e64ca1SJose Luis Duran
15148e64ca1SJose Luis Duran if (*rsl != bi->bi_slen) {
15248e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "bad client passed socket length %u != %u",
15348e64ca1SJose Luis Duran (unsigned)*rsl, (unsigned)bi->bi_slen);
15448e64ca1SJose Luis Duran return -1;
15548e64ca1SJose Luis Duran }
15648e64ca1SJose Luis Duran
15748e64ca1SJose Luis Duran memcpy(rss, &bi->bi_ss, *rsl);
15848e64ca1SJose Luis Duran
15948e64ca1SJose Luis Duran #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
16048e64ca1SJose Luis Duran if (*rsl != rss->ss_len) {
16148e64ca1SJose Luis Duran (*lfun)(LOG_ERR,
16248e64ca1SJose Luis Duran "bad client passed socket internal length %u != %u",
16348e64ca1SJose Luis Duran (unsigned)*rsl, (unsigned)rss->ss_len);
16448e64ca1SJose Luis Duran return -1;
16548e64ca1SJose Luis Duran }
16648e64ca1SJose Luis Duran #endif
16748e64ca1SJose Luis Duran return 0;
16848e64ca1SJose Luis Duran }
16948e64ca1SJose Luis Duran
17048e64ca1SJose Luis Duran static void
process(bl_t bl)17148e64ca1SJose Luis Duran process(bl_t bl)
17248e64ca1SJose Luis Duran {
17348e64ca1SJose Luis Duran struct sockaddr_storage rss;
17448e64ca1SJose Luis Duran socklen_t rsl;
17548e64ca1SJose Luis Duran char rbuf[BUFSIZ];
17648e64ca1SJose Luis Duran bl_info_t *bi;
17748e64ca1SJose Luis Duran struct conf c;
17848e64ca1SJose Luis Duran struct dbinfo dbi;
17948e64ca1SJose Luis Duran struct timespec ts;
18048e64ca1SJose Luis Duran
18148e64ca1SJose Luis Duran memset(&dbi, 0, sizeof(dbi));
18248e64ca1SJose Luis Duran memset(&c, 0, sizeof(c));
18348e64ca1SJose Luis Duran if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
18448e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "clock_gettime failed (%m)");
18548e64ca1SJose Luis Duran return;
18648e64ca1SJose Luis Duran }
18748e64ca1SJose Luis Duran
18848e64ca1SJose Luis Duran if ((bi = bl_recv(bl)) == NULL) {
18948e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "no message (%m)");
19048e64ca1SJose Luis Duran return;
19148e64ca1SJose Luis Duran }
19248e64ca1SJose Luis Duran
19348e64ca1SJose Luis Duran if (getremoteaddress(bi, &rss, &rsl) == -1)
19448e64ca1SJose Luis Duran goto out;
19548e64ca1SJose Luis Duran
19648e64ca1SJose Luis Duran if (debug || bi->bi_msg[0]) {
19748e64ca1SJose Luis Duran sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss);
19848e64ca1SJose Luis Duran (*lfun)(bi->bi_msg[0] ? LOG_INFO : LOG_DEBUG,
19948e64ca1SJose Luis Duran "processing type=%d fd=%d remote=%s msg=%s uid=%lu gid=%lu",
20048e64ca1SJose Luis Duran bi->bi_type, bi->bi_fd, rbuf,
20148e64ca1SJose Luis Duran bi->bi_msg, (unsigned long)bi->bi_uid,
20248e64ca1SJose Luis Duran (unsigned long)bi->bi_gid);
20348e64ca1SJose Luis Duran }
20448e64ca1SJose Luis Duran
20548e64ca1SJose Luis Duran if (conf_find(bi->bi_fd, bi->bi_uid, &rss, &c) == NULL) {
20648e64ca1SJose Luis Duran (*lfun)(LOG_DEBUG, "no rule matched");
20748e64ca1SJose Luis Duran goto out;
20848e64ca1SJose Luis Duran }
20948e64ca1SJose Luis Duran
21048e64ca1SJose Luis Duran
21148e64ca1SJose Luis Duran if (state_get(state, &c, &dbi) == -1)
21248e64ca1SJose Luis Duran goto out;
21348e64ca1SJose Luis Duran
21448e64ca1SJose Luis Duran if (debug) {
21548e64ca1SJose Luis Duran char b1[128], b2[128];
21648e64ca1SJose Luis Duran (*lfun)(LOG_DEBUG, "%s: initial db state for %s: count=%d/%d "
21748e64ca1SJose Luis Duran "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
21848e64ca1SJose Luis Duran fmttime(b1, sizeof(b1), dbi.last),
21948e64ca1SJose Luis Duran fmttime(b2, sizeof(b2), ts.tv_sec));
22048e64ca1SJose Luis Duran }
22148e64ca1SJose Luis Duran
22248e64ca1SJose Luis Duran switch (bi->bi_type) {
22348e64ca1SJose Luis Duran case BL_ABUSE:
22448e64ca1SJose Luis Duran /*
225*4d56eb00SJose Luis Duran * If the application has signaled abusive behavior,
226*4d56eb00SJose Luis Duran * set the number of fails to be one less than the
227*4d56eb00SJose Luis Duran * configured limit. Fallthrough to the normal BL_ADD
228*4d56eb00SJose Luis Duran * processing, which will increment the failure count
229*4d56eb00SJose Luis Duran * to the threshhold, and block the abusive address.
23048e64ca1SJose Luis Duran */
23148e64ca1SJose Luis Duran if (c.c_nfail != -1)
232*4d56eb00SJose Luis Duran dbi.count = c.c_nfail - 1;
23348e64ca1SJose Luis Duran /*FALLTHROUGH*/
23448e64ca1SJose Luis Duran case BL_ADD:
23548e64ca1SJose Luis Duran dbi.count++;
23648e64ca1SJose Luis Duran dbi.last = ts.tv_sec;
23748e64ca1SJose Luis Duran if (c.c_nfail != -1 && dbi.count >= c.c_nfail) {
23848e64ca1SJose Luis Duran /*
23948e64ca1SJose Luis Duran * No point in re-adding the rule.
24048e64ca1SJose Luis Duran * It might exist already due to latency in processing
24148e64ca1SJose Luis Duran * and removing the rule is the wrong thing to do as
24248e64ca1SJose Luis Duran * it allows a window to attack again.
24348e64ca1SJose Luis Duran */
24448e64ca1SJose Luis Duran if (dbi.id[0] == '\0') {
24548e64ca1SJose Luis Duran int res = run_change("add", &c,
24648e64ca1SJose Luis Duran dbi.id, sizeof(dbi.id));
24748e64ca1SJose Luis Duran if (res == -1)
24848e64ca1SJose Luis Duran goto out;
24948e64ca1SJose Luis Duran }
25048e64ca1SJose Luis Duran sockaddr_snprintf(rbuf, sizeof(rbuf), "%a",
25148e64ca1SJose Luis Duran (void *)&rss);
25248e64ca1SJose Luis Duran (*lfun)(LOG_INFO,
25348e64ca1SJose Luis Duran "blocked %s/%d:%d for %d seconds",
25448e64ca1SJose Luis Duran rbuf, c.c_lmask, c.c_port, c.c_duration);
25548e64ca1SJose Luis Duran }
25648e64ca1SJose Luis Duran break;
25748e64ca1SJose Luis Duran case BL_DELETE:
25848e64ca1SJose Luis Duran if (dbi.last == 0)
25948e64ca1SJose Luis Duran goto out;
26048e64ca1SJose Luis Duran dbi.count = 0;
26148e64ca1SJose Luis Duran dbi.last = 0;
26248e64ca1SJose Luis Duran break;
263*4d56eb00SJose Luis Duran case BL_BADUSER:
264*4d56eb00SJose Luis Duran /* ignore for now */
265*4d56eb00SJose Luis Duran break;
26648e64ca1SJose Luis Duran default:
26748e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "unknown message %d", bi->bi_type);
26848e64ca1SJose Luis Duran }
26948e64ca1SJose Luis Duran state_put(state, &c, &dbi);
27048e64ca1SJose Luis Duran
27148e64ca1SJose Luis Duran out:
27248e64ca1SJose Luis Duran close(bi->bi_fd);
27348e64ca1SJose Luis Duran
27448e64ca1SJose Luis Duran if (debug) {
27548e64ca1SJose Luis Duran char b1[128], b2[128];
27648e64ca1SJose Luis Duran (*lfun)(LOG_DEBUG, "%s: final db state for %s: count=%d/%d "
27748e64ca1SJose Luis Duran "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
27848e64ca1SJose Luis Duran fmttime(b1, sizeof(b1), dbi.last),
27948e64ca1SJose Luis Duran fmttime(b2, sizeof(b2), ts.tv_sec));
28048e64ca1SJose Luis Duran }
28148e64ca1SJose Luis Duran }
28248e64ca1SJose Luis Duran
28348e64ca1SJose Luis Duran static void
update_interfaces(void)28448e64ca1SJose Luis Duran update_interfaces(void)
28548e64ca1SJose Luis Duran {
28648e64ca1SJose Luis Duran struct ifaddrs *oifas, *nifas;
28748e64ca1SJose Luis Duran
28848e64ca1SJose Luis Duran if (getifaddrs(&nifas) == -1)
28948e64ca1SJose Luis Duran return;
29048e64ca1SJose Luis Duran
29148e64ca1SJose Luis Duran oifas = ifas;
29248e64ca1SJose Luis Duran ifas = nifas;
29348e64ca1SJose Luis Duran
29448e64ca1SJose Luis Duran if (oifas)
29548e64ca1SJose Luis Duran freeifaddrs(oifas);
29648e64ca1SJose Luis Duran }
29748e64ca1SJose Luis Duran
29848e64ca1SJose Luis Duran static void
update(void)29948e64ca1SJose Luis Duran update(void)
30048e64ca1SJose Luis Duran {
30148e64ca1SJose Luis Duran struct timespec ts;
30248e64ca1SJose Luis Duran struct conf c;
30348e64ca1SJose Luis Duran struct dbinfo dbi;
30448e64ca1SJose Luis Duran unsigned int f, n;
30548e64ca1SJose Luis Duran char buf[128];
30648e64ca1SJose Luis Duran void *ss = &c.c_ss;
30748e64ca1SJose Luis Duran
30848e64ca1SJose Luis Duran if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
30948e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "clock_gettime failed (%m)");
31048e64ca1SJose Luis Duran return;
31148e64ca1SJose Luis Duran }
31248e64ca1SJose Luis Duran
31348e64ca1SJose Luis Duran again:
31448e64ca1SJose Luis Duran for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1;
31548e64ca1SJose Luis Duran f = 0, n++)
31648e64ca1SJose Luis Duran {
31748e64ca1SJose Luis Duran time_t when = c.c_duration + dbi.last;
31848e64ca1SJose Luis Duran if (debug > 1) {
31948e64ca1SJose Luis Duran char b1[64], b2[64];
32048e64ca1SJose Luis Duran sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss);
32148e64ca1SJose Luis Duran (*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d "
32248e64ca1SJose Luis Duran "last=%s " "now=%s", __func__, n, buf, dbi.count,
32348e64ca1SJose Luis Duran c.c_duration, fmttime(b1, sizeof(b1), dbi.last),
32448e64ca1SJose Luis Duran fmttime(b2, sizeof(b2), ts.tv_sec));
32548e64ca1SJose Luis Duran }
32648e64ca1SJose Luis Duran if (c.c_duration == -1 || when >= ts.tv_sec)
32748e64ca1SJose Luis Duran continue;
32848e64ca1SJose Luis Duran if (dbi.id[0]) {
32948e64ca1SJose Luis Duran run_change("rem", &c, dbi.id, 0);
33048e64ca1SJose Luis Duran sockaddr_snprintf(buf, sizeof(buf), "%a", ss);
33148e64ca1SJose Luis Duran (*lfun)(LOG_INFO, "released %s/%d:%d after %d seconds",
33248e64ca1SJose Luis Duran buf, c.c_lmask, c.c_port, c.c_duration);
33348e64ca1SJose Luis Duran }
33448e64ca1SJose Luis Duran state_del(state, &c);
33548e64ca1SJose Luis Duran goto again;
33648e64ca1SJose Luis Duran }
33748e64ca1SJose Luis Duran }
33848e64ca1SJose Luis Duran
33948e64ca1SJose Luis Duran static void
addfd(struct pollfd ** pfdp,bl_t ** blp,size_t * nfd,size_t * maxfd,const char * path)34048e64ca1SJose Luis Duran addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd,
34148e64ca1SJose Luis Duran const char *path)
34248e64ca1SJose Luis Duran {
34348e64ca1SJose Luis Duran bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog_r);
34448e64ca1SJose Luis Duran if (bl == NULL || !bl_isconnected(bl))
34548e64ca1SJose Luis Duran exit(EXIT_FAILURE);
34648e64ca1SJose Luis Duran if (*nfd >= *maxfd) {
34748e64ca1SJose Luis Duran *maxfd += 10;
34848e64ca1SJose Luis Duran *blp = realloc(*blp, sizeof(**blp) * *maxfd);
34948e64ca1SJose Luis Duran if (*blp == NULL)
35048e64ca1SJose Luis Duran err(EXIT_FAILURE, "malloc");
35148e64ca1SJose Luis Duran *pfdp = realloc(*pfdp, sizeof(**pfdp) * *maxfd);
35248e64ca1SJose Luis Duran if (*pfdp == NULL)
35348e64ca1SJose Luis Duran err(EXIT_FAILURE, "malloc");
35448e64ca1SJose Luis Duran }
35548e64ca1SJose Luis Duran
35648e64ca1SJose Luis Duran (*pfdp)[*nfd].fd = bl_getfd(bl);
35748e64ca1SJose Luis Duran (*pfdp)[*nfd].events = POLLIN;
35848e64ca1SJose Luis Duran (*blp)[*nfd] = bl;
35948e64ca1SJose Luis Duran *nfd += 1;
36048e64ca1SJose Luis Duran }
36148e64ca1SJose Luis Duran
36248e64ca1SJose Luis Duran static void
uniqueadd(struct conf *** listp,size_t * nlist,size_t * mlist,struct conf * c)36348e64ca1SJose Luis Duran uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c)
36448e64ca1SJose Luis Duran {
36548e64ca1SJose Luis Duran struct conf **list = *listp;
36648e64ca1SJose Luis Duran
36748e64ca1SJose Luis Duran if (c->c_name[0] == '\0')
36848e64ca1SJose Luis Duran return;
36948e64ca1SJose Luis Duran for (size_t i = 0; i < *nlist; i++) {
37048e64ca1SJose Luis Duran if (strcmp(list[i]->c_name, c->c_name) == 0)
37148e64ca1SJose Luis Duran return;
37248e64ca1SJose Luis Duran }
37348e64ca1SJose Luis Duran if (*nlist == *mlist) {
37448e64ca1SJose Luis Duran *mlist += 10;
37548e64ca1SJose Luis Duran void *p = realloc(*listp, *mlist * sizeof(*list));
37648e64ca1SJose Luis Duran if (p == NULL)
37748e64ca1SJose Luis Duran err(EXIT_FAILURE, "Can't allocate for rule list");
37848e64ca1SJose Luis Duran list = *listp = p;
37948e64ca1SJose Luis Duran }
38048e64ca1SJose Luis Duran list[(*nlist)++] = c;
38148e64ca1SJose Luis Duran }
38248e64ca1SJose Luis Duran
38348e64ca1SJose Luis Duran static void
rules_flush(void)38448e64ca1SJose Luis Duran rules_flush(void)
38548e64ca1SJose Luis Duran {
38648e64ca1SJose Luis Duran struct conf **list;
38748e64ca1SJose Luis Duran size_t nlist, mlist;
38848e64ca1SJose Luis Duran
38948e64ca1SJose Luis Duran list = NULL;
39048e64ca1SJose Luis Duran mlist = nlist = 0;
39148e64ca1SJose Luis Duran for (size_t i = 0; i < rconf.cs_n; i++)
39248e64ca1SJose Luis Duran uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]);
39348e64ca1SJose Luis Duran for (size_t i = 0; i < lconf.cs_n; i++)
39448e64ca1SJose Luis Duran uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]);
39548e64ca1SJose Luis Duran
39648e64ca1SJose Luis Duran for (size_t i = 0; i < nlist; i++)
39748e64ca1SJose Luis Duran run_flush(list[i]);
39848e64ca1SJose Luis Duran free(list);
39948e64ca1SJose Luis Duran }
40048e64ca1SJose Luis Duran
40148e64ca1SJose Luis Duran static void
rules_restore(void)40248e64ca1SJose Luis Duran rules_restore(void)
40348e64ca1SJose Luis Duran {
40448e64ca1SJose Luis Duran DB *db;
40548e64ca1SJose Luis Duran struct conf c;
40648e64ca1SJose Luis Duran struct dbinfo dbi;
40748e64ca1SJose Luis Duran unsigned int f;
40848e64ca1SJose Luis Duran
40948e64ca1SJose Luis Duran db = state_open(dbfile, O_RDONLY, 0);
41048e64ca1SJose Luis Duran if (db == NULL) {
41148e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "Can't open `%s' to restore state (%m)",
41248e64ca1SJose Luis Duran dbfile);
41348e64ca1SJose Luis Duran return;
41448e64ca1SJose Luis Duran }
41548e64ca1SJose Luis Duran for (f = 1; state_iterate(db, &c, &dbi, f) == 1; f = 0) {
41648e64ca1SJose Luis Duran if (dbi.id[0] == '\0')
41748e64ca1SJose Luis Duran continue;
41848e64ca1SJose Luis Duran (void)run_change("add", &c, dbi.id, sizeof(dbi.id));
41948e64ca1SJose Luis Duran state_put(state, &c, &dbi);
42048e64ca1SJose Luis Duran }
42148e64ca1SJose Luis Duran state_close(db);
42248e64ca1SJose Luis Duran state_sync(state);
42348e64ca1SJose Luis Duran }
42448e64ca1SJose Luis Duran
42548e64ca1SJose Luis Duran int
main(int argc,char * argv[])42648e64ca1SJose Luis Duran main(int argc, char *argv[])
42748e64ca1SJose Luis Duran {
42848e64ca1SJose Luis Duran int c, tout, flags, flush, restore, ret;
42948e64ca1SJose Luis Duran const char *spath, **blsock;
43048e64ca1SJose Luis Duran size_t nblsock, maxblsock;
43148e64ca1SJose Luis Duran
43248e64ca1SJose Luis Duran setprogname(argv[0]);
43348e64ca1SJose Luis Duran
43448e64ca1SJose Luis Duran spath = NULL;
43548e64ca1SJose Luis Duran blsock = NULL;
43648e64ca1SJose Luis Duran maxblsock = nblsock = 0;
43748e64ca1SJose Luis Duran flush = 0;
43848e64ca1SJose Luis Duran restore = 0;
43948e64ca1SJose Luis Duran tout = 0;
44048e64ca1SJose Luis Duran flags = O_RDWR|O_EXCL|O_CLOEXEC;
44148e64ca1SJose Luis Duran while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) {
44248e64ca1SJose Luis Duran switch (c) {
44348e64ca1SJose Luis Duran case 'C':
44448e64ca1SJose Luis Duran controlprog = optarg;
44548e64ca1SJose Luis Duran break;
44648e64ca1SJose Luis Duran case 'c':
44748e64ca1SJose Luis Duran configfile = optarg;
44848e64ca1SJose Luis Duran break;
44948e64ca1SJose Luis Duran case 'D':
45048e64ca1SJose Luis Duran dbfile = optarg;
45148e64ca1SJose Luis Duran break;
45248e64ca1SJose Luis Duran case 'd':
45348e64ca1SJose Luis Duran debug++;
45448e64ca1SJose Luis Duran break;
45548e64ca1SJose Luis Duran case 'f':
45648e64ca1SJose Luis Duran flush++;
45748e64ca1SJose Luis Duran break;
45848e64ca1SJose Luis Duran case 'P':
45948e64ca1SJose Luis Duran spath = optarg;
46048e64ca1SJose Luis Duran break;
46148e64ca1SJose Luis Duran case 'R':
46248e64ca1SJose Luis Duran rulename = optarg;
46348e64ca1SJose Luis Duran break;
46448e64ca1SJose Luis Duran case 'r':
46548e64ca1SJose Luis Duran restore++;
46648e64ca1SJose Luis Duran break;
46748e64ca1SJose Luis Duran case 's':
46848e64ca1SJose Luis Duran if (nblsock >= maxblsock) {
46948e64ca1SJose Luis Duran maxblsock += 10;
47048e64ca1SJose Luis Duran void *p = realloc(blsock,
47148e64ca1SJose Luis Duran sizeof(*blsock) * maxblsock);
47248e64ca1SJose Luis Duran if (p == NULL)
47348e64ca1SJose Luis Duran err(EXIT_FAILURE,
47448e64ca1SJose Luis Duran "Can't allocate memory for %zu sockets",
47548e64ca1SJose Luis Duran maxblsock);
47648e64ca1SJose Luis Duran blsock = p;
47748e64ca1SJose Luis Duran }
47848e64ca1SJose Luis Duran blsock[nblsock++] = optarg;
47948e64ca1SJose Luis Duran break;
48048e64ca1SJose Luis Duran case 't':
48148e64ca1SJose Luis Duran tout = atoi(optarg) * 1000;
48248e64ca1SJose Luis Duran break;
48348e64ca1SJose Luis Duran case 'v':
48448e64ca1SJose Luis Duran vflag++;
48548e64ca1SJose Luis Duran break;
48648e64ca1SJose Luis Duran default:
48748e64ca1SJose Luis Duran usage(c);
48848e64ca1SJose Luis Duran }
48948e64ca1SJose Luis Duran }
49048e64ca1SJose Luis Duran
49148e64ca1SJose Luis Duran argc -= optind;
49248e64ca1SJose Luis Duran if (argc)
49348e64ca1SJose Luis Duran usage('?');
49448e64ca1SJose Luis Duran
49548e64ca1SJose Luis Duran signal(SIGHUP, sighup);
49648e64ca1SJose Luis Duran signal(SIGINT, sigdone);
49748e64ca1SJose Luis Duran signal(SIGQUIT, sigdone);
49848e64ca1SJose Luis Duran signal(SIGTERM, sigdone);
49948e64ca1SJose Luis Duran signal(SIGUSR1, sigusr1);
50048e64ca1SJose Luis Duran signal(SIGUSR2, sigusr2);
50148e64ca1SJose Luis Duran
50248e64ca1SJose Luis Duran openlog(getprogname(), LOG_PID, LOG_DAEMON);
50348e64ca1SJose Luis Duran
50448e64ca1SJose Luis Duran if (debug) {
50548e64ca1SJose Luis Duran lfun = dlog;
50648e64ca1SJose Luis Duran if (tout == 0)
50748e64ca1SJose Luis Duran tout = 5000;
50848e64ca1SJose Luis Duran } else {
50948e64ca1SJose Luis Duran if (tout == 0)
51048e64ca1SJose Luis Duran tout = 15000;
51148e64ca1SJose Luis Duran }
51248e64ca1SJose Luis Duran
51348e64ca1SJose Luis Duran update_interfaces();
51448e64ca1SJose Luis Duran conf_parse(configfile);
51548e64ca1SJose Luis Duran if (flush) {
51648e64ca1SJose Luis Duran rules_flush();
51748e64ca1SJose Luis Duran if (!restore)
51848e64ca1SJose Luis Duran flags |= O_TRUNC;
51948e64ca1SJose Luis Duran }
52048e64ca1SJose Luis Duran
52148e64ca1SJose Luis Duran struct pollfd *pfd = NULL;
52248e64ca1SJose Luis Duran bl_t *bl = NULL;
52348e64ca1SJose Luis Duran size_t nfd = 0;
52448e64ca1SJose Luis Duran size_t maxfd = 0;
52548e64ca1SJose Luis Duran
52648e64ca1SJose Luis Duran for (size_t i = 0; i < nblsock; i++)
52748e64ca1SJose Luis Duran addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]);
52848e64ca1SJose Luis Duran free(blsock);
52948e64ca1SJose Luis Duran
53048e64ca1SJose Luis Duran if (spath) {
53148e64ca1SJose Luis Duran FILE *fp = fopen(spath, "r");
53248e64ca1SJose Luis Duran char *line;
53348e64ca1SJose Luis Duran if (fp == NULL)
53448e64ca1SJose Luis Duran err(EXIT_FAILURE, "Can't open `%s'", spath);
53548e64ca1SJose Luis Duran for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL;
53648e64ca1SJose Luis Duran free(line))
53748e64ca1SJose Luis Duran addfd(&pfd, &bl, &nfd, &maxfd, line);
53848e64ca1SJose Luis Duran fclose(fp);
53948e64ca1SJose Luis Duran }
54048e64ca1SJose Luis Duran if (nfd == 0)
54148e64ca1SJose Luis Duran addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK);
54248e64ca1SJose Luis Duran
54348e64ca1SJose Luis Duran state = state_open(dbfile, flags, 0600);
54448e64ca1SJose Luis Duran if (state == NULL)
54548e64ca1SJose Luis Duran state = state_open(dbfile, flags | O_CREAT, 0600);
54648e64ca1SJose Luis Duran if (state == NULL)
54748e64ca1SJose Luis Duran return EXIT_FAILURE;
54848e64ca1SJose Luis Duran
54948e64ca1SJose Luis Duran if (restore) {
55048e64ca1SJose Luis Duran if (!flush)
55148e64ca1SJose Luis Duran rules_flush();
55248e64ca1SJose Luis Duran rules_restore();
55348e64ca1SJose Luis Duran }
55448e64ca1SJose Luis Duran
55548e64ca1SJose Luis Duran if (!debug) {
55648e64ca1SJose Luis Duran if (daemon(0, 0) == -1)
55748e64ca1SJose Luis Duran err(EXIT_FAILURE, "daemon failed");
55848e64ca1SJose Luis Duran if (pidfile(NULL) == -1)
55948e64ca1SJose Luis Duran err(EXIT_FAILURE, "Can't create pidfile");
56048e64ca1SJose Luis Duran }
56148e64ca1SJose Luis Duran
56248e64ca1SJose Luis Duran for (size_t t = 0; !done; t++) {
56348e64ca1SJose Luis Duran if (readconf) {
56448e64ca1SJose Luis Duran readconf = 0;
56548e64ca1SJose Luis Duran conf_parse(configfile);
56648e64ca1SJose Luis Duran }
56748e64ca1SJose Luis Duran ret = poll(pfd, (nfds_t)nfd, tout);
56848e64ca1SJose Luis Duran if (debug)
56948e64ca1SJose Luis Duran (*lfun)(LOG_DEBUG, "received %d from poll()", ret);
57048e64ca1SJose Luis Duran switch (ret) {
57148e64ca1SJose Luis Duran case -1:
57248e64ca1SJose Luis Duran if (errno == EINTR)
57348e64ca1SJose Luis Duran continue;
57448e64ca1SJose Luis Duran (*lfun)(LOG_ERR, "poll (%m)");
57548e64ca1SJose Luis Duran return EXIT_FAILURE;
57648e64ca1SJose Luis Duran case 0:
57748e64ca1SJose Luis Duran state_sync(state);
57848e64ca1SJose Luis Duran break;
57948e64ca1SJose Luis Duran default:
58048e64ca1SJose Luis Duran for (size_t i = 0; i < nfd; i++)
58148e64ca1SJose Luis Duran if (pfd[i].revents & POLLIN)
58248e64ca1SJose Luis Duran process(bl[i]);
58348e64ca1SJose Luis Duran }
58448e64ca1SJose Luis Duran if (t % 100 == 0)
58548e64ca1SJose Luis Duran state_sync(state);
58648e64ca1SJose Luis Duran if (t % 10000 == 0)
58748e64ca1SJose Luis Duran update_interfaces();
58848e64ca1SJose Luis Duran update();
58948e64ca1SJose Luis Duran }
59048e64ca1SJose Luis Duran state_close(state);
59148e64ca1SJose Luis Duran return 0;
59248e64ca1SJose Luis Duran }
593