From: Michael Poole Date: Sun, 18 Mar 2007 17:36:31 +0000 (-0400) Subject: Add file-based IP blacklist module. X-Git-Tag: v1.4.0-rc1~42 X-Git-Url: http://git.pk910.de/?p=srvx.git;a=commitdiff_plain;h=fd97d015df8937cfc28e45709af65b2a8a7fa6be Add file-based IP blacklist module. src/mod-blacklist.c: New module. srvx.conf.example: Document its configuration options. --- diff --git a/src/mod-blacklist.c b/src/mod-blacklist.c new file mode 100644 index 0000000..f90f596 --- /dev/null +++ b/src/mod-blacklist.c @@ -0,0 +1,159 @@ +/* Blacklist module for srvx 1.x + * Copyright 2007 Michael Poole + * + * 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; +} diff --git a/srvx.conf.example b/srvx.conf.example index e4f3420..0e112fc 100644 --- a/srvx.conf.example +++ b/srvx.conf.example @@ -299,6 +299,16 @@ "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" {