"Fossies" - the Fresh Open Source Software Archive

Member "lighttpd-1.4.54/src/mod_evasive.c" (27 May 2019, 5005 Bytes) of package /linux/www/lighttpd-1.4.54.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mod_evasive.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.4.53_vs_1.4.54.

    1 #include "first.h"
    2 
    3 #include "base.h"
    4 #include "log.h"
    5 #include "buffer.h"
    6 #include "http_header.h"
    7 #include "sock_addr.h"
    8 
    9 #include "plugin.h"
   10 
   11 #include <stdlib.h>
   12 #include <string.h>
   13 
   14 /**
   15  * mod_evasive
   16  *
   17  * we indent to implement all features the mod_evasive from apache has
   18  *
   19  * - limit of connections per IP
   20  * - provide a list of block-listed ip/networks (no access)
   21  * - provide a white-list of ips/network which is not affected by the limit
   22  *   (hmm, conditionals might be enough)
   23  * - provide a bandwidth limiter per IP
   24  *
   25  * started by:
   26  * - w1zzard@techpowerup.com
   27  */
   28 
   29 typedef struct {
   30     unsigned short max_conns;
   31     unsigned short silent;
   32     buffer *location;
   33 } plugin_config;
   34 
   35 typedef struct {
   36     PLUGIN_DATA;
   37 
   38     plugin_config **config_storage;
   39 
   40     plugin_config conf;
   41 } plugin_data;
   42 
   43 INIT_FUNC(mod_evasive_init) {
   44     plugin_data *p;
   45 
   46     p = calloc(1, sizeof(*p));
   47 
   48     return p;
   49 }
   50 
   51 FREE_FUNC(mod_evasive_free) {
   52     plugin_data *p = p_d;
   53 
   54     UNUSED(srv);
   55 
   56     if (!p) return HANDLER_GO_ON;
   57 
   58     if (p->config_storage) {
   59         size_t i;
   60         for (i = 0; i < srv->config_context->used; i++) {
   61             plugin_config *s = p->config_storage[i];
   62 
   63             if (NULL == s) continue;
   64 
   65             buffer_free(s->location);
   66 
   67             free(s);
   68         }
   69         free(p->config_storage);
   70     }
   71 
   72     free(p);
   73 
   74     return HANDLER_GO_ON;
   75 }
   76 
   77 SETDEFAULTS_FUNC(mod_evasive_set_defaults) {
   78     plugin_data *p = p_d;
   79     size_t i = 0;
   80 
   81     config_values_t cv[] = {
   82         { "evasive.max-conns-per-ip",    NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },   /* 0 */
   83         { "evasive.silent",              NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
   84         { "evasive.location",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },  /* 2 */
   85         { NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
   86     };
   87 
   88     p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
   89 
   90     for (i = 0; i < srv->config_context->used; i++) {
   91         data_config const* config = (data_config const*)srv->config_context->data[i];
   92         plugin_config *s;
   93 
   94         s = calloc(1, sizeof(plugin_config));
   95         s->max_conns       = 0;
   96         s->silent          = 0;
   97         s->location        = buffer_init();
   98 
   99         cv[0].destination = &(s->max_conns);
  100         cv[1].destination = &(s->silent);
  101         cv[2].destination = s->location;
  102 
  103         p->config_storage[i] = s;
  104 
  105         if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
  106             return HANDLER_ERROR;
  107         }
  108     }
  109 
  110     return HANDLER_GO_ON;
  111 }
  112 
  113 #define PATCH(x) \
  114     p->conf.x = s->x;
  115 static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) {
  116     size_t i, j;
  117     plugin_config *s = p->config_storage[0];
  118 
  119     PATCH(max_conns);
  120     PATCH(silent);
  121     PATCH(location);
  122 
  123     /* skip the first, the global context */
  124     for (i = 1; i < srv->config_context->used; i++) {
  125         data_config *dc = (data_config *)srv->config_context->data[i];
  126         s = p->config_storage[i];
  127 
  128         /* condition didn't match */
  129         if (!config_check_cond(srv, con, dc)) continue;
  130 
  131         /* merge config */
  132         for (j = 0; j < dc->value->used; j++) {
  133             data_unset *du = dc->value->data[j];
  134 
  135             if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) {
  136                 PATCH(max_conns);
  137             } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.silent"))) {
  138                 PATCH(silent);
  139             } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.location"))) {
  140                 PATCH(location);
  141             }
  142         }
  143     }
  144 
  145     return 0;
  146 }
  147 #undef PATCH
  148 
  149 URIHANDLER_FUNC(mod_evasive_uri_handler) {
  150     plugin_data *p = p_d;
  151     size_t conns_by_ip = 0;
  152     size_t j;
  153 
  154     if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
  155 
  156     mod_evasive_patch_connection(srv, con, p);
  157 
  158     /* no limit set, nothing to block */
  159     if (p->conf.max_conns == 0) return HANDLER_GO_ON;
  160 
  161     for (j = 0; j < srv->conns->used; j++) {
  162         connection *c = srv->conns->ptr[j];
  163 
  164         /* check if other connections are already actively serving data for the same IP
  165          * we can only ban connections which are already behind the 'read request' state
  166          * */
  167         if (c->state <= CON_STATE_REQUEST_END) continue;
  168 
  169         if (!sock_addr_is_addr_eq(&c->dst_addr, &con->dst_addr)) continue;
  170         conns_by_ip++;
  171 
  172         if (conns_by_ip > p->conf.max_conns) {
  173             if (!p->conf.silent) {
  174                 log_error_write(srv, __FILE__, __LINE__, "bs",
  175                     con->dst_addr_buf,
  176                     "turned away. Too many connections.");
  177             }
  178 
  179             if (!buffer_is_empty(p->conf.location)) {
  180                 http_header_response_set(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.location));
  181                 con->http_status = 302;
  182                 con->file_finished = 1;
  183             } else {
  184                 con->http_status = 403;
  185             }
  186             con->mode = DIRECT;
  187             return HANDLER_FINISHED;
  188         }
  189     }
  190 
  191     return HANDLER_GO_ON;
  192 }
  193 
  194 
  195 int mod_evasive_plugin_init(plugin *p);
  196 int mod_evasive_plugin_init(plugin *p) {
  197     p->version     = LIGHTTPD_VERSION_ID;
  198     p->name        = buffer_init_string("evasive");
  199 
  200     p->init        = mod_evasive_init;
  201     p->set_defaults = mod_evasive_set_defaults;
  202     p->handle_uri_clean  = mod_evasive_uri_handler;
  203     p->cleanup     = mod_evasive_free;
  204 
  205     p->data        = NULL;
  206 
  207     return 0;
  208 }