Annotation of embedaddon/lighttpd/src/mod_evasive.c, revision 1.1
1.1 ! misho 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>