xref: /freebsd/contrib/blocklist/bin/blacklistd.c (revision 5f4c09dd)
15f4c09ddSEd Maste /*	$NetBSD: blacklistd.c,v 1.38 2019/02/27 02:20:18 christos Exp $	*/
25f4c09ddSEd Maste 
35f4c09ddSEd Maste /*-
45f4c09ddSEd Maste  * Copyright (c) 2015 The NetBSD Foundation, Inc.
55f4c09ddSEd Maste  * All rights reserved.
65f4c09ddSEd Maste  *
75f4c09ddSEd Maste  * This code is derived from software contributed to The NetBSD Foundation
85f4c09ddSEd Maste  * by Christos Zoulas.
95f4c09ddSEd Maste  *
105f4c09ddSEd Maste  * Redistribution and use in source and binary forms, with or without
115f4c09ddSEd Maste  * modification, are permitted provided that the following conditions
125f4c09ddSEd Maste  * are met:
135f4c09ddSEd Maste  * 1. Redistributions of source code must retain the above copyright
145f4c09ddSEd Maste  *    notice, this list of conditions and the following disclaimer.
155f4c09ddSEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
165f4c09ddSEd Maste  *    notice, this list of conditions and the following disclaimer in the
175f4c09ddSEd Maste  *    documentation and/or other materials provided with the distribution.
185f4c09ddSEd Maste  *
195f4c09ddSEd Maste  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
205f4c09ddSEd Maste  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
215f4c09ddSEd Maste  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
225f4c09ddSEd Maste  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
235f4c09ddSEd Maste  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
245f4c09ddSEd Maste  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
255f4c09ddSEd Maste  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265f4c09ddSEd Maste  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
275f4c09ddSEd Maste  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
285f4c09ddSEd Maste  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
295f4c09ddSEd Maste  * POSSIBILITY OF SUCH DAMAGE.
305f4c09ddSEd Maste  */
315f4c09ddSEd Maste #ifdef HAVE_CONFIG_H
325f4c09ddSEd Maste #include "config.h"
335f4c09ddSEd Maste #endif
345f4c09ddSEd Maste #include <sys/cdefs.h>
355f4c09ddSEd Maste __RCSID("$NetBSD: blacklistd.c,v 1.38 2019/02/27 02:20:18 christos Exp $");
365f4c09ddSEd Maste 
375f4c09ddSEd Maste #include <sys/types.h>
385f4c09ddSEd Maste #include <sys/socket.h>
395f4c09ddSEd Maste #include <sys/queue.h>
405f4c09ddSEd Maste 
415f4c09ddSEd Maste #ifdef HAVE_LIBUTIL_H
425f4c09ddSEd Maste #include <libutil.h>
435f4c09ddSEd Maste #endif
445f4c09ddSEd Maste #ifdef HAVE_UTIL_H
455f4c09ddSEd Maste #include <util.h>
465f4c09ddSEd Maste #endif
475f4c09ddSEd Maste #include <string.h>
485f4c09ddSEd Maste #include <signal.h>
495f4c09ddSEd Maste #include <netdb.h>
505f4c09ddSEd Maste #include <stdio.h>
515f4c09ddSEd Maste #include <stdbool.h>
525f4c09ddSEd Maste #include <string.h>
535f4c09ddSEd Maste #include <inttypes.h>
545f4c09ddSEd Maste #include <syslog.h>
555f4c09ddSEd Maste #include <ctype.h>
565f4c09ddSEd Maste #include <limits.h>
575f4c09ddSEd Maste #include <errno.h>
585f4c09ddSEd Maste #include <poll.h>
595f4c09ddSEd Maste #include <fcntl.h>
605f4c09ddSEd Maste #include <err.h>
615f4c09ddSEd Maste #include <stdlib.h>
625f4c09ddSEd Maste #include <unistd.h>
635f4c09ddSEd Maste #include <time.h>
645f4c09ddSEd Maste #include <ifaddrs.h>
655f4c09ddSEd Maste #include <netinet/in.h>
665f4c09ddSEd Maste 
675f4c09ddSEd Maste #include "bl.h"
685f4c09ddSEd Maste #include "internal.h"
695f4c09ddSEd Maste #include "conf.h"
705f4c09ddSEd Maste #include "run.h"
715f4c09ddSEd Maste #include "state.h"
725f4c09ddSEd Maste #include "support.h"
735f4c09ddSEd Maste 
745f4c09ddSEd Maste static const char *configfile = _PATH_BLCONF;
755f4c09ddSEd Maste static DB *state;
765f4c09ddSEd Maste static const char *dbfile = _PATH_BLSTATE;
775f4c09ddSEd Maste static sig_atomic_t readconf;
785f4c09ddSEd Maste static sig_atomic_t done;
795f4c09ddSEd Maste static int vflag;
805f4c09ddSEd Maste 
815f4c09ddSEd Maste static void
sigusr1(int n __unused)825f4c09ddSEd Maste sigusr1(int n __unused)
835f4c09ddSEd Maste {
845f4c09ddSEd Maste 	debug++;
855f4c09ddSEd Maste }
865f4c09ddSEd Maste 
875f4c09ddSEd Maste static void
sigusr2(int n __unused)885f4c09ddSEd Maste sigusr2(int n __unused)
895f4c09ddSEd Maste {
905f4c09ddSEd Maste 	debug--;
915f4c09ddSEd Maste }
925f4c09ddSEd Maste 
935f4c09ddSEd Maste static void
sighup(int n __unused)945f4c09ddSEd Maste sighup(int n __unused)
955f4c09ddSEd Maste {
965f4c09ddSEd Maste 	readconf++;
975f4c09ddSEd Maste }
985f4c09ddSEd Maste 
995f4c09ddSEd Maste static void
sigdone(int n __unused)1005f4c09ddSEd Maste sigdone(int n __unused)
1015f4c09ddSEd Maste {
1025f4c09ddSEd Maste 	done++;
1035f4c09ddSEd Maste }
1045f4c09ddSEd Maste 
1055f4c09ddSEd Maste static __dead void
usage(int c)1065f4c09ddSEd Maste usage(int c)
1075f4c09ddSEd Maste {
1085f4c09ddSEd Maste 	if (c != '?')
1095f4c09ddSEd Maste 		warnx("Unknown option `%c'", (char)c);
1105f4c09ddSEd Maste 	fprintf(stderr, "Usage: %s [-vdfr] [-c <config>] [-R <rulename>] "
1115f4c09ddSEd Maste 	    "[-P <sockpathsfile>] [-C <controlprog>] [-D <dbfile>] "
1125f4c09ddSEd Maste 	    "[-s <sockpath>] [-t <timeout>]\n", getprogname());
1135f4c09ddSEd Maste 	exit(EXIT_FAILURE);
1145f4c09ddSEd Maste }
1155f4c09ddSEd Maste 
1165f4c09ddSEd Maste static int
getremoteaddress(bl_info_t * bi,struct sockaddr_storage * rss,socklen_t * rsl)1175f4c09ddSEd Maste getremoteaddress(bl_info_t *bi, struct sockaddr_storage *rss, socklen_t *rsl)
1185f4c09ddSEd Maste {
1195f4c09ddSEd Maste 	*rsl = sizeof(*rss);
1205f4c09ddSEd Maste 	memset(rss, 0, *rsl);
1215f4c09ddSEd Maste 
1225f4c09ddSEd Maste 	if (getpeername(bi->bi_fd, (void *)rss, rsl) != -1)
1235f4c09ddSEd Maste 		return 0;
1245f4c09ddSEd Maste 
1255f4c09ddSEd Maste 	if (errno != ENOTCONN) {
1265f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "getpeername failed (%m)");
1275f4c09ddSEd Maste 		return -1;
1285f4c09ddSEd Maste 	}
1295f4c09ddSEd Maste 
1305f4c09ddSEd Maste 	if (bi->bi_slen == 0) {
1315f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "unconnected socket with no peer in message");
1325f4c09ddSEd Maste 		return -1;
1335f4c09ddSEd Maste 	}
1345f4c09ddSEd Maste 
1355f4c09ddSEd Maste 	switch (bi->bi_ss.ss_family) {
1365f4c09ddSEd Maste 	case AF_INET:
1375f4c09ddSEd Maste 		*rsl = sizeof(struct sockaddr_in);
1385f4c09ddSEd Maste 		break;
1395f4c09ddSEd Maste 	case AF_INET6:
1405f4c09ddSEd Maste 		*rsl = sizeof(struct sockaddr_in6);
1415f4c09ddSEd Maste 		break;
1425f4c09ddSEd Maste 	default:
1435f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "bad client passed socket family %u",
1445f4c09ddSEd Maste 		    (unsigned)bi->bi_ss.ss_family);
1455f4c09ddSEd Maste 		return -1;
1465f4c09ddSEd Maste 	}
1475f4c09ddSEd Maste 
1485f4c09ddSEd Maste 	if (*rsl != bi->bi_slen) {
1495f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "bad client passed socket length %u != %u",
1505f4c09ddSEd Maste 		    (unsigned)*rsl, (unsigned)bi->bi_slen);
1515f4c09ddSEd Maste 		return -1;
1525f4c09ddSEd Maste 	}
1535f4c09ddSEd Maste 
1545f4c09ddSEd Maste 	memcpy(rss, &bi->bi_ss, *rsl);
1555f4c09ddSEd Maste 
1565f4c09ddSEd Maste #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1575f4c09ddSEd Maste 	if (*rsl != rss->ss_len) {
1585f4c09ddSEd Maste 		(*lfun)(LOG_ERR,
1595f4c09ddSEd Maste 		    "bad client passed socket internal length %u != %u",
1605f4c09ddSEd Maste 		    (unsigned)*rsl, (unsigned)rss->ss_len);
1615f4c09ddSEd Maste 		return -1;
1625f4c09ddSEd Maste 	}
1635f4c09ddSEd Maste #endif
1645f4c09ddSEd Maste 	return 0;
1655f4c09ddSEd Maste }
1665f4c09ddSEd Maste 
1675f4c09ddSEd Maste static void
process(bl_t bl)1685f4c09ddSEd Maste process(bl_t bl)
1695f4c09ddSEd Maste {
1705f4c09ddSEd Maste 	struct sockaddr_storage rss;
1715f4c09ddSEd Maste 	socklen_t rsl;
1725f4c09ddSEd Maste 	char rbuf[BUFSIZ];
1735f4c09ddSEd Maste 	bl_info_t *bi;
1745f4c09ddSEd Maste 	struct conf c;
1755f4c09ddSEd Maste 	struct dbinfo dbi;
1765f4c09ddSEd Maste 	struct timespec ts;
1775f4c09ddSEd Maste 
1785f4c09ddSEd Maste 	if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
1795f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "clock_gettime failed (%m)");
1805f4c09ddSEd Maste 		return;
1815f4c09ddSEd Maste 	}
1825f4c09ddSEd Maste 
1835f4c09ddSEd Maste 	if ((bi = bl_recv(bl)) == NULL) {
1845f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "no message (%m)");
1855f4c09ddSEd Maste 		return;
1865f4c09ddSEd Maste 	}
1875f4c09ddSEd Maste 
1885f4c09ddSEd Maste 	if (getremoteaddress(bi, &rss, &rsl) == -1)
1895f4c09ddSEd Maste 		goto out;
1905f4c09ddSEd Maste 
1915f4c09ddSEd Maste 	if (debug) {
1925f4c09ddSEd Maste 		sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss);
1935f4c09ddSEd Maste 		(*lfun)(LOG_DEBUG, "processing type=%d fd=%d remote=%s msg=%s"
1945f4c09ddSEd Maste 		    " uid=%lu gid=%lu", bi->bi_type, bi->bi_fd, rbuf,
1955f4c09ddSEd Maste 		    bi->bi_msg, (unsigned long)bi->bi_uid,
1965f4c09ddSEd Maste 		    (unsigned long)bi->bi_gid);
1975f4c09ddSEd Maste 	}
1985f4c09ddSEd Maste 
1995f4c09ddSEd Maste 	if (conf_find(bi->bi_fd, bi->bi_uid, &rss, &c) == NULL) {
2005f4c09ddSEd Maste 		(*lfun)(LOG_DEBUG, "no rule matched");
2015f4c09ddSEd Maste 		goto out;
2025f4c09ddSEd Maste 	}
2035f4c09ddSEd Maste 
2045f4c09ddSEd Maste 
2055f4c09ddSEd Maste 	if (state_get(state, &c, &dbi) == -1)
2065f4c09ddSEd Maste 		goto out;
2075f4c09ddSEd Maste 
2085f4c09ddSEd Maste 	if (debug) {
2095f4c09ddSEd Maste 		char b1[128], b2[128];
2105f4c09ddSEd Maste 		(*lfun)(LOG_DEBUG, "%s: initial db state for %s: count=%d/%d "
2115f4c09ddSEd Maste 		    "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
2125f4c09ddSEd Maste 		    fmttime(b1, sizeof(b1), dbi.last),
2135f4c09ddSEd Maste 		    fmttime(b2, sizeof(b2), ts.tv_sec));
2145f4c09ddSEd Maste 	}
2155f4c09ddSEd Maste 
2165f4c09ddSEd Maste 	switch (bi->bi_type) {
2175f4c09ddSEd Maste 	case BL_ABUSE:
2185f4c09ddSEd Maste 		/*
2195f4c09ddSEd Maste 		 * If the application has signaled abusive behavior,
2205f4c09ddSEd Maste 		 * set the number of fails to be one less than the
2215f4c09ddSEd Maste 		 * configured limit.  Fallthrough to the normal BL_ADD
2225f4c09ddSEd Maste 		 * processing, which will increment the failure count
2235f4c09ddSEd Maste 		 * to the threshhold, and block the abusive address.
2245f4c09ddSEd Maste 		 */
2255f4c09ddSEd Maste 		if (c.c_nfail != -1)
2265f4c09ddSEd Maste 			dbi.count = c.c_nfail - 1;
2275f4c09ddSEd Maste 		/*FALLTHROUGH*/
2285f4c09ddSEd Maste 	case BL_ADD:
2295f4c09ddSEd Maste 		dbi.count++;
2305f4c09ddSEd Maste 		dbi.last = ts.tv_sec;
2315f4c09ddSEd Maste 		if (c.c_nfail != -1 && dbi.count >= c.c_nfail) {
2325f4c09ddSEd Maste 			/*
2335f4c09ddSEd Maste 			 * No point in re-adding the rule.
2345f4c09ddSEd Maste 			 * It might exist already due to latency in processing
2355f4c09ddSEd Maste 			 * and removing the rule is the wrong thing to do as
2365f4c09ddSEd Maste 			 * it allows a window to attack again.
2375f4c09ddSEd Maste 			 */
2385f4c09ddSEd Maste 			if (dbi.id[0] == '\0') {
2395f4c09ddSEd Maste 				int res = run_change("add", &c,
2405f4c09ddSEd Maste 				    dbi.id, sizeof(dbi.id));
2415f4c09ddSEd Maste 				if (res == -1)
2425f4c09ddSEd Maste 					goto out;
2435f4c09ddSEd Maste 			}
2445f4c09ddSEd Maste 			sockaddr_snprintf(rbuf, sizeof(rbuf), "%a",
2455f4c09ddSEd Maste 			    (void *)&rss);
2465f4c09ddSEd Maste 			(*lfun)(LOG_INFO,
2475f4c09ddSEd Maste 			    "blocked %s/%d:%d for %d seconds",
2485f4c09ddSEd Maste 			    rbuf, c.c_lmask, c.c_port, c.c_duration);
2495f4c09ddSEd Maste 		}
2505f4c09ddSEd Maste 		break;
2515f4c09ddSEd Maste 	case BL_DELETE:
2525f4c09ddSEd Maste 		if (dbi.last == 0)
2535f4c09ddSEd Maste 			goto out;
2545f4c09ddSEd Maste 		dbi.count = 0;
2555f4c09ddSEd Maste 		dbi.last = 0;
2565f4c09ddSEd Maste 		break;
2575f4c09ddSEd Maste 	case BL_BADUSER:
2585f4c09ddSEd Maste 		/* ignore for now */
2595f4c09ddSEd Maste 		break;
2605f4c09ddSEd Maste 	default:
2615f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "unknown message %d", bi->bi_type);
2625f4c09ddSEd Maste 	}
2635f4c09ddSEd Maste 	state_put(state, &c, &dbi);
2645f4c09ddSEd Maste 
2655f4c09ddSEd Maste out:
2665f4c09ddSEd Maste 	close(bi->bi_fd);
2675f4c09ddSEd Maste 
2685f4c09ddSEd Maste 	if (debug) {
2695f4c09ddSEd Maste 		char b1[128], b2[128];
2705f4c09ddSEd Maste 		(*lfun)(LOG_DEBUG, "%s: final db state for %s: count=%d/%d "
2715f4c09ddSEd Maste 		    "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
2725f4c09ddSEd Maste 		    fmttime(b1, sizeof(b1), dbi.last),
2735f4c09ddSEd Maste 		    fmttime(b2, sizeof(b2), ts.tv_sec));
2745f4c09ddSEd Maste 	}
2755f4c09ddSEd Maste }
2765f4c09ddSEd Maste 
2775f4c09ddSEd Maste static void
update_interfaces(void)2785f4c09ddSEd Maste update_interfaces(void)
2795f4c09ddSEd Maste {
2805f4c09ddSEd Maste 	struct ifaddrs *oifas, *nifas;
2815f4c09ddSEd Maste 
2825f4c09ddSEd Maste 	if (getifaddrs(&nifas) == -1)
2835f4c09ddSEd Maste 		return;
2845f4c09ddSEd Maste 
2855f4c09ddSEd Maste 	oifas = ifas;
2865f4c09ddSEd Maste 	ifas = nifas;
2875f4c09ddSEd Maste 
2885f4c09ddSEd Maste 	if (oifas)
2895f4c09ddSEd Maste 		freeifaddrs(oifas);
2905f4c09ddSEd Maste }
2915f4c09ddSEd Maste 
2925f4c09ddSEd Maste static void
update(void)2935f4c09ddSEd Maste update(void)
2945f4c09ddSEd Maste {
2955f4c09ddSEd Maste 	struct timespec ts;
2965f4c09ddSEd Maste 	struct conf c;
2975f4c09ddSEd Maste 	struct dbinfo dbi;
2985f4c09ddSEd Maste 	unsigned int f, n;
2995f4c09ddSEd Maste 	char buf[128];
3005f4c09ddSEd Maste 	void *ss = &c.c_ss;
3015f4c09ddSEd Maste 
3025f4c09ddSEd Maste 	if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
3035f4c09ddSEd Maste 		(*lfun)(LOG_ERR, "clock_gettime failed (%m)");
3045f4c09ddSEd Maste 		return;
3055f4c09ddSEd Maste 	}
3065f4c09ddSEd Maste 
3075f4c09ddSEd Maste again:
3085f4c09ddSEd Maste 	for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1;
3095f4c09ddSEd Maste 	    f = 0, n++)
3105f4c09ddSEd Maste 	{
3115f4c09ddSEd Maste 		time_t when = c.c_duration + dbi.last;
3125f4c09ddSEd Maste 		if (debug > 1) {
3135f4c09ddSEd Maste 			char b1[64], b2[64];
3145f4c09ddSEd Maste 			sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss);
3155f4c09ddSEd Maste 			(*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d "
3165f4c09ddSEd Maste 			    "last=%s " "now=%s", __func__, n, buf, dbi.count,
3175f4c09ddSEd Maste 			    c.c_duration, fmttime(b1, sizeof(b1), dbi.last),
3185f4c09ddSEd Maste 			    fmttime(b2, sizeof(b2), ts.tv_sec));
3195f4c09ddSEd Maste 		}
3205f4c09ddSEd Maste 		if (c.c_duration == -1 || when >= ts.tv_sec)
3215f4c09ddSEd Maste 			continue;
3225f4c09ddSEd Maste 		if (dbi.id[0]) {
3235f4c09ddSEd Maste 			run_change("rem", &c, dbi.id, 0);
3245f4c09ddSEd Maste 			sockaddr_snprintf(buf, sizeof(buf), "%a", ss);
3255f4c09ddSEd Maste 			(*lfun)(LOG_INFO, "released %s/%d:%d after %d seconds",
3265f4c09ddSEd Maste 			    buf, c.c_lmask, c.c_port, c.c_duration);
3275f4c09ddSEd Maste 		}
3285f4c09ddSEd Maste 		state_del(state, &c);
3295f4c09ddSEd Maste 		goto again;
3305f4c09ddSEd Maste 	}
3315f4c09ddSEd Maste }
3325f4c09ddSEd Maste 
3335f4c09ddSEd Maste static void
addfd(struct pollfd ** pfdp,bl_t ** blp,size_t * nfd,size_t * maxfd,const char * path)3345f4c09ddSEd Maste addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd,
3355f4c09ddSEd Maste     const char *path)
3365f4c09ddSEd Maste {
3375f4c09ddSEd Maste 	bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog);
3385f4c09ddSEd Maste 	if (bl == NULL || !bl_isconnected(bl))
3395f4c09ddSEd Maste 		exit(EXIT_FAILURE);
3405f4c09ddSEd Maste 	if (*nfd >= *maxfd) {
3415f4c09ddSEd Maste 		*maxfd += 10;
3425f4c09ddSEd Maste 		*blp = realloc(*blp, sizeof(**blp) * *maxfd);
3435f4c09ddSEd Maste 		if (*blp == NULL)
3445f4c09ddSEd Maste 			err(EXIT_FAILURE, "malloc");
3455f4c09ddSEd Maste 		*pfdp = realloc(*pfdp, sizeof(**pfdp) * *maxfd);
3465f4c09ddSEd Maste 		if (*pfdp == NULL)
3475f4c09ddSEd Maste 			err(EXIT_FAILURE, "malloc");
3485f4c09ddSEd Maste 	}
3495f4c09ddSEd Maste 
3505f4c09ddSEd Maste 	(*pfdp)[*nfd].fd = bl_getfd(bl);
3515f4c09ddSEd Maste 	(*pfdp)[*nfd].events = POLLIN;
3525f4c09ddSEd Maste 	(*blp)[*nfd] = bl;
3535f4c09ddSEd Maste 	*nfd += 1;
3545f4c09ddSEd Maste }
3555f4c09ddSEd Maste 
3565f4c09ddSEd Maste static void
uniqueadd(struct conf *** listp,size_t * nlist,size_t * mlist,struct conf * c)3575f4c09ddSEd Maste uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c)
3585f4c09ddSEd Maste {
3595f4c09ddSEd Maste 	struct conf **list = *listp;
3605f4c09ddSEd Maste 
3615f4c09ddSEd Maste 	if (c->c_name[0] == '\0')
3625f4c09ddSEd Maste 		return;
3635f4c09ddSEd Maste 	for (size_t i = 0; i < *nlist; i++) {
3645f4c09ddSEd Maste 		if (strcmp(list[i]->c_name, c->c_name) == 0)
3655f4c09ddSEd Maste 			return;
3665f4c09ddSEd Maste 	}
3675f4c09ddSEd Maste 	if (*nlist == *mlist) {
3685f4c09ddSEd Maste 		*mlist += 10;
3695f4c09ddSEd Maste 		void *p = realloc(*listp, *mlist * sizeof(*list));
3705f4c09ddSEd Maste 		if (p == NULL)
3715f4c09ddSEd Maste 			err(EXIT_FAILURE, "Can't allocate for rule list");
3725f4c09ddSEd Maste 		list = *listp = p;
3735f4c09ddSEd Maste 	}
3745f4c09ddSEd Maste 	list[(*nlist)++] = c;
3755f4c09ddSEd Maste }
3765f4c09ddSEd Maste 
3775f4c09ddSEd Maste static void
rules_flush(void)3785f4c09ddSEd Maste rules_flush(void)
3795f4c09ddSEd Maste {
3805f4c09ddSEd Maste 	struct conf **list;
3815f4c09ddSEd Maste 	size_t nlist, mlist;
3825f4c09ddSEd Maste 
3835f4c09ddSEd Maste 	list = NULL;
3845f4c09ddSEd Maste 	mlist = nlist = 0;
3855f4c09ddSEd Maste 	for (size_t i = 0; i < rconf.cs_n; i++)
3865f4c09ddSEd Maste 		uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]);
3875f4c09ddSEd Maste 	for (size_t i = 0; i < lconf.cs_n; i++)
3885f4c09ddSEd Maste 		uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]);
3895f4c09ddSEd Maste 
3905f4c09ddSEd Maste 	for (size_t i = 0; i < nlist; i++)
3915f4c09ddSEd Maste 		run_flush(list[i]);
3925f4c09ddSEd Maste 	free(list);
3935f4c09ddSEd Maste }
3945f4c09ddSEd Maste 
3955f4c09ddSEd Maste static void
rules_restore(void)3965f4c09ddSEd Maste rules_restore(void)
3975f4c09ddSEd Maste {
3985f4c09ddSEd Maste 	struct conf c;
3995f4c09ddSEd Maste 	struct dbinfo dbi;
4005f4c09ddSEd Maste 	unsigned int f;
4015f4c09ddSEd Maste 
4025f4c09ddSEd Maste 	for (f = 1; state_iterate(state, &c, &dbi, f) == 1; f = 0) {
4035f4c09ddSEd Maste 		if (dbi.id[0] == '\0')
4045f4c09ddSEd Maste 			continue;
4055f4c09ddSEd Maste 		(void)run_change("add", &c, dbi.id, sizeof(dbi.id));
4065f4c09ddSEd Maste 	}
4075f4c09ddSEd Maste }
4085f4c09ddSEd Maste 
4095f4c09ddSEd Maste int
main(int argc,char * argv[])4105f4c09ddSEd Maste main(int argc, char *argv[])
4115f4c09ddSEd Maste {
4125f4c09ddSEd Maste 	int c, tout, flags, flush, restore, ret;
4135f4c09ddSEd Maste 	const char *spath, **blsock;
4145f4c09ddSEd Maste 	size_t nblsock, maxblsock;
4155f4c09ddSEd Maste 
4165f4c09ddSEd Maste 	setprogname(argv[0]);
4175f4c09ddSEd Maste 
4185f4c09ddSEd Maste 	spath = NULL;
4195f4c09ddSEd Maste 	blsock = NULL;
4205f4c09ddSEd Maste 	maxblsock = nblsock = 0;
4215f4c09ddSEd Maste 	flush = 0;
4225f4c09ddSEd Maste 	restore = 0;
4235f4c09ddSEd Maste 	tout = 0;
4245f4c09ddSEd Maste 	flags = O_RDWR|O_EXCL|O_CLOEXEC;
4255f4c09ddSEd Maste 	while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) {
4265f4c09ddSEd Maste 		switch (c) {
4275f4c09ddSEd Maste 		case 'C':
4285f4c09ddSEd Maste 			controlprog = optarg;
4295f4c09ddSEd Maste 			break;
4305f4c09ddSEd Maste 		case 'c':
4315f4c09ddSEd Maste 			configfile = optarg;
4325f4c09ddSEd Maste 			break;
4335f4c09ddSEd Maste 		case 'D':
4345f4c09ddSEd Maste 			dbfile = optarg;
4355f4c09ddSEd Maste 			break;
4365f4c09ddSEd Maste 		case 'd':
4375f4c09ddSEd Maste 			debug++;
4385f4c09ddSEd Maste 			break;
4395f4c09ddSEd Maste 		case 'f':
4405f4c09ddSEd Maste 			flush++;
4415f4c09ddSEd Maste 			break;
4425f4c09ddSEd Maste 		case 'P':
4435f4c09ddSEd Maste 			spath = optarg;
4445f4c09ddSEd Maste 			break;
4455f4c09ddSEd Maste 		case 'R':
4465f4c09ddSEd Maste 			rulename = optarg;
4475f4c09ddSEd Maste 			break;
4485f4c09ddSEd Maste 		case 'r':
4495f4c09ddSEd Maste 			restore++;
4505f4c09ddSEd Maste 			break;
4515f4c09ddSEd Maste 		case 's':
4525f4c09ddSEd Maste 			if (nblsock >= maxblsock) {
4535f4c09ddSEd Maste 				maxblsock += 10;
4545f4c09ddSEd Maste 				void *p = realloc(blsock,
4555f4c09ddSEd Maste 				    sizeof(*blsock) * maxblsock);
4565f4c09ddSEd Maste 				if (p == NULL)
4575f4c09ddSEd Maste 				    err(EXIT_FAILURE,
4585f4c09ddSEd Maste 					"Can't allocate memory for %zu sockets",
4595f4c09ddSEd Maste 					maxblsock);
4605f4c09ddSEd Maste 				blsock = p;
4615f4c09ddSEd Maste 			}
4625f4c09ddSEd Maste 			blsock[nblsock++] = optarg;
4635f4c09ddSEd Maste 			break;
4645f4c09ddSEd Maste 		case 't':
4655f4c09ddSEd Maste 			tout = atoi(optarg) * 1000;
4665f4c09ddSEd Maste 			break;
4675f4c09ddSEd Maste 		case 'v':
4685f4c09ddSEd Maste 			vflag++;
4695f4c09ddSEd Maste 			break;
4705f4c09ddSEd Maste 		default:
4715f4c09ddSEd Maste 			usage(c);
4725f4c09ddSEd Maste 		}
4735f4c09ddSEd Maste 	}
4745f4c09ddSEd Maste 
4755f4c09ddSEd Maste 	argc -= optind;
4765f4c09ddSEd Maste 	if (argc)
4775f4c09ddSEd Maste 		usage('?');
4785f4c09ddSEd Maste 
4795f4c09ddSEd Maste 	signal(SIGHUP, sighup);
4805f4c09ddSEd Maste 	signal(SIGINT, sigdone);
4815f4c09ddSEd Maste 	signal(SIGQUIT, sigdone);
4825f4c09ddSEd Maste 	signal(SIGTERM, sigdone);
4835f4c09ddSEd Maste 	signal(SIGUSR1, sigusr1);
4845f4c09ddSEd Maste 	signal(SIGUSR2, sigusr2);
4855f4c09ddSEd Maste 
4865f4c09ddSEd Maste 	openlog(getprogname(), LOG_PID, LOG_DAEMON);
4875f4c09ddSEd Maste 
4885f4c09ddSEd Maste 	if (debug) {
4895f4c09ddSEd Maste 		lfun = dlog;
4905f4c09ddSEd Maste 		if (tout == 0)
4915f4c09ddSEd Maste 			tout = 5000;
4925f4c09ddSEd Maste 	} else {
4935f4c09ddSEd Maste 		if (tout == 0)
4945f4c09ddSEd Maste 			tout = 15000;
4955f4c09ddSEd Maste 	}
4965f4c09ddSEd Maste 
4975f4c09ddSEd Maste 	update_interfaces();
4985f4c09ddSEd Maste 	conf_parse(configfile);
4995f4c09ddSEd Maste 	if (flush) {
5005f4c09ddSEd Maste 		rules_flush();
5015f4c09ddSEd Maste 		if (!restore)
5025f4c09ddSEd Maste 			flags |= O_TRUNC;
5035f4c09ddSEd Maste 	}
5045f4c09ddSEd Maste 
5055f4c09ddSEd Maste 	struct pollfd *pfd = NULL;
5065f4c09ddSEd Maste 	bl_t *bl = NULL;
5075f4c09ddSEd Maste 	size_t nfd = 0;
5085f4c09ddSEd Maste 	size_t maxfd = 0;
5095f4c09ddSEd Maste 
5105f4c09ddSEd Maste 	for (size_t i = 0; i < nblsock; i++)
5115f4c09ddSEd Maste 		addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]);
5125f4c09ddSEd Maste 	free(blsock);
5135f4c09ddSEd Maste 
5145f4c09ddSEd Maste 	if (spath) {
5155f4c09ddSEd Maste 		FILE *fp = fopen(spath, "r");
5165f4c09ddSEd Maste 		char *line;
5175f4c09ddSEd Maste 		if (fp == NULL)
5185f4c09ddSEd Maste 			err(EXIT_FAILURE, "Can't open `%s'", spath);
5195f4c09ddSEd Maste 		for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL;
5205f4c09ddSEd Maste 		    free(line))
5215f4c09ddSEd Maste 			addfd(&pfd, &bl, &nfd, &maxfd, line);
5225f4c09ddSEd Maste 		fclose(fp);
5235f4c09ddSEd Maste 	}
5245f4c09ddSEd Maste 	if (nfd == 0)
5255f4c09ddSEd Maste 		addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK);
5265f4c09ddSEd Maste 
5275f4c09ddSEd Maste 	state = state_open(dbfile, flags, 0600);
5285f4c09ddSEd Maste 	if (state == NULL)
5295f4c09ddSEd Maste 		state = state_open(dbfile,  flags | O_CREAT, 0600);
5305f4c09ddSEd Maste 	if (state == NULL)
5315f4c09ddSEd Maste 		return EXIT_FAILURE;
5325f4c09ddSEd Maste 
5335f4c09ddSEd Maste 	if (restore) {
5345f4c09ddSEd Maste 		if (!flush)
5355f4c09ddSEd Maste 			rules_flush();
5365f4c09ddSEd Maste 		rules_restore();
5375f4c09ddSEd Maste 	}
5385f4c09ddSEd Maste 
5395f4c09ddSEd Maste 	if (!debug) {
5405f4c09ddSEd Maste 		if (daemon(0, 0) == -1)
5415f4c09ddSEd Maste 			err(EXIT_FAILURE, "daemon failed");
5425f4c09ddSEd Maste 		if (pidfile(NULL) == -1)
5435f4c09ddSEd Maste 			err(EXIT_FAILURE, "Can't create pidfile");
5445f4c09ddSEd Maste 	}
5455f4c09ddSEd Maste 
5465f4c09ddSEd Maste 	for (size_t t = 0; !done; t++) {
5475f4c09ddSEd Maste 		if (readconf) {
5485f4c09ddSEd Maste 			readconf = 0;
5495f4c09ddSEd Maste 			conf_parse(configfile);
5505f4c09ddSEd Maste 		}
5515f4c09ddSEd Maste 		ret = poll(pfd, (nfds_t)nfd, tout);
5525f4c09ddSEd Maste 		if (debug)
5535f4c09ddSEd Maste 			(*lfun)(LOG_DEBUG, "received %d from poll()", ret);
5545f4c09ddSEd Maste 		switch (ret) {
5555f4c09ddSEd Maste 		case -1:
5565f4c09ddSEd Maste 			if (errno == EINTR)
5575f4c09ddSEd Maste 				continue;
5585f4c09ddSEd Maste 			(*lfun)(LOG_ERR, "poll (%m)");
5595f4c09ddSEd Maste 			return EXIT_FAILURE;
5605f4c09ddSEd Maste 		case 0:
5615f4c09ddSEd Maste 			state_sync(state);
5625f4c09ddSEd Maste 			break;
5635f4c09ddSEd Maste 		default:
5645f4c09ddSEd Maste 			for (size_t i = 0; i < nfd; i++)
5655f4c09ddSEd Maste 				if (pfd[i].revents & POLLIN)
5665f4c09ddSEd Maste 					process(bl[i]);
5675f4c09ddSEd Maste 		}
5685f4c09ddSEd Maste 		if (t % 100 == 0)
5695f4c09ddSEd Maste 			state_sync(state);
5705f4c09ddSEd Maste 		if (t % 10000 == 0)
5715f4c09ddSEd Maste 			update_interfaces();
5725f4c09ddSEd Maste 		update();
5735f4c09ddSEd Maste 	}
5745f4c09ddSEd Maste 	state_close(state);
5755f4c09ddSEd Maste 	return 0;
5765f4c09ddSEd Maste }
577