Add file-based IP blacklist module.
[srvx.git] / src / mod-blacklist.c
1 /* Blacklist module for srvx 1.x
2  * Copyright 2007 Michael Poole <mdpoole@troilus.org>
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 #include "conf.h"
22 #include "gline.h"
23 #include "modcmd.h"
24 #include "proto.h"
25
26 const char *blacklist_module_deps[] = { NULL };
27
28 static struct log_type *bl_log;
29 static dict_t blacklist_hosts; /* maps IPs or hostnames to reasons from blacklist_reasons */
30 static dict_t blacklist_reasons; /* maps strings to themselves (poor man's data sharing) */
31
32 static struct {
33     unsigned long gline_duration;
34 } conf;
35
36 static int
37 blacklist_check_user(struct userNode *user)
38 {
39     const char *reason;
40     const char *host;
41     char ip[IRC_NTOP_MAX_SIZE];
42
43     irc_ntop(ip, sizeof(ip), &user->ip);
44     reason = dict_find(blacklist_hosts, host = ip, NULL);
45     if (reason == NULL) {
46         reason = dict_find(blacklist_hosts, host = user->hostname, NULL);
47     }
48     if (reason != NULL) {
49         char *target;
50         target = alloca(strlen(host) + 3);
51         target[0] = '*';
52         target[1] = '@';
53         strcpy(target + 2, host);
54         gline_add(self->name, target, conf.gline_duration, reason, now, now, 1);
55     }
56     return 0;
57 }
58
59 static void
60 blacklist_load_file(const char *filename, const char *default_reason)
61 {
62     FILE *file;
63     const char *reason;
64     char *mapped_reason;
65     char *sep;
66     size_t len;
67     char linebuf[MAXLEN];
68
69     if (!filename)
70         return;
71     if (!default_reason)
72         default_reason = "client is blacklisted";
73     file = fopen(filename, "r");
74     if (!file) {
75         log_module(bl_log, LOG_ERROR, "Unable to open %s for reading: %s", filename, strerror(errno));
76         return;
77     }
78     log_module(bl_log, LOG_DEBUG, "Loading blacklist from %s.", filename);
79     while (fgets(linebuf, sizeof(linebuf), file)) {
80         /* Trim whitespace from end of line. */
81         len = strlen(linebuf);
82         while (isspace(linebuf[len-1]))
83             linebuf[--len] = '\0';
84
85         /* Figure out which reason string we should use. */
86         reason = default_reason;
87         sep = strchr(linebuf, ' ');
88         if (sep) {
89             *sep++ = '\0';
90             while (isspace(*sep))
91                 sep++;
92             if (*sep != '\0')
93                 reason = sep;
94         }
95
96         /* See if the reason string is already known. */
97         mapped_reason = dict_find(blacklist_reasons, reason, NULL);
98         if (!mapped_reason) {
99             mapped_reason = strdup(reason);
100             dict_insert(blacklist_reasons, mapped_reason, (char*)mapped_reason);
101         }
102
103         /* Store the blacklist entry. */
104         dict_insert(blacklist_hosts, strdup(linebuf), mapped_reason);
105     }
106     fclose(file);
107 }
108
109 static void
110 blacklist_conf_read(void)
111 {
112     dict_t node;
113     const char *str1;
114     const char *str2;
115
116     dict_delete(blacklist_hosts);
117     blacklist_hosts = dict_new();
118     dict_set_free_keys(blacklist_hosts, free);
119
120     dict_delete(blacklist_reasons);
121     blacklist_reasons = dict_new();
122     dict_set_free_keys(blacklist_reasons, free);
123
124     node = conf_get_data("modules/blacklist", RECDB_OBJECT);
125     if (node == NULL)
126         return;
127
128     str1 = database_get_data(node, "file", RECDB_QSTRING);
129     str2 = database_get_data(node, "file_reason", RECDB_QSTRING);
130     blacklist_load_file(str1, str2);
131
132     str1 = database_get_data(node, "gline_duration", RECDB_QSTRING);
133     if (str1 == NULL)
134         str1 = "1h";
135     conf.gline_duration = ParseInterval(str1);
136 }
137
138 static void
139 blacklist_cleanup(void)
140 {
141     dict_delete(blacklist_hosts);
142     dict_delete(blacklist_reasons);
143 }
144
145 int
146 blacklist_init(void)
147 {
148     bl_log = log_register_type("Blacklist", "file:blacklist.log");
149     conf_register_reload(blacklist_conf_read);
150     reg_new_user_func(blacklist_check_user);
151     reg_exit_func(blacklist_cleanup);
152     return 1;
153 }
154
155 int
156 blacklist_finalize(void)
157 {
158     return 1;
159 }