File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_evasive.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 7 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

    1: #include "first.h"
    2: 
    3: #include "base.h"
    4: #include "log.h"
    5: #include "buffer.h"
    6: #include "response.h"
    7: 
    8: #include "plugin.h"
    9: 
   10: #include "inet_ntop_cache.h"
   11: 
   12: #include <ctype.h>
   13: #include <stdlib.h>
   14: #include <string.h>
   15: 
   16: /**
   17:  * mod_evasive
   18:  *
   19:  * we indent to implement all features the mod_evasive from apache has
   20:  *
   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
   26:  *
   27:  * started by:
   28:  * - w1zzard@techpowerup.com
   29:  */
   30: 
   31: typedef struct {
   32: 	unsigned short max_conns;
   33: 	unsigned short silent;
   34: 	buffer *location;
   35: } plugin_config;
   36: 
   37: typedef struct {
   38: 	PLUGIN_DATA;
   39: 
   40: 	plugin_config **config_storage;
   41: 
   42: 	plugin_config conf;
   43: } plugin_data;
   44: 
   45: INIT_FUNC(mod_evasive_init) {
   46: 	plugin_data *p;
   47: 
   48: 	p = calloc(1, sizeof(*p));
   49: 
   50: 	return p;
   51: }
   52: 
   53: FREE_FUNC(mod_evasive_free) {
   54: 	plugin_data *p = p_d;
   55: 
   56: 	UNUSED(srv);
   57: 
   58: 	if (!p) return HANDLER_GO_ON;
   59: 
   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];
   64: 
   65: 			if (NULL == s) continue;
   66: 
   67: 			buffer_free(s->location);
   68: 
   69: 			free(s);
   70: 		}
   71: 		free(p->config_storage);
   72: 	}
   73: 
   74: 	free(p);
   75: 
   76: 	return HANDLER_GO_ON;
   77: }
   78: 
   79: SETDEFAULTS_FUNC(mod_evasive_set_defaults) {
   80: 	plugin_data *p = p_d;
   81: 	size_t i = 0;
   82: 
   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 }
   88: 	};
   89: 
   90: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
   91: 
   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;
   95: 
   96: 		s = calloc(1, sizeof(plugin_config));
   97: 		s->max_conns       = 0;
   98: 		s->silent          = 0;
   99: 		s->location        = buffer_init();
  100: 
  101: 		cv[0].destination = &(s->max_conns);
  102: 		cv[1].destination = &(s->silent);
  103: 		cv[2].destination = s->location;
  104: 
  105: 		p->config_storage[i] = s;
  106: 
  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;
  109: 		}
  110: 	}
  111: 
  112: 	return HANDLER_GO_ON;
  113: }
  114: 
  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];
  120: 
  121: 	PATCH(max_conns);
  122: 	PATCH(silent);
  123: 	PATCH(location);
  124: 
  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];
  129: 
  130: 		/* condition didn't match */
  131: 		if (!config_check_cond(srv, con, dc)) continue;
  132: 
  133: 		/* merge config */
  134: 		for (j = 0; j < dc->value->used; j++) {
  135: 			data_unset *du = dc->value->data[j];
  136: 
  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);
  143: 			}
  144: 		}
  145: 	}
  146: 
  147: 	return 0;
  148: }
  149: #undef PATCH
  150: 
  151: URIHANDLER_FUNC(mod_evasive_uri_handler) {
  152: 	plugin_data *p = p_d;
  153: 	size_t conns_by_ip = 0;
  154: 	size_t j;
  155: 
  156: 	if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
  157: 
  158: 	mod_evasive_patch_connection(srv, con, p);
  159: 
  160: 	/* no limit set, nothing to block */
  161: 	if (p->conf.max_conns == 0) return HANDLER_GO_ON;
  162: 
  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;
  171: 	};
  172: 
  173: 	for (j = 0; j < srv->conns->used; j++) {
  174: 		connection *c = srv->conns->ptr[j];
  175: 
  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;
  181: 
  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;
  193: 		};
  194: 		conns_by_ip++;
  195: 
  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.");
  201: 			}
  202: 
  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;
  209: 			}
  210: 			con->mode = DIRECT;
  211: 			return HANDLER_FINISHED;
  212: 		}
  213: 	}
  214: 
  215: 	return HANDLER_GO_ON;
  216: }
  217: 
  218: 
  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");
  223: 
  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;
  228: 
  229: 	p->data        = NULL;
  230: 
  231: 	return 0;
  232: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>