1 #include "base.h"
2 #include "log.h"
3 #include "buffer.h"
4
5 #include "plugin.h"
6
7 #include "inet_ntop_cache.h"
8
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.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 * - [email protected]
26 */
27
28 typedef struct {
29 unsigned short max_conns;
30 unsigned short silent;
31 } plugin_config;
32
33 typedef struct {
34 PLUGIN_DATA;
35
36 plugin_config **config_storage;
37
38 plugin_config conf;
39 } plugin_data;
40
INIT_FUNC(mod_evasive_init)41 INIT_FUNC(mod_evasive_init) {
42 plugin_data *p;
43
44 p = calloc(1, sizeof(*p));
45
46 return p;
47 }
48
FREE_FUNC(mod_evasive_free)49 FREE_FUNC(mod_evasive_free) {
50 plugin_data *p = p_d;
51
52 UNUSED(srv);
53
54 if (!p) return HANDLER_GO_ON;
55
56 if (p->config_storage) {
57 size_t i;
58 for (i = 0; i < srv->config_context->used; i++) {
59 plugin_config *s = p->config_storage[i];
60
61 free(s);
62 }
63 free(p->config_storage);
64 }
65
66 free(p);
67
68 return HANDLER_GO_ON;
69 }
70
SETDEFAULTS_FUNC(mod_evasive_set_defaults)71 SETDEFAULTS_FUNC(mod_evasive_set_defaults) {
72 plugin_data *p = p_d;
73 size_t i = 0;
74
75 config_values_t cv[] = {
76 { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
77 { "evasive.silent", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
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 s->silent = 0;
89
90 cv[0].destination = &(s->max_conns);
91 cv[1].destination = &(s->silent);
92
93 p->config_storage[i] = s;
94
95 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
96 return HANDLER_ERROR;
97 }
98 }
99
100 return HANDLER_GO_ON;
101 }
102
103 #define PATCH(x) \
104 p->conf.x = s->x;
mod_evasive_patch_connection(server * srv,connection * con,plugin_data * p)105 static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) {
106 size_t i, j;
107 plugin_config *s = p->config_storage[0];
108
109 PATCH(max_conns);
110 PATCH(silent);
111
112 /* skip the first, the global context */
113 for (i = 1; i < srv->config_context->used; i++) {
114 data_config *dc = (data_config *)srv->config_context->data[i];
115 s = p->config_storage[i];
116
117 /* condition didn't match */
118 if (!config_check_cond(srv, con, dc)) continue;
119
120 /* merge config */
121 for (j = 0; j < dc->value->used; j++) {
122 data_unset *du = dc->value->data[j];
123
124 if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) {
125 PATCH(max_conns);
126 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.silent"))) {
127 PATCH(silent);
128 }
129 }
130 }
131
132 return 0;
133 }
134 #undef PATCH
135
URIHANDLER_FUNC(mod_evasive_uri_handler)136 URIHANDLER_FUNC(mod_evasive_uri_handler) {
137 plugin_data *p = p_d;
138 size_t conns_by_ip = 0;
139 size_t j;
140
141 if (con->uri.path->used == 0) return HANDLER_GO_ON;
142
143 mod_evasive_patch_connection(srv, con, p);
144
145 /* no limit set, nothing to block */
146 if (p->conf.max_conns == 0) return HANDLER_GO_ON;
147
148 switch (con->dst_addr.plain.sa_family) {
149 case AF_INET:
150 #ifdef HAVE_IPV6
151 case AF_INET6:
152 #endif
153 break;
154 default: /* Address family not supported */
155 return HANDLER_GO_ON;
156 };
157
158 for (j = 0; j < srv->conns->used; j++) {
159 connection *c = srv->conns->ptr[j];
160
161 /* check if other connections are already actively serving data for the same IP
162 * we can only ban connections which are already behind the 'read request' state
163 * */
164 if (c->dst_addr.plain.sa_family != con->dst_addr.plain.sa_family) continue;
165 if (c->state <= CON_STATE_REQUEST_END) continue;
166
167 switch (con->dst_addr.plain.sa_family) {
168 case AF_INET:
169 if (c->dst_addr.ipv4.sin_addr.s_addr != con->dst_addr.ipv4.sin_addr.s_addr) continue;
170 break;
171 #ifdef HAVE_IPV6
172 case AF_INET6:
173 if (0 != memcmp(c->dst_addr.ipv6.sin6_addr.s6_addr, con->dst_addr.ipv6.sin6_addr.s6_addr, 16)) continue;
174 break;
175 #endif
176 default: /* Address family not supported, should never be reached */
177 continue;
178 };
179 conns_by_ip++;
180
181 if (conns_by_ip > p->conf.max_conns) {
182 if (!p->conf.silent) {
183 log_error_write(srv, __FILE__, __LINE__, "ss",
184 inet_ntop_cache_get_ip(srv, &(con->dst_addr)),
185 "turned away. Too many connections.");
186 }
187
188 con->http_status = 403;
189 con->mode = DIRECT;
190 return HANDLER_FINISHED;
191 }
192 }
193
194 return HANDLER_GO_ON;
195 }
196
197
198 int mod_evasive_plugin_init(plugin *p);
mod_evasive_plugin_init(plugin * p)199 int mod_evasive_plugin_init(plugin *p) {
200 p->version = LIGHTTPD_VERSION_ID;
201 p->name = buffer_init_string("evasive");
202
203 p->init = mod_evasive_init;
204 p->set_defaults = mod_evasive_set_defaults;
205 p->handle_uri_clean = mod_evasive_uri_handler;
206 p->cleanup = mod_evasive_free;
207
208 p->data = NULL;
209
210 return 0;
211 }
212