xref: /freebsd/contrib/blocklist/bin/blocklistd.c (revision 4d56eb007b18881becb2107f87bd2a7edca3e6bf)
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