Home | History | Annotate | Line # | Download | only in bin
      1  1.15  christos /*	$NetBSD: blocklistd.c,v 1.15 2026/02/07 14:32:04 christos Exp $	*/
      2   1.1  christos 
      3   1.1  christos /*-
      4   1.1  christos  * Copyright (c) 2015 The NetBSD Foundation, Inc.
      5   1.1  christos  * All rights reserved.
      6   1.1  christos  *
      7   1.1  christos  * This code is derived from software contributed to The NetBSD Foundation
      8   1.1  christos  * by Christos Zoulas.
      9   1.1  christos  *
     10   1.1  christos  * Redistribution and use in source and binary forms, with or without
     11   1.1  christos  * modification, are permitted provided that the following conditions
     12   1.1  christos  * are met:
     13   1.1  christos  * 1. Redistributions of source code must retain the above copyright
     14   1.1  christos  *    notice, this list of conditions and the following disclaimer.
     15   1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     17   1.1  christos  *    documentation and/or other materials provided with the distribution.
     18   1.1  christos  *
     19   1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.1  christos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.1  christos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.1  christos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.1  christos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.1  christos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.1  christos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.1  christos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.1  christos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.1  christos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.1  christos  * POSSIBILITY OF SUCH DAMAGE.
     30   1.1  christos  */
     31   1.1  christos #ifdef HAVE_CONFIG_H
     32   1.1  christos #include "config.h"
     33   1.1  christos #endif
     34   1.8  christos 
     35   1.8  christos #ifdef HAVE_SYS_CDEFS_H
     36   1.1  christos #include <sys/cdefs.h>
     37   1.8  christos #endif
     38  1.15  christos __RCSID("$NetBSD: blocklistd.c,v 1.15 2026/02/07 14:32:04 christos Exp $");
     39   1.1  christos 
     40   1.1  christos #include <sys/types.h>
     41   1.1  christos #include <sys/socket.h>
     42   1.1  christos #include <sys/queue.h>
     43   1.1  christos 
     44   1.1  christos #ifdef HAVE_LIBUTIL_H
     45   1.1  christos #include <libutil.h>
     46   1.1  christos #endif
     47   1.1  christos #ifdef HAVE_UTIL_H
     48   1.1  christos #include <util.h>
     49   1.1  christos #endif
     50   1.1  christos #include <string.h>
     51   1.1  christos #include <signal.h>
     52   1.1  christos #include <netdb.h>
     53   1.1  christos #include <stdio.h>
     54   1.1  christos #include <stdbool.h>
     55   1.1  christos #include <string.h>
     56   1.1  christos #include <inttypes.h>
     57   1.1  christos #include <syslog.h>
     58   1.1  christos #include <ctype.h>
     59   1.1  christos #include <limits.h>
     60   1.1  christos #include <errno.h>
     61   1.1  christos #include <poll.h>
     62   1.1  christos #include <fcntl.h>
     63   1.1  christos #include <err.h>
     64   1.1  christos #include <stdlib.h>
     65   1.1  christos #include <unistd.h>
     66   1.1  christos #include <time.h>
     67   1.1  christos #include <ifaddrs.h>
     68   1.1  christos #include <netinet/in.h>
     69   1.1  christos 
     70   1.1  christos #include "bl.h"
     71   1.1  christos #include "internal.h"
     72   1.1  christos #include "conf.h"
     73   1.1  christos #include "run.h"
     74   1.1  christos #include "state.h"
     75   1.1  christos #include "support.h"
     76   1.1  christos 
     77   1.1  christos static const char *configfile = _PATH_BLCONF;
     78   1.1  christos static DB *state;
     79   1.1  christos static const char *dbfile = _PATH_BLSTATE;
     80   1.1  christos static sig_atomic_t readconf;
     81   1.1  christos static sig_atomic_t done;
     82   1.1  christos static int vflag;
     83   1.1  christos 
     84   1.1  christos static void
     85   1.1  christos sigusr1(int n __unused)
     86   1.1  christos {
     87   1.1  christos 	debug++;
     88   1.1  christos }
     89   1.1  christos 
     90   1.1  christos static void
     91   1.1  christos sigusr2(int n __unused)
     92   1.1  christos {
     93   1.1  christos 	debug--;
     94   1.1  christos }
     95   1.1  christos 
     96   1.1  christos static void
     97   1.1  christos sighup(int n __unused)
     98   1.1  christos {
     99   1.1  christos 	readconf++;
    100   1.1  christos }
    101   1.1  christos 
    102   1.1  christos static void
    103   1.1  christos sigdone(int n __unused)
    104   1.1  christos {
    105   1.1  christos 	done++;
    106   1.1  christos }
    107   1.1  christos 
    108   1.1  christos static __dead void
    109   1.1  christos usage(int c)
    110   1.1  christos {
    111   1.2  christos 	if (c != '?')
    112   1.1  christos 		warnx("Unknown option `%c'", (char)c);
    113   1.1  christos 	fprintf(stderr, "Usage: %s [-vdfr] [-c <config>] [-R <rulename>] "
    114   1.1  christos 	    "[-P <sockpathsfile>] [-C <controlprog>] [-D <dbfile>] "
    115   1.1  christos 	    "[-s <sockpath>] [-t <timeout>]\n", getprogname());
    116   1.1  christos 	exit(EXIT_FAILURE);
    117   1.1  christos }
    118   1.1  christos 
    119   1.1  christos static int
    120   1.1  christos getremoteaddress(bl_info_t *bi, struct sockaddr_storage *rss, socklen_t *rsl)
    121   1.1  christos {
    122   1.1  christos 	*rsl = sizeof(*rss);
    123   1.1  christos 	memset(rss, 0, *rsl);
    124   1.1  christos 
    125   1.1  christos 	if (getpeername(bi->bi_fd, (void *)rss, rsl) != -1)
    126   1.1  christos 		return 0;
    127   1.1  christos 
    128   1.1  christos 	if (errno != ENOTCONN) {
    129   1.1  christos 		(*lfun)(LOG_ERR, "getpeername failed (%m)");
    130   1.1  christos 		return -1;
    131   1.1  christos 	}
    132   1.1  christos 
    133   1.1  christos 	if (bi->bi_slen == 0) {
    134   1.1  christos 		(*lfun)(LOG_ERR, "unconnected socket with no peer in message");
    135   1.1  christos 		return -1;
    136   1.1  christos 	}
    137   1.1  christos 
    138   1.1  christos 	switch (bi->bi_ss.ss_family) {
    139   1.1  christos 	case AF_INET:
    140   1.1  christos 		*rsl = sizeof(struct sockaddr_in);
    141   1.1  christos 		break;
    142   1.1  christos 	case AF_INET6:
    143   1.1  christos 		*rsl = sizeof(struct sockaddr_in6);
    144   1.1  christos 		break;
    145   1.1  christos 	default:
    146   1.1  christos 		(*lfun)(LOG_ERR, "bad client passed socket family %u",
    147   1.1  christos 		    (unsigned)bi->bi_ss.ss_family);
    148   1.1  christos 		return -1;
    149   1.1  christos 	}
    150   1.1  christos 
    151   1.1  christos 	if (*rsl != bi->bi_slen) {
    152   1.1  christos 		(*lfun)(LOG_ERR, "bad client passed socket length %u != %u",
    153   1.1  christos 		    (unsigned)*rsl, (unsigned)bi->bi_slen);
    154   1.1  christos 		return -1;
    155   1.1  christos 	}
    156   1.1  christos 
    157   1.1  christos 	memcpy(rss, &bi->bi_ss, *rsl);
    158   1.1  christos 
    159   1.1  christos #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
    160   1.1  christos 	if (*rsl != rss->ss_len) {
    161   1.1  christos 		(*lfun)(LOG_ERR,
    162   1.1  christos 		    "bad client passed socket internal length %u != %u",
    163   1.1  christos 		    (unsigned)*rsl, (unsigned)rss->ss_len);
    164   1.1  christos 		return -1;
    165   1.1  christos 	}
    166   1.1  christos #endif
    167   1.1  christos 	return 0;
    168   1.1  christos }
    169   1.1  christos 
    170   1.1  christos static void
    171   1.1  christos process(bl_t bl)
    172   1.1  christos {
    173   1.1  christos 	struct sockaddr_storage rss;
    174   1.1  christos 	socklen_t rsl;
    175   1.1  christos 	char rbuf[BUFSIZ];
    176   1.1  christos 	bl_info_t *bi;
    177   1.1  christos 	struct conf c;
    178   1.1  christos 	struct dbinfo dbi;
    179   1.1  christos 	struct timespec ts;
    180   1.1  christos 
    181   1.9  christos 	memset(&dbi, 0, sizeof(dbi));
    182  1.10  christos 	memset(&c, 0, sizeof(c));
    183   1.1  christos 	if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
    184   1.1  christos 		(*lfun)(LOG_ERR, "clock_gettime failed (%m)");
    185   1.1  christos 		return;
    186   1.1  christos 	}
    187   1.1  christos 
    188   1.1  christos 	if ((bi = bl_recv(bl)) == NULL) {
    189   1.1  christos 		(*lfun)(LOG_ERR, "no message (%m)");
    190   1.1  christos 		return;
    191   1.1  christos 	}
    192   1.1  christos 
    193   1.1  christos 	if (getremoteaddress(bi, &rss, &rsl) == -1)
    194  1.13  christos 		goto out;
    195   1.1  christos 
    196   1.6  christos 	if (debug || bi->bi_msg[0]) {
    197   1.1  christos 		sockaddr_snprintf(rbuf, sizeof(rbuf), "%a:%p", (void *)&rss);
    198   1.6  christos 		(*lfun)(bi->bi_msg[0] ? LOG_INFO : LOG_DEBUG,
    199  1.14  christos 		    "processing type=%d fd=%d remote=%s msg=\"%s\" "
    200  1.14  christos 		    "uid=%lu gid=%lu",
    201   1.6  christos 		    bi->bi_type, bi->bi_fd, rbuf,
    202   1.1  christos 		    bi->bi_msg, (unsigned long)bi->bi_uid,
    203   1.1  christos 		    (unsigned long)bi->bi_gid);
    204   1.1  christos 	}
    205   1.1  christos 
    206   1.1  christos 	if (conf_find(bi->bi_fd, bi->bi_uid, &rss, &c) == NULL) {
    207   1.1  christos 		(*lfun)(LOG_DEBUG, "no rule matched");
    208  1.13  christos 		goto out;
    209   1.1  christos 	}
    210   1.1  christos 
    211   1.1  christos 
    212   1.1  christos 	if (state_get(state, &c, &dbi) == -1)
    213  1.13  christos 		goto out;
    214   1.1  christos 
    215   1.1  christos 	if (debug) {
    216   1.1  christos 		char b1[128], b2[128];
    217   1.1  christos 		(*lfun)(LOG_DEBUG, "%s: initial db state for %s: count=%d/%d "
    218   1.1  christos 		    "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
    219   1.1  christos 		    fmttime(b1, sizeof(b1), dbi.last),
    220   1.1  christos 		    fmttime(b2, sizeof(b2), ts.tv_sec));
    221   1.1  christos 	}
    222   1.1  christos 
    223   1.1  christos 	switch (bi->bi_type) {
    224   1.1  christos 	case BL_ABUSE:
    225   1.1  christos 		/*
    226   1.7  christos 		 * If the application has signaled abusive behavior, set the
    227   1.7  christos 		 * number of fails to be two less than the configured limit.
    228   1.7  christos 		 * Fall through to the normal BL_ADD and BL_BADUSER processing,
    229  1.11  christos 		 * which will increment the failure count to the threshold, and
    230   1.7  christos 		 * block the abusive address.
    231   1.1  christos 		 */
    232   1.1  christos 		if (c.c_nfail != -1)
    233   1.7  christos 			dbi.count = c.c_nfail - 2;
    234   1.1  christos 		/*FALLTHROUGH*/
    235   1.1  christos 	case BL_ADD:
    236   1.7  christos 		dbi.count++;		/* will become += 2 */
    237   1.7  christos 		/*FALLTHROUGH*/
    238   1.7  christos 	case BL_BADUSER:
    239   1.1  christos 		dbi.count++;
    240   1.1  christos 		dbi.last = ts.tv_sec;
    241   1.1  christos 		if (c.c_nfail != -1 && dbi.count >= c.c_nfail) {
    242   1.1  christos 			/*
    243   1.1  christos 			 * No point in re-adding the rule.
    244   1.1  christos 			 * It might exist already due to latency in processing
    245   1.1  christos 			 * and removing the rule is the wrong thing to do as
    246   1.1  christos 			 * it allows a window to attack again.
    247   1.1  christos 			 */
    248   1.1  christos 			if (dbi.id[0] == '\0') {
    249   1.1  christos 				int res = run_change("add", &c,
    250   1.1  christos 				    dbi.id, sizeof(dbi.id));
    251   1.1  christos 				if (res == -1)
    252   1.1  christos 					goto out;
    253   1.1  christos 			}
    254   1.1  christos 			sockaddr_snprintf(rbuf, sizeof(rbuf), "%a",
    255   1.1  christos 			    (void *)&rss);
    256   1.1  christos 			(*lfun)(LOG_INFO,
    257   1.1  christos 			    "blocked %s/%d:%d for %d seconds",
    258   1.1  christos 			    rbuf, c.c_lmask, c.c_port, c.c_duration);
    259   1.1  christos 		}
    260   1.1  christos 		break;
    261   1.1  christos 	case BL_DELETE:
    262   1.1  christos 		if (dbi.last == 0)
    263   1.1  christos 			goto out;
    264   1.1  christos 		dbi.count = 0;
    265   1.1  christos 		dbi.last = 0;
    266   1.1  christos 		break;
    267   1.1  christos 	default:
    268   1.1  christos 		(*lfun)(LOG_ERR, "unknown message %d", bi->bi_type);
    269   1.1  christos 	}
    270   1.1  christos 	state_put(state, &c, &dbi);
    271   1.1  christos 
    272   1.1  christos out:
    273  1.13  christos 	close(bi->bi_fd);
    274  1.13  christos 
    275   1.1  christos 	if (debug) {
    276   1.1  christos 		char b1[128], b2[128];
    277   1.1  christos 		(*lfun)(LOG_DEBUG, "%s: final db state for %s: count=%d/%d "
    278   1.1  christos 		    "last=%s now=%s", __func__, rbuf, dbi.count, c.c_nfail,
    279   1.1  christos 		    fmttime(b1, sizeof(b1), dbi.last),
    280   1.1  christos 		    fmttime(b2, sizeof(b2), ts.tv_sec));
    281   1.1  christos 	}
    282   1.1  christos }
    283   1.1  christos 
    284   1.1  christos static void
    285   1.1  christos update_interfaces(void)
    286   1.1  christos {
    287   1.1  christos 	struct ifaddrs *oifas, *nifas;
    288   1.1  christos 
    289   1.1  christos 	if (getifaddrs(&nifas) == -1)
    290   1.1  christos 		return;
    291   1.1  christos 
    292   1.1  christos 	oifas = ifas;
    293   1.1  christos 	ifas = nifas;
    294   1.1  christos 
    295   1.1  christos 	if (oifas)
    296   1.1  christos 		freeifaddrs(oifas);
    297   1.1  christos }
    298   1.1  christos 
    299   1.1  christos static void
    300   1.1  christos update(void)
    301   1.1  christos {
    302   1.1  christos 	struct timespec ts;
    303   1.1  christos 	struct conf c;
    304   1.1  christos 	struct dbinfo dbi;
    305   1.1  christos 	unsigned int f, n;
    306   1.1  christos 	char buf[128];
    307   1.1  christos 	void *ss = &c.c_ss;
    308   1.1  christos 
    309   1.1  christos 	if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
    310   1.1  christos 		(*lfun)(LOG_ERR, "clock_gettime failed (%m)");
    311   1.1  christos 		return;
    312   1.1  christos 	}
    313   1.1  christos 
    314   1.1  christos again:
    315   1.1  christos 	for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1;
    316   1.1  christos 	    f = 0, n++)
    317   1.1  christos 	{
    318   1.1  christos 		time_t when = c.c_duration + dbi.last;
    319   1.1  christos 		if (debug > 1) {
    320   1.1  christos 			char b1[64], b2[64];
    321   1.1  christos 			sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss);
    322   1.1  christos 			(*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d "
    323   1.1  christos 			    "last=%s " "now=%s", __func__, n, buf, dbi.count,
    324   1.1  christos 			    c.c_duration, fmttime(b1, sizeof(b1), dbi.last),
    325   1.1  christos 			    fmttime(b2, sizeof(b2), ts.tv_sec));
    326   1.1  christos 		}
    327   1.1  christos 		if (c.c_duration == -1 || when >= ts.tv_sec)
    328   1.1  christos 			continue;
    329   1.1  christos 		if (dbi.id[0]) {
    330   1.1  christos 			run_change("rem", &c, dbi.id, 0);
    331   1.1  christos 			sockaddr_snprintf(buf, sizeof(buf), "%a", ss);
    332   1.3  christos 			(*lfun)(LOG_INFO, "released %s/%d:%d after %d seconds",
    333   1.1  christos 			    buf, c.c_lmask, c.c_port, c.c_duration);
    334   1.1  christos 		}
    335  1.12  christos 		if (state_del(state, &c) == 0)
    336  1.12  christos 			goto again;
    337   1.1  christos 	}
    338   1.1  christos }
    339   1.1  christos 
    340   1.1  christos static void
    341   1.1  christos addfd(struct pollfd **pfdp, bl_t **blp, size_t *nfd, size_t *maxfd,
    342   1.1  christos     const char *path)
    343   1.1  christos {
    344   1.5  christos 	bl_t bl = bl_create(true, path, vflag ? vdlog : vsyslog_r);
    345   1.1  christos 	if (bl == NULL || !bl_isconnected(bl))
    346   1.1  christos 		exit(EXIT_FAILURE);
    347   1.1  christos 	if (*nfd >= *maxfd) {
    348   1.1  christos 		*maxfd += 10;
    349  1.15  christos 		*blp = reallocarray(*blp, *maxfd, sizeof(**blp));
    350   1.1  christos 		if (*blp == NULL)
    351   1.1  christos 			err(EXIT_FAILURE, "malloc");
    352  1.15  christos 		*pfdp = reallocarray(*pfdp, *maxfd, sizeof(**pfdp));
    353   1.1  christos 		if (*pfdp == NULL)
    354   1.1  christos 			err(EXIT_FAILURE, "malloc");
    355   1.1  christos 	}
    356   1.1  christos 
    357   1.1  christos 	(*pfdp)[*nfd].fd = bl_getfd(bl);
    358   1.1  christos 	(*pfdp)[*nfd].events = POLLIN;
    359   1.1  christos 	(*blp)[*nfd] = bl;
    360   1.1  christos 	*nfd += 1;
    361   1.1  christos }
    362   1.1  christos 
    363   1.1  christos static void
    364   1.1  christos uniqueadd(struct conf ***listp, size_t *nlist, size_t *mlist, struct conf *c)
    365   1.1  christos {
    366   1.1  christos 	struct conf **list = *listp;
    367   1.1  christos 
    368   1.1  christos 	if (c->c_name[0] == '\0')
    369   1.1  christos 		return;
    370   1.1  christos 	for (size_t i = 0; i < *nlist; i++) {
    371   1.1  christos 		if (strcmp(list[i]->c_name, c->c_name) == 0)
    372   1.1  christos 			return;
    373   1.1  christos 	}
    374   1.1  christos 	if (*nlist == *mlist) {
    375   1.1  christos 		*mlist += 10;
    376  1.15  christos 		void *p = reallocarray(*listp, *mlist, sizeof(*list));
    377   1.1  christos 		if (p == NULL)
    378   1.1  christos 			err(EXIT_FAILURE, "Can't allocate for rule list");
    379   1.1  christos 		list = *listp = p;
    380   1.1  christos 	}
    381   1.1  christos 	list[(*nlist)++] = c;
    382   1.1  christos }
    383   1.1  christos 
    384   1.1  christos static void
    385   1.1  christos rules_flush(void)
    386   1.1  christos {
    387   1.1  christos 	struct conf **list;
    388   1.1  christos 	size_t nlist, mlist;
    389   1.1  christos 
    390   1.1  christos 	list = NULL;
    391   1.1  christos 	mlist = nlist = 0;
    392   1.1  christos 	for (size_t i = 0; i < rconf.cs_n; i++)
    393   1.1  christos 		uniqueadd(&list, &nlist, &mlist, &rconf.cs_c[i]);
    394   1.1  christos 	for (size_t i = 0; i < lconf.cs_n; i++)
    395   1.1  christos 		uniqueadd(&list, &nlist, &mlist, &lconf.cs_c[i]);
    396   1.1  christos 
    397   1.1  christos 	for (size_t i = 0; i < nlist; i++)
    398   1.1  christos 		run_flush(list[i]);
    399   1.1  christos 	free(list);
    400   1.1  christos }
    401   1.1  christos 
    402   1.1  christos static void
    403   1.1  christos rules_restore(void)
    404   1.1  christos {
    405   1.4  christos 	DB *db;
    406   1.1  christos 	struct conf c;
    407   1.1  christos 	struct dbinfo dbi;
    408   1.1  christos 	unsigned int f;
    409   1.1  christos 
    410   1.4  christos 	db = state_open(dbfile, O_RDONLY, 0);
    411   1.4  christos 	if (db == NULL) {
    412   1.4  christos 		(*lfun)(LOG_ERR, "Can't open `%s' to restore state (%m)",
    413  1.15  christos 		    dbfile);
    414   1.4  christos 		return;
    415   1.4  christos 	}
    416   1.4  christos 	for (f = 1; state_iterate(db, &c, &dbi, f) == 1; f = 0) {
    417   1.1  christos 		if (dbi.id[0] == '\0')
    418   1.1  christos 			continue;
    419   1.1  christos 		(void)run_change("add", &c, dbi.id, sizeof(dbi.id));
    420   1.4  christos 		state_put(state, &c, &dbi);
    421   1.1  christos 	}
    422   1.4  christos 	state_close(db);
    423   1.4  christos 	state_sync(state);
    424   1.1  christos }
    425   1.1  christos 
    426   1.1  christos int
    427   1.1  christos main(int argc, char *argv[])
    428   1.1  christos {
    429   1.1  christos 	int c, tout, flags, flush, restore, ret;
    430   1.1  christos 	const char *spath, **blsock;
    431   1.1  christos 	size_t nblsock, maxblsock;
    432   1.1  christos 
    433   1.1  christos 	setprogname(argv[0]);
    434   1.1  christos 
    435   1.1  christos 	spath = NULL;
    436   1.1  christos 	blsock = NULL;
    437   1.1  christos 	maxblsock = nblsock = 0;
    438   1.1  christos 	flush = 0;
    439   1.1  christos 	restore = 0;
    440   1.1  christos 	tout = 0;
    441   1.1  christos 	flags = O_RDWR|O_EXCL|O_CLOEXEC;
    442   1.1  christos 	while ((c = getopt(argc, argv, "C:c:D:dfP:rR:s:t:v")) != -1) {
    443   1.1  christos 		switch (c) {
    444   1.1  christos 		case 'C':
    445   1.1  christos 			controlprog = optarg;
    446   1.1  christos 			break;
    447   1.1  christos 		case 'c':
    448   1.1  christos 			configfile = optarg;
    449   1.1  christos 			break;
    450   1.1  christos 		case 'D':
    451   1.1  christos 			dbfile = optarg;
    452   1.1  christos 			break;
    453   1.1  christos 		case 'd':
    454   1.1  christos 			debug++;
    455   1.1  christos 			break;
    456   1.1  christos 		case 'f':
    457   1.1  christos 			flush++;
    458   1.1  christos 			break;
    459   1.1  christos 		case 'P':
    460   1.1  christos 			spath = optarg;
    461   1.1  christos 			break;
    462   1.1  christos 		case 'R':
    463   1.1  christos 			rulename = optarg;
    464   1.1  christos 			break;
    465   1.1  christos 		case 'r':
    466   1.1  christos 			restore++;
    467   1.1  christos 			break;
    468   1.1  christos 		case 's':
    469   1.1  christos 			if (nblsock >= maxblsock) {
    470   1.1  christos 				maxblsock += 10;
    471  1.15  christos 				void *p = reallocarray(blsock, maxblsock,
    472  1.15  christos 				    sizeof(*blsock));
    473   1.1  christos 				if (p == NULL)
    474  1.15  christos 					err(EXIT_FAILURE, "Can't allocate "
    475  1.15  christos 					    "memory for %zu sockets",
    476  1.15  christos 					    maxblsock);
    477   1.1  christos 				blsock = p;
    478   1.1  christos 			}
    479   1.1  christos 			blsock[nblsock++] = optarg;
    480   1.1  christos 			break;
    481   1.1  christos 		case 't':
    482   1.1  christos 			tout = atoi(optarg) * 1000;
    483   1.1  christos 			break;
    484   1.1  christos 		case 'v':
    485   1.1  christos 			vflag++;
    486   1.1  christos 			break;
    487   1.1  christos 		default:
    488   1.1  christos 			usage(c);
    489   1.1  christos 		}
    490   1.1  christos 	}
    491   1.1  christos 
    492   1.1  christos 	argc -= optind;
    493   1.1  christos 	if (argc)
    494   1.2  christos 		usage('?');
    495   1.1  christos 
    496   1.1  christos 	signal(SIGHUP, sighup);
    497   1.1  christos 	signal(SIGINT, sigdone);
    498   1.1  christos 	signal(SIGQUIT, sigdone);
    499   1.1  christos 	signal(SIGTERM, sigdone);
    500   1.1  christos 	signal(SIGUSR1, sigusr1);
    501   1.1  christos 	signal(SIGUSR2, sigusr2);
    502   1.1  christos 
    503   1.1  christos 	openlog(getprogname(), LOG_PID, LOG_DAEMON);
    504   1.1  christos 
    505   1.1  christos 	if (debug) {
    506   1.1  christos 		lfun = dlog;
    507   1.1  christos 		if (tout == 0)
    508   1.1  christos 			tout = 5000;
    509   1.1  christos 	} else {
    510   1.1  christos 		if (tout == 0)
    511   1.1  christos 			tout = 15000;
    512   1.1  christos 	}
    513   1.1  christos 
    514   1.1  christos 	update_interfaces();
    515   1.1  christos 	conf_parse(configfile);
    516   1.1  christos 	if (flush) {
    517   1.1  christos 		rules_flush();
    518   1.1  christos 		if (!restore)
    519   1.1  christos 			flags |= O_TRUNC;
    520   1.1  christos 	}
    521   1.1  christos 
    522   1.1  christos 	struct pollfd *pfd = NULL;
    523   1.1  christos 	bl_t *bl = NULL;
    524   1.1  christos 	size_t nfd = 0;
    525   1.1  christos 	size_t maxfd = 0;
    526   1.1  christos 
    527   1.1  christos 	for (size_t i = 0; i < nblsock; i++)
    528   1.1  christos 		addfd(&pfd, &bl, &nfd, &maxfd, blsock[i]);
    529   1.1  christos 	free(blsock);
    530   1.1  christos 
    531   1.1  christos 	if (spath) {
    532   1.1  christos 		FILE *fp = fopen(spath, "r");
    533   1.1  christos 		char *line;
    534   1.1  christos 		if (fp == NULL)
    535   1.1  christos 			err(EXIT_FAILURE, "Can't open `%s'", spath);
    536   1.1  christos 		for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL;
    537   1.1  christos 		    free(line))
    538   1.1  christos 			addfd(&pfd, &bl, &nfd, &maxfd, line);
    539   1.1  christos 		fclose(fp);
    540   1.1  christos 	}
    541   1.1  christos 	if (nfd == 0)
    542   1.1  christos 		addfd(&pfd, &bl, &nfd, &maxfd, _PATH_BLSOCK);
    543   1.1  christos 
    544   1.1  christos 	state = state_open(dbfile, flags, 0600);
    545   1.1  christos 	if (state == NULL)
    546   1.1  christos 		state = state_open(dbfile,  flags | O_CREAT, 0600);
    547  1.14  christos 	else {
    548  1.14  christos 		if (restore) {
    549  1.14  christos 			if (!flush)
    550  1.14  christos 				rules_flush();
    551  1.14  christos 			rules_restore();
    552  1.14  christos 		}
    553  1.14  christos 	}
    554   1.1  christos 	if (state == NULL)
    555  1.15  christos 		exit(EXIT_FAILURE);
    556   1.1  christos 
    557   1.1  christos 	if (!debug) {
    558   1.1  christos 		if (daemon(0, 0) == -1)
    559   1.1  christos 			err(EXIT_FAILURE, "daemon failed");
    560   1.1  christos 		if (pidfile(NULL) == -1)
    561   1.1  christos 			err(EXIT_FAILURE, "Can't create pidfile");
    562   1.1  christos 	}
    563   1.1  christos 
    564   1.1  christos 	for (size_t t = 0; !done; t++) {
    565   1.1  christos 		if (readconf) {
    566   1.1  christos 			readconf = 0;
    567   1.1  christos 			conf_parse(configfile);
    568   1.1  christos 		}
    569   1.1  christos 		ret = poll(pfd, (nfds_t)nfd, tout);
    570  1.13  christos 		if (debug)
    571   1.1  christos 			(*lfun)(LOG_DEBUG, "received %d from poll()", ret);
    572   1.1  christos 		switch (ret) {
    573   1.1  christos 		case -1:
    574   1.1  christos 			if (errno == EINTR)
    575   1.1  christos 				continue;
    576   1.1  christos 			(*lfun)(LOG_ERR, "poll (%m)");
    577  1.15  christos 			exit(EXIT_FAILURE);
    578   1.1  christos 		case 0:
    579   1.1  christos 			state_sync(state);
    580   1.1  christos 			break;
    581   1.1  christos 		default:
    582   1.1  christos 			for (size_t i = 0; i < nfd; i++)
    583   1.1  christos 				if (pfd[i].revents & POLLIN)
    584   1.1  christos 					process(bl[i]);
    585   1.1  christos 		}
    586   1.1  christos 		if (t % 100 == 0)
    587   1.1  christos 			state_sync(state);
    588   1.1  christos 		if (t % 10000 == 0)
    589   1.1  christos 			update_interfaces();
    590   1.1  christos 		update();
    591   1.1  christos 	}
    592   1.1  christos 	state_close(state);
    593  1.15  christos 	exit(EXIT_SUCCESS);
    594   1.1  christos }
    595