Annotation of embedaddon/lighttpd/src/mod_evasive.c, revision 1.1.1.3

1.1.1.3 ! misho       1: #include "first.h"
        !             2: 
1.1       misho       3: #include "base.h"
                      4: #include "log.h"
                      5: #include "buffer.h"
1.1.1.3 ! misho       6: #include "response.h"
1.1       misho       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;
1.1.1.3 ! misho      34:        buffer *location;
1.1       misho      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: 
1.1.1.3 ! misho      65:                        if (NULL == s) continue;
        !            66: 
        !            67:                        buffer_free(s->location);
        !            68: 
1.1       misho      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 */
1.1.1.3 ! misho      86:                { "evasive.location",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },  /* 2 */
1.1       misho      87:                { NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
                     88:        };
                     89: 
1.1.1.2   misho      90:        p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1       misho      91: 
                     92:        for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho      93:                data_config const* config = (data_config const*)srv->config_context->data[i];
1.1       misho      94:                plugin_config *s;
                     95: 
                     96:                s = calloc(1, sizeof(plugin_config));
                     97:                s->max_conns       = 0;
                     98:                s->silent          = 0;
1.1.1.3 ! misho      99:                s->location        = buffer_init();
1.1       misho     100: 
                    101:                cv[0].destination = &(s->max_conns);
                    102:                cv[1].destination = &(s->silent);
1.1.1.3 ! misho     103:                cv[2].destination = s->location;
1.1       misho     104: 
                    105:                p->config_storage[i] = s;
                    106: 
1.1.1.3 ! misho     107:                if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1       misho     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);
1.1.1.3 ! misho     123:        PATCH(location);
1.1       misho     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);
1.1.1.3 ! misho     141:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.location"))) {
        !           142:                                PATCH(location);
1.1       misho     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: 
1.1.1.3 ! misho     156:        if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
1.1       misho     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: 
1.1.1.3 ! misho     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:                        }
1.1       misho     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>