Statistics
| Revision:

root / trunk / src / mod_evasive.c

History | View | Annotate | Download (4.44 KB)

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

    
28
typedef struct {
29
        unsigned short max_conns;
30
} plugin_config;
31

    
32
typedef struct {
33
        PLUGIN_DATA;
34

    
35
        plugin_config **config_storage;
36

    
37
        plugin_config conf;
38
} plugin_data;
39

    
40
INIT_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

    
50
FREE_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

    
72
SETDEFAULTS_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

    
101
static 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

    
128
URIHANDLER_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

    
187
LI_EXPORT int mod_evasive_plugin_init(plugin *p);
188
LI_EXPORT int mod_evasive_plugin_init(plugin *p) {
189
        p->version     = LIGHTTPD_VERSION_ID;
190
        p->name        = buffer_init_string("evasive");
191

    
192
        p->init        = mod_evasive_init;
193
        p->set_defaults = mod_evasive_set_defaults;
194
        p->handle_uri_clean  = mod_evasive_uri_handler;
195
        p->cleanup     = mod_evasive_free;
196

    
197
        p->data        = NULL;
198

    
199
        return 0;
200
}