[core] avoid spurious trace and error abort
[lighttpd.git] / src / mod_evasive.c
blob3aebb986eb7d0549be48cde79b9d60b7a84878e0
1 #include "first.h"
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 #include "response.h"
8 #include "plugin.h"
10 #include "inet_ntop_cache.h"
12 #include <ctype.h>
13 #include <stdlib.h>
14 #include <string.h>
16 /**
17 * mod_evasive
19 * we indent to implement all features the mod_evasive from apache has
21 * - limit of connections per IP
22 * - provide a list of block-listed ip/networks (no access)
23 * - provide a white-list of ips/network which is not affected by the limit
24 * (hmm, conditionals might be enough)
25 * - provide a bandwidth limiter per IP
27 * started by:
28 * - w1zzard@techpowerup.com
31 typedef struct {
32 unsigned short max_conns;
33 unsigned short silent;
34 buffer *location;
35 } plugin_config;
37 typedef struct {
38 PLUGIN_DATA;
40 plugin_config **config_storage;
42 plugin_config conf;
43 } plugin_data;
45 INIT_FUNC(mod_evasive_init) {
46 plugin_data *p;
48 p = calloc(1, sizeof(*p));
50 return p;
53 FREE_FUNC(mod_evasive_free) {
54 plugin_data *p = p_d;
56 UNUSED(srv);
58 if (!p) return HANDLER_GO_ON;
60 if (p->config_storage) {
61 size_t i;
62 for (i = 0; i < srv->config_context->used; i++) {
63 plugin_config *s = p->config_storage[i];
65 if (NULL == s) continue;
67 buffer_free(s->location);
69 free(s);
71 free(p->config_storage);
74 free(p);
76 return HANDLER_GO_ON;
79 SETDEFAULTS_FUNC(mod_evasive_set_defaults) {
80 plugin_data *p = p_d;
81 size_t i = 0;
83 config_values_t cv[] = {
84 { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
85 { "evasive.silent", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
86 { "evasive.location", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
87 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
90 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
92 for (i = 0; i < srv->config_context->used; i++) {
93 data_config const* config = (data_config const*)srv->config_context->data[i];
94 plugin_config *s;
96 s = calloc(1, sizeof(plugin_config));
97 s->max_conns = 0;
98 s->silent = 0;
99 s->location = buffer_init();
101 cv[0].destination = &(s->max_conns);
102 cv[1].destination = &(s->silent);
103 cv[2].destination = s->location;
105 p->config_storage[i] = s;
107 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
108 return HANDLER_ERROR;
112 return HANDLER_GO_ON;
115 #define PATCH(x) \
116 p->conf.x = s->x;
117 static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) {
118 size_t i, j;
119 plugin_config *s = p->config_storage[0];
121 PATCH(max_conns);
122 PATCH(silent);
123 PATCH(location);
125 /* skip the first, the global context */
126 for (i = 1; i < srv->config_context->used; i++) {
127 data_config *dc = (data_config *)srv->config_context->data[i];
128 s = p->config_storage[i];
130 /* condition didn't match */
131 if (!config_check_cond(srv, con, dc)) continue;
133 /* merge config */
134 for (j = 0; j < dc->value->used; j++) {
135 data_unset *du = dc->value->data[j];
137 if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) {
138 PATCH(max_conns);
139 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.silent"))) {
140 PATCH(silent);
141 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.location"))) {
142 PATCH(location);
147 return 0;
149 #undef PATCH
151 URIHANDLER_FUNC(mod_evasive_uri_handler) {
152 plugin_data *p = p_d;
153 size_t conns_by_ip = 0;
154 size_t j;
156 if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
158 mod_evasive_patch_connection(srv, con, p);
160 /* no limit set, nothing to block */
161 if (p->conf.max_conns == 0) return HANDLER_GO_ON;
163 switch (con->dst_addr.plain.sa_family) {
164 case AF_INET:
165 #ifdef HAVE_IPV6
166 case AF_INET6:
167 #endif
168 break;
169 default: /* Address family not supported */
170 return HANDLER_GO_ON;
173 for (j = 0; j < srv->conns->used; j++) {
174 connection *c = srv->conns->ptr[j];
176 /* check if other connections are already actively serving data for the same IP
177 * we can only ban connections which are already behind the 'read request' state
178 * */
179 if (c->dst_addr.plain.sa_family != con->dst_addr.plain.sa_family) continue;
180 if (c->state <= CON_STATE_REQUEST_END) continue;
182 switch (con->dst_addr.plain.sa_family) {
183 case AF_INET:
184 if (c->dst_addr.ipv4.sin_addr.s_addr != con->dst_addr.ipv4.sin_addr.s_addr) continue;
185 break;
186 #ifdef HAVE_IPV6
187 case AF_INET6:
188 if (0 != memcmp(c->dst_addr.ipv6.sin6_addr.s6_addr, con->dst_addr.ipv6.sin6_addr.s6_addr, 16)) continue;
189 break;
190 #endif
191 default: /* Address family not supported, should never be reached */
192 continue;
194 conns_by_ip++;
196 if (conns_by_ip > p->conf.max_conns) {
197 if (!p->conf.silent) {
198 log_error_write(srv, __FILE__, __LINE__, "ss",
199 inet_ntop_cache_get_ip(srv, &(con->dst_addr)),
200 "turned away. Too many connections.");
203 if (!buffer_is_empty(p->conf.location)) {
204 response_header_overwrite(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.location));
205 con->http_status = 302;
206 con->file_finished = 1;
207 } else {
208 con->http_status = 403;
210 con->mode = DIRECT;
211 return HANDLER_FINISHED;
215 return HANDLER_GO_ON;
219 int mod_evasive_plugin_init(plugin *p);
220 int mod_evasive_plugin_init(plugin *p) {
221 p->version = LIGHTTPD_VERSION_ID;
222 p->name = buffer_init_string("evasive");
224 p->init = mod_evasive_init;
225 p->set_defaults = mod_evasive_set_defaults;
226 p->handle_uri_clean = mod_evasive_uri_handler;
227 p->cleanup = mod_evasive_free;
229 p->data = NULL;
231 return 0;