Add file-based IP blacklist module.
authorMichael Poole <mdpoole@troilus.org>
Sun, 18 Mar 2007 17:36:31 +0000 (13:36 -0400)
committerMichael Poole <mdpoole@troilus.org>
Sun, 18 Mar 2007 17:36:31 +0000 (13:36 -0400)
src/mod-blacklist.c: New module.

srvx.conf.example: Document its configuration options.

src/mod-blacklist.c [new file with mode: 0644]
srvx.conf.example

diff --git a/src/mod-blacklist.c b/src/mod-blacklist.c
new file mode 100644 (file)
index 0000000..f90f596
--- /dev/null
@@ -0,0 +1,159 @@
+/* Blacklist module for srvx 1.x
+ * Copyright 2007 Michael Poole <mdpoole@troilus.org>
+ *
+ * This file is part of srvx.
+ *
+ * srvx is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+
+#include "conf.h"
+#include "gline.h"
+#include "modcmd.h"
+#include "proto.h"
+
+const char *blacklist_module_deps[] = { NULL };
+
+static struct log_type *bl_log;
+static dict_t blacklist_hosts; /* maps IPs or hostnames to reasons from blacklist_reasons */
+static dict_t blacklist_reasons; /* maps strings to themselves (poor man's data sharing) */
+
+static struct {
+    unsigned long gline_duration;
+} conf;
+
+static int
+blacklist_check_user(struct userNode *user)
+{
+    const char *reason;
+    const char *host;
+    char ip[IRC_NTOP_MAX_SIZE];
+
+    irc_ntop(ip, sizeof(ip), &user->ip);
+    reason = dict_find(blacklist_hosts, host = ip, NULL);
+    if (reason == NULL) {
+        reason = dict_find(blacklist_hosts, host = user->hostname, NULL);
+    }
+    if (reason != NULL) {
+        char *target;
+        target = alloca(strlen(host) + 3);
+        target[0] = '*';
+        target[1] = '@';
+        strcpy(target + 2, host);
+        gline_add(self->name, target, conf.gline_duration, reason, now, now, 1);
+    }
+    return 0;
+}
+
+static void
+blacklist_load_file(const char *filename, const char *default_reason)
+{
+    FILE *file;
+    const char *reason;
+    char *mapped_reason;
+    char *sep;
+    size_t len;
+    char linebuf[MAXLEN];
+
+    if (!filename)
+        return;
+    if (!default_reason)
+        default_reason = "client is blacklisted";
+    file = fopen(filename, "r");
+    if (!file) {
+        log_module(bl_log, LOG_ERROR, "Unable to open %s for reading: %s", filename, strerror(errno));
+        return;
+    }
+    log_module(bl_log, LOG_DEBUG, "Loading blacklist from %s.", filename);
+    while (fgets(linebuf, sizeof(linebuf), file)) {
+        /* Trim whitespace from end of line. */
+        len = strlen(linebuf);
+        while (isspace(linebuf[len-1]))
+            linebuf[--len] = '\0';
+
+        /* Figure out which reason string we should use. */
+        reason = default_reason;
+        sep = strchr(linebuf, ' ');
+        if (sep) {
+            *sep++ = '\0';
+            while (isspace(*sep))
+                sep++;
+            if (*sep != '\0')
+                reason = sep;
+        }
+
+        /* See if the reason string is already known. */
+        mapped_reason = dict_find(blacklist_reasons, reason, NULL);
+        if (!mapped_reason) {
+            mapped_reason = strdup(reason);
+            dict_insert(blacklist_reasons, mapped_reason, (char*)mapped_reason);
+        }
+
+        /* Store the blacklist entry. */
+        dict_insert(blacklist_hosts, strdup(linebuf), mapped_reason);
+    }
+    fclose(file);
+}
+
+static void
+blacklist_conf_read(void)
+{
+    dict_t node;
+    const char *str1;
+    const char *str2;
+
+    dict_delete(blacklist_hosts);
+    blacklist_hosts = dict_new();
+    dict_set_free_keys(blacklist_hosts, free);
+
+    dict_delete(blacklist_reasons);
+    blacklist_reasons = dict_new();
+    dict_set_free_keys(blacklist_reasons, free);
+
+    node = conf_get_data("modules/blacklist", RECDB_OBJECT);
+    if (node == NULL)
+        return;
+
+    str1 = database_get_data(node, "file", RECDB_QSTRING);
+    str2 = database_get_data(node, "file_reason", RECDB_QSTRING);
+    blacklist_load_file(str1, str2);
+
+    str1 = database_get_data(node, "gline_duration", RECDB_QSTRING);
+    if (str1 == NULL)
+        str1 = "1h";
+    conf.gline_duration = ParseInterval(str1);
+}
+
+static void
+blacklist_cleanup(void)
+{
+    dict_delete(blacklist_hosts);
+    dict_delete(blacklist_reasons);
+}
+
+int
+blacklist_init(void)
+{
+    bl_log = log_register_type("Blacklist", "file:blacklist.log");
+    conf_register_reload(blacklist_conf_read);
+    reg_new_user_func(blacklist_check_user);
+    reg_exit_func(blacklist_cleanup);
+    return 1;
+}
+
+int
+blacklist_finalize(void)
+{
+    return 1;
+}
index e4f3420e8e5ba2fbfa0f6445d243a415982d56a0..0e112fc53b1ad715b286325dd53c93c7840946ab 100644 (file)
         "port" "7702";
         "password" "hello";
     };
+    "blacklist" {
+        // File containing blacklisted client addresses.
+        // "file" "blacklist.txt";
+        // Each line in the file should start with an IP or hostname.
+        // If there is whitespace and a message after that, the
+        // message will override this one:
+        "file_reason" "client is blacklisted";
+        // How long should a blacklist G-line last?
+        "gline_duration" "1h";
+    };
 };
 
 "policers" {