Module Name:    src
Committed By:   christos
Date:           Thu Feb  6 19:16:04 UTC 2025

Modified Files:
        src/external/bsd/blocklist/bin: blocklistd.8 conf.c

Log Message:
PR/28: hrosenfeld: This came up as a feature request during code
review of my port of blocklistd to illumos.

This change modifies the config file parsing code of blocklistd to
support multiple configuration files in a configuration directory
like /etc/blocklistd.conf.d. This can be used by specifying a
directory instead of a config file with -d. Alternatively, if a
directory exists of the same name as the config file plus ".d",
it'll be read by default, too.


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/external/bsd/blocklist/bin/blocklistd.8
cvs rdiff -u -r1.7 -r1.8 src/external/bsd/blocklist/bin/conf.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/blocklist/bin/blocklistd.8
diff -u src/external/bsd/blocklist/bin/blocklistd.8:1.4 src/external/bsd/blocklist/bin/blocklistd.8:1.5
--- src/external/bsd/blocklist/bin/blocklistd.8:1.4	Mon Jan 27 12:23:25 2025
+++ src/external/bsd/blocklist/bin/blocklistd.8	Thu Feb  6 14:16:03 2025
@@ -1,4 +1,4 @@
-.\" $NetBSD: blocklistd.8,v 1.4 2025/01/27 17:23:25 christos Exp $
+.\" $NetBSD: blocklistd.8,v 1.5 2025/02/06 19:16:03 christos Exp $
 .\"
 .\" Copyright (c) 2015 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd January 27, 2025
+.Dd February 6, 2025
 .Dt BLOCKLISTD 8
 .Os
 .Sh NAME
@@ -58,13 +58,21 @@ or if that is not specified to
 .Pa /var/run/blocklistd.sock .
 Each notification contains an (action, port, protocol, address, owner) tuple
 that identifies the remote connection and the action.
-This tuple is consulted against entries in
-.Ar configfile
-with syntax specified in
+This tuple is consulted against entries from the
+.Ar configfile ,
+with the syntax specified in
 .Xr blocklistd.conf 5 .
 If an entry is matched, a state entry is created for that tuple.
 Each entry contains a number of tries limit and a duration.
 .Pp
+If
+.Ar configfile
+is a directory, or a directory exists with the same name as
+.Ar configfile
+with
+.Qq .d
+appended to it, each file in the directory will be read as configuration file.
+.Pp
 The way
 .Nm
 does configuration entry matching is by having the client side pass the

Index: src/external/bsd/blocklist/bin/conf.c
diff -u src/external/bsd/blocklist/bin/conf.c:1.7 src/external/bsd/blocklist/bin/conf.c:1.8
--- src/external/bsd/blocklist/bin/conf.c:1.7	Wed Feb  5 15:09:33 2025
+++ src/external/bsd/blocklist/bin/conf.c	Thu Feb  6 14:16:03 2025
@@ -1,4 +1,4 @@
-/*	$NetBSD: conf.c,v 1.7 2025/02/05 20:09:33 christos Exp $	*/
+/*	$NetBSD: conf.c,v 1.8 2025/02/06 19:16:03 christos Exp $	*/
 
 /*-
  * Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -33,7 +33,7 @@
 #endif
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: conf.c,v 1.7 2025/02/05 20:09:33 christos Exp $");
+__RCSID("$NetBSD: conf.c,v 1.8 2025/02/06 19:16:03 christos Exp $");
 
 #include <stdio.h>
 #ifdef HAVE_LIBUTIL_H
@@ -58,6 +58,7 @@ __RCSID("$NetBSD: conf.c,v 1.7 2025/02/0
 #include <net/if.h>
 #include <net/route.h>
 #include <sys/socket.h>
+#include <dirent.h>
 
 #include "bl.h"
 #include "internal.h"
@@ -693,6 +694,25 @@ conf_addr_eq(const struct sockaddr_stora
 static int
 conf_eq(const struct conf *c1, const struct conf *c2)
 {
+	if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, FSTAR))
+		return 0;
+
+#define	CMP(a, b, f) \
+	if ((a)->f != (b)->f) \
+		return 0;
+
+	CMP(c1, c2, c_port);
+	CMP(c1, c2, c_proto);
+	CMP(c1, c2, c_family);
+	CMP(c1, c2, c_uid);
+#undef CMP
+
+	return 1;
+}
+
+static int
+conf_match(const struct conf *c1, const struct conf *c2)
+{
 
 	if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
 		return 0;
@@ -955,13 +975,54 @@ confset_free(struct confset *cs)
 }
 
 static void
-confset_replace(struct confset *dc, struct confset *sc)
+confset_merge(struct confset *dc, struct confset *sc)
 {
-	struct confset tc;
-	tc = *dc;
-	*dc = *sc;
-	confset_init(sc);
-	confset_free(&tc);
+	size_t i, j;
+	char buf[BUFSIZ];
+
+	/* Check each rule of the src confset (sc) */
+	for (i = 0; i < sc->cs_n; i++) {
+		/* Compare to each rule in the dest confset (dc) */
+		for (j = 0; j < dc->cs_n; j++) {
+			if (conf_eq(&dc->cs_c[j], &sc->cs_c[i])) {
+				break;
+			}
+		}
+
+		if (j == dc->cs_n) {
+			/* This is a new rule to add to the dest confset. */
+			if (confset_full(dc) && confset_grow(dc) == -1)
+				return;
+
+			*confset_get(dc) = sc->cs_c[i];
+			confset_add(dc);
+			continue;
+		}
+
+		/* We had a match above. */
+		/*
+		 * Check whether the rule from the src confset is more
+		 * restrictive than the existing one. Adjust the
+		 * existing rule if necessary.
+		 */
+		if (sc->cs_c[i].c_nfail == dc->cs_c[j].c_nfail &&
+		    sc->cs_c[i].c_duration && dc->cs_c[j].c_duration) {
+			(*lfun)(LOG_DEBUG, "skipping existing rule: %s",
+			conf_print(buf, sizeof (buf), "", "\t", &sc->cs_c[i]));
+			continue;
+		}
+
+		if (sc->cs_c[i].c_nfail < dc->cs_c[j].c_nfail)
+			dc->cs_c[j].c_nfail = sc->cs_c[i].c_nfail;
+
+		if (sc->cs_c[i].c_duration > dc->cs_c[j].c_duration)
+			dc->cs_c[j].c_duration = sc->cs_c[i].c_duration;
+
+		(*lfun)(LOG_DEBUG, "adjusted existing rule: %s",
+		    conf_print(buf, sizeof (buf), "", "\t", &dc->cs_c[j]));
+	}
+
+	confset_free(sc);
 }
 
 static void
@@ -992,7 +1053,7 @@ confset_match(const struct confset *cs, 
 		if (debug)
 			(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
 			    "check:\t", "", &cs->cs_c[i]));
-		if (conf_eq(c, &cs->cs_c[i])) {
+		if (conf_match(c, &cs->cs_c[i])) {
 			if (debug)
 				(*lfun)(LOG_DEBUG, "%s",
 				    conf_print(buf, sizeof(buf),
@@ -1162,20 +1223,13 @@ conf_find(int fd, uid_t uid, const struc
 	return cr;
 }
 
-
-void
-conf_parse(const char *f)
+static void
+conf_parsefile(FILE *fp, const char *config_file)
 {
-	FILE *fp;
 	char *line;
 	size_t lineno, len;
 	struct confset lc, rc, *cs;
 
-	if ((fp = fopen(f, "r")) == NULL) {
-		(*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f);
-		return;
-	}
-
 	lineno = 0;
 
 	confset_init(&rc);
@@ -1199,23 +1253,103 @@ conf_parse(const char *f)
 			if (confset_grow(cs) == -1) {
 				confset_free(&lc);
 				confset_free(&rc);
-				fclose(fp);
 				free(line);
 				return;
 			}
 		}
-		if (conf_parseline(f, lineno, line, confset_get(cs),
+		if (conf_parseline(config_file, lineno, line, confset_get(cs),
 		    cs == &lc) == -1)
 			continue;
 		confset_add(cs);
 	}
 
-	fclose(fp);
-	confset_sort(&lc);
-	confset_sort(&rc);
+	confset_merge(&rconf, &rc);
+	confset_merge(&lconf, &lc);
+}
+
+
+static void
+conf_parsedir(DIR *dir, const char *config_path)
+{
+	long path_max;
+	struct dirent *dent;
+	char *path;
+	FILE *fp;
+
+	if ((path_max = pathconf(config_path, _PC_PATH_MAX)) == -1)
+		path_max = 2048;
+
+	if ((path = malloc((size_t)path_max)) == NULL) {
+		(*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
+		    __func__);
+		return;
+	}
+
+	while ((dent = readdir(dir)) != NULL) {
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
+			continue;
+
+		(void) snprintf(path, path_max, "%s/%s", config_path,
+		    dent->d_name);
+		if ((fp = fopen(path, "r")) == NULL) {
+			(*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
+			    path);
+			continue;
+		}
+		conf_parsefile(fp, path);
+		fclose(fp);
+	}
+
+	free(path);
+}
+
+void
+conf_parse(const char *config_path)
+{
+	char *path;
+	DIR *dir;
+	FILE *fp;
+
+	if ((dir = opendir(config_path)) != NULL) {
+		/*
+		 * If config_path is a directory, parse the configuration files
+		 * in the directory. Then we're done here.
+		 */
+		conf_parsedir(dir, config_path);
+		closedir(dir);
+		goto out;
+	} else if ((fp = fopen(config_path, "r")) != NULL) {
+		/* If config_path is a file, parse it. */
+		conf_parsefile(fp, config_path);
+		fclose(fp);
+	}
+
+	/*
+	 * Append ".d" to config_path, and if that is a directory, parse the
+	 * configuration files in the directory.
+	 */
+	if (asprintf(&path, "%s.d", config_path) < 0) {
+		(*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
+		    __func__);
+		goto out;
+	}
+
+	if ((dir = opendir(path)) != NULL) {
+		conf_parsedir(dir, path);
+		closedir(dir);
+	}
+	free(path);
+
+out:
+	if (dir == NULL && fp == NULL) {
+		(*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
+		    config_path);
+		return;
+	}
 
-	confset_replace(&rconf, &rc);
-	confset_replace(&lconf, &lc);
+	confset_sort(&lconf);
+	confset_sort(&rconf);
 
 	if (debug) {
 		confset_list(&lconf, "local", "target");

Reply via email to