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");