root/trunk/src/mod_evasive.c

Revision 2224, 4.4 kB (checked in by stbuehler, 5 weeks ago)

Fix bug with IPv6 in mod_evasive

  • Property svn:eol-style set to native
Line 
1#include <ctype.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "base.h"
6#include "log.h"
7#include "buffer.h"
8
9#include "plugin.h"
10
11#include "inet_ntop_cache.h"
12
13/**
14 * mod_evasive
15 *
16 * we indent to implement all features the mod_evasive from apache has
17 *
18 * - limit of connections per IP
19 * - provide a list of block-listed ip/networks (no access)
20 * - provide a white-list of ips/network which is not affected by the limit
21 *   (hmm, conditionals might be enough)
22 * - provide a bandwidth limiter per IP
23 *
24 * started by:
25 * - w1zzard@techpowerup.com
26 */
27
28typedef struct {
29        unsigned short max_conns;
30} plugin_config;
31
32typedef struct {
33        PLUGIN_DATA;
34
35        plugin_config **config_storage;
36
37        plugin_config conf;
38} plugin_data;
39
40INIT_FUNC(mod_evasive_init) {
41        plugin_data *p;
42
43        UNUSED(srv);
44
45        p = calloc(1, sizeof(*p));
46
47        return p;
48}
49
50FREE_FUNC(mod_evasive_free) {
51        plugin_data *p = p_d;
52
53        UNUSED(srv);
54
55        if (!p) return HANDLER_GO_ON;
56
57        if (p->config_storage) {
58                size_t i;
59                for (i = 0; i < srv->config_context->used; i++) {
60                        plugin_config *s = p->config_storage[i];
61
62                        free(s);
63                }
64                free(p->config_storage);
65        }
66
67        free(p);
68
69        return HANDLER_GO_ON;
70}
71
72SETDEFAULTS_FUNC(mod_evasive_set_defaults) {
73        plugin_data *p = p_d;
74        size_t i = 0;
75
76        config_values_t cv[] = {
77                { "evasive.max-conns-per-ip",    NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
78                { NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
79        };
80
81        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
82
83        for (i = 0; i < srv->config_context->used; i++) {
84                plugin_config *s;
85
86                s = calloc(1, sizeof(plugin_config));
87                s->max_conns       = 0;
88
89                cv[0].destination = &(s->max_conns);
90
91                p->config_storage[i] = s;
92
93                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
94                        return HANDLER_ERROR;
95                }
96        }
97
98        return HANDLER_GO_ON;
99}
100
101static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) {
102        size_t i, j;
103        plugin_config *s = p->config_storage[0];
104
105        PATCH_OPTION(max_conns);
106
107        /* skip the first, the global context */
108        for (i = 1; i < srv->config_context->used; i++) {
109                data_config *dc = (data_config *)srv->config_context->data[i];
110                s = p->config_storage[i];
111
112                /* condition didn't match */
113                if (!config_check_cond(srv, con, dc)) continue;
114
115                /* merge config */
116                for (j = 0; j < dc->value->used; j++) {
117                        data_unset *du = dc->value->data[j];
118
119                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) {
120                                PATCH_OPTION(max_conns);
121                        }
122                }
123        }
124
125        return 0;
126}
127
128URIHANDLER_FUNC(mod_evasive_uri_handler) {
129        plugin_data *p = p_d;
130        size_t conns_by_ip = 0;
131        size_t j;
132
133        if (con->uri.path->used == 0) return HANDLER_GO_ON;
134
135        mod_evasive_patch_connection(srv, con, p);
136
137        /* no limit set, nothing to block */
138        if (p->conf.max_conns == 0) return HANDLER_GO_ON;
139
140        switch (con->dst_addr.plain.sa_family) {
141                case AF_INET:
142#ifdef HAVE_IPV6
143                case AF_INET6:
144#endif
145                        break;
146                default: // Address family not supported
147                        return HANDLER_GO_ON;
148        };
149
150        for (j = 0; j < srv->conns->used; j++) {
151                connection *c = srv->conns->ptr[j];
152
153                /* check if other connections are already actively serving data for the same IP
154                 * we can only ban connections which are already behind the 'read request' state
155                 * */
156                if (c->dst_addr.plain.sa_family != con->dst_addr.plain.sa_family) continue;
157                if (c->state <= CON_STATE_HANDLE_REQUEST_HEADER) continue;
158
159                switch (con->dst_addr.plain.sa_family) {
160                        case AF_INET:
161                                if (c->dst_addr.ipv4.sin_addr.s_addr != con->dst_addr.ipv4.sin_addr.s_addr) continue;
162                                break;
163#ifdef HAVE_IPV6
164                        case AF_INET6:
165                                if (0 != memcmp(c->dst_addr.ipv6.sin6_addr.s6_addr, con->dst_addr.ipv6.sin6_addr.s6_addr, 16)) continue;
166                                break;
167#endif
168                        default: // Address family not supported, should never be reached
169                                continue;
170                };
171                conns_by_ip++;
172
173                if (conns_by_ip > p->conf.max_conns) {
174                        log_error_write(srv, __FILE__, __LINE__, "ss",
175                                inet_ntop_cache_get_ip(srv, &(con->dst_addr)),
176                                "turned away. Too many connections.");
177
178                        con->http_status = 403;
179                        return HANDLER_FINISHED;
180                }
181        }
182
183        return HANDLER_GO_ON;
184}
185
186
187LI_EXPORT int mod_evasive_plugin_init(plugin *p) {
188        p->version     = LIGHTTPD_VERSION_ID;
189        p->name        = buffer_init_string("evasive");
190
191        p->init        = mod_evasive_init;
192        p->set_defaults = mod_evasive_set_defaults;
193        p->handle_uri_clean  = mod_evasive_uri_handler;
194        p->cleanup     = mod_evasive_free;
195
196        p->data        = NULL;
197
198        return 0;
199}
Note: See TracBrowser for help on using the browser.