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