File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_evasive.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 10:32:47 2013 UTC (10 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_33, HEAD
1.4.33

    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:  * - w1zzard@techpowerup.com
   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: 
   41: INIT_FUNC(mod_evasive_init) {
   42: 	plugin_data *p;
   43: 
   44: 	p = calloc(1, sizeof(*p));
   45: 
   46: 	return p;
   47: }
   48: 
   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: 
   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;
  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: 
  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);
  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: }

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