10 #include "inet_ntop_cache.h"
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
28 * - w1zzard@techpowerup.com
32 unsigned short max_conns
;
33 unsigned short silent
;
40 plugin_config
**config_storage
;
45 INIT_FUNC(mod_evasive_init
) {
48 p
= calloc(1, sizeof(*p
));
53 FREE_FUNC(mod_evasive_free
) {
58 if (!p
) return HANDLER_GO_ON
;
60 if (p
->config_storage
) {
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
);
71 free(p
->config_storage
);
79 SETDEFAULTS_FUNC(mod_evasive_set_defaults
) {
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
];
96 s
= calloc(1, sizeof(plugin_config
));
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
;
117 static int mod_evasive_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
119 plugin_config
*s
= p
->config_storage
[0];
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;
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"))) {
139 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("evasive.silent"))) {
141 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("evasive.location"))) {
151 URIHANDLER_FUNC(mod_evasive_uri_handler
) {
152 plugin_data
*p
= p_d
;
153 size_t conns_by_ip
= 0;
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
) {
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
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
) {
184 if (c
->dst_addr
.ipv4
.sin_addr
.s_addr
!= con
->dst_addr
.ipv4
.sin_addr
.s_addr
) continue;
188 if (0 != memcmp(c
->dst_addr
.ipv6
.sin6_addr
.s6_addr
, con
->dst_addr
.ipv6
.sin6_addr
.s6_addr
, 16)) continue;
191 default: /* Address family not supported, should never be reached */
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;
208 con
->http_status
= 403;
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
;