Ticket #1214: mod_evasive.c

File mod_evasive.c, 4.7 kB (added by tsj5j, 17 months ago)

mod_evasive updated. just replace old one for it to work. tested.

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        unsigned short enable_logging;
31        unsigned short error_code;
32} plugin_config;
33
34typedef struct {
35        PLUGIN_DATA;
36
37        plugin_config **config_storage;
38
39        plugin_config conf;
40} plugin_data;
41
42INIT_FUNC(mod_evasive_init) {
43        plugin_data *p;
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                { "evasive.enable-logging",      NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },
79                { "evasive.error-code",          NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },
80                { NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
81        };
82
83        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
84
85        for (i = 0; i < srv->config_context->used; i++) {
86                plugin_config *s;
87
88                s = calloc(1, sizeof(plugin_config));
89                s->max_conns       = 0;
90                s->enable_logging  = 0;
91                s->error_code      = 503;
92               
93                cv[0].destination = &(s->max_conns);
94                cv[1].destination = &(s->enable_logging);
95                cv[2].destination = &(s->error_code);
96               
97                p->config_storage[i] = s;
98
99                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
100                        return HANDLER_ERROR;
101                }
102        }
103
104        return HANDLER_GO_ON;
105}
106
107#define PATCH(x) \
108        p->conf.x = s->x;
109static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) {
110        size_t i, j;
111        plugin_config *s = p->config_storage[0];
112
113        PATCH(max_conns);
114        PATCH(enable_logging);
115        PATCH(error_code);
116
117        /* skip the first, the global context */
118        for (i = 1; i < srv->config_context->used; i++) {
119                data_config *dc = (data_config *)srv->config_context->data[i];
120                s = p->config_storage[i];
121
122                /* condition didn't match */
123                if (!config_check_cond(srv, con, dc)) continue;
124
125                /* merge config */
126                for (j = 0; j < dc->value->used; j++) {
127                        data_unset *du = dc->value->data[j];
128
129                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) {
130                                PATCH(max_conns);
131                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.enable-logging"))) {
132                                PATCH(enable_logging);
133                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.error-code"))) {
134                                PATCH(error_code);
135                        }
136                }
137        }
138
139        return 0;
140}
141#undef PATCH
142
143URIHANDLER_FUNC(mod_evasive_uri_handler) {
144        plugin_data *p = p_d;
145        size_t conns_by_ip = 0;
146        size_t j;
147
148        if (con->uri.path->used == 0) return HANDLER_GO_ON;
149
150        mod_evasive_patch_connection(srv, con, p);
151
152        /* no limit set, nothing to block */
153        if (p->conf.max_conns == 0) return HANDLER_GO_ON;
154
155        for (j = 0; j < srv->conns->used; j++) {
156                connection *c = srv->conns->ptr[j];
157
158                /* check if other connections are already actively serving data for the same IP
159                 * we can only ban connections which are already behind the 'read request' state
160                 * */
161                if (c->dst_addr.ipv4.sin_addr.s_addr == con->dst_addr.ipv4.sin_addr.s_addr &&
162                    c->state > CON_STATE_REQUEST_END) {
163                        conns_by_ip++;
164
165                        if (conns_by_ip > p->conf.max_conns) {
166                                if(p->conf.enable_logging) {
167                                        log_error_write(srv, __FILE__, __LINE__, "ss",
168                                                inet_ntop_cache_get_ip(srv, &(con->dst_addr)),
169                                                "turned away. Too many connections.");
170                                }
171                                if(p->conf.error_code > 99 && p->conf.error_code < 506) {
172                                        con->http_status = p->conf.error_code;
173                                } else {
174                                        con->http_status = 503;
175                                }
176                                return HANDLER_FINISHED;       
177                        }
178                }
179        }
180
181        return HANDLER_GO_ON;
182}
183
184
185int mod_evasive_plugin_init(plugin *p) {
186        p->version     = LIGHTTPD_VERSION_ID;
187        p->name        = buffer_init_string("evasive");
188
189        p->init        = mod_evasive_init;
190        p->set_defaults = mod_evasive_set_defaults;
191        p->handle_uri_clean  = mod_evasive_uri_handler;
192        p->cleanup     = mod_evasive_free;
193
194        p->data        = NULL;
195
196        return 0;
197}