"Fossies" - the Fresh Open Source Software Archive 
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.
1 /* Copyright 2011 Google Inc. All Rights Reserved.
2
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6
7 * http://www.apache.org/licenses/LICENSE-2.0
8
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "httpd.h"
17 #include "http_config.h"
18 #include "http_core.h"
19 #include "http_log.h"
20 #include "http_main.h"
21 #include "http_protocol.h"
22 #include "http_request.h"
23 #include "util_script.h"
24 #include "http_connection.h"
25
26 #include "apr_global_mutex.h"
27 #include "apr_strings.h"
28 #include "ap_config.h"
29 #include "apr_time.h"
30 #if APR_HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #if APR_HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #define RULEURL "url"
38 #define RULEHEADER "header"
39 #define ARSIZE 300 /* No of seconds that we use to calculate the rate. Default is rate over 5 mins */
40 #define MAXRULE 20 /* Max number of dos rules that can be configured */
41
42 #ifdef AP_NEED_SET_MUTEX_PERMS
43 #include "unixd.h"
44 #endif
45
46 /*--------------------------------------------------------------------------
47 * mod_dosblock was developed to fill the need for dos protection at the http
48 * layer. We hook at the post_read_request phase to catch problems early. It
49 * can be configured to act on the url or the http header. It does not the
50 * support combining two or more subrules. You can define a url based subrule
51 * using the following syntax
52 *
53 * DosBlockUrl <name of the url subrule> <pattern>
54 *
55 * example
56 *
57 * DosBlockUrl myurlrule /test
58 *
59 * Header based subrule can be defined as follows
60 * DosBlockHeader <name of the header subrule> <headername> <pattern>
61 *
62 * example
63 *
64 * DosBlockHeader myheaderrule User-Agent mozilla
65 *
66 * Then you use the following syntax to do the real blocking
67 * DosBlockRule <dos rule name>:<name of the subrule> <threshold rate (hits per
68 * sec)> <time to blocki ( in secs)>
69 *
70 * example
71 *
72 * DosBlockRule dosblock_header:myheaderrule 2 60
73 *
74 * A complete example
75 *
76 * DosBlockUrl myurlrule /test
77 * DosBlockRule blocktest:myurlrule 50 120
78 *
79 * In case you want to enable debugging you can set
80 *
81 * DosBlockVerbose On
82 *
83 * Its off by default
84 *
85 * I plan to add support for combining subrules in a subsequent release
86 *
87 ---------------------------------------------------------------------------*/
88
89 apr_shm_t *dosblockipc_shm[MAXRULE]; /* Array of pointers to shared memory blocks */
90 char *shmfilename; /* Shared memory file name, used on some systems */
91 char *mutex_filename;
92 apr_global_mutex_t *dosblockipc_mutex[MAXRULE]; /* Array of mutex locks around shared memory segments */
93
94 /* Config related data structure */
95 typedef struct {
96 apr_table_t *urlrules;
97 apr_hash_t *headerrules;
98 apr_table_t *ruletype; /* Table to store the type of the subrule i.e(url or header) */
99 apr_table_t *dosrulemap; /* Mapping between the dos rule and the subrule */
100 apr_hash_t *dosrules; /* This hash map stores in its value object of type dosrulestruct */
101 apr_hash_t *dosrule_shm_map; /* Mapping between the dos rule and the shared mem segment */
102 apr_table_t *dosrule_mutex_map; /* Mapping between the dos rule and the mutext */
103 int verbosity; /* Used to log messages based on what we have here */
104 }dosblock_cfg;
105
106 /* struct to hold info about header based subrule.
107 * Note that we do not need any struct for url based subrule as a normal table is sufficient.
108 */
109
110 typedef struct {
111 char *headername;
112 char *headerpattern;
113 }headerrulestruct;
114
115 /* Struct to hold info about the actual dos rule */
116 typedef struct {
117 char *subrule;
118 char *threshold;
119 char *timetoblock;
120 }dosrulestruct;
121
122 typedef struct dosblock_hits {
123 apr_time_t t;
124 apr_int64_t counter;
125 }dosblock_hits;
126
127 typedef struct dosblock_status {
128 apr_time_t t;
129 int isblocked;
130 int rate_when_blocked;
131 }dosblock_status;
132
133 /* Data structure for shared memory block */
134 typedef struct dosblockipc_data {
135 dosblock_hits dh[ARSIZE];
136 dosblock_status ds;
137 int next;
138 }dosblockipc_data;
139
140 dosblockipc_data *base = NULL;
141
142 module AP_MODULE_DECLARE_DATA mod_dosblock_module;
143
144 /* Set up all our data structs in the pre config phase */
145 static void * dosblock_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) {
146 dosblock_cfg *cfg = apr_pcalloc(p, sizeof(*cfg));
147
148 if (!cfg)
149 return NULL;
150 cfg->urlrules = apr_table_make(p, MAXRULE);
151 cfg->headerrules = apr_hash_make(p);
152 cfg->ruletype = apr_table_make(p, MAXRULE);
153 cfg->dosrulemap = apr_table_make(p, MAXRULE);
154 cfg->dosrules = apr_hash_make(p);
155 cfg->dosrule_shm_map = apr_hash_make(p);
156 cfg->dosrule_mutex_map = apr_table_make(p, MAXRULE);
157
158 return cfg;
159 }
160
161 /* Initialise the requisite data structs for our url based dos subrule
162 * @param word1 The name of the subrule
163 * @param word2 The pattern
164 */
165 static void *dosblock_url_config(cmd_parms *cmd, void *mconfig, char *word1, char *word2) {
166 server_rec *s = cmd->server;
167 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(s->module_config, &mod_dosblock_module);
168
169 if(word1 !=NULL && word2 !=NULL)
170 {
171 /* We test the regexp and populate the table only on success */
172 if (ap_pregcomp(cmd->pool, word2, AP_REG_EXTENDED) != NULL )
173 {
174 apr_table_set(cfg->urlrules, word1, word2);
175 apr_table_set(cfg->ruletype, word1, RULEURL);
176 }
177 else {
178 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Failed to compile regexp %s", word2);
179 }
180 }
181
182 return NULL;
183 }
184
185 /* Initialise the requisite data structs for our header based dos subrule
186 * @param word1 The name of the subrule
187 * @param word2 The http header to match
188 * @paramm word3 The pattern
189 */
190 static void *dosblock_header_config(cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3) {
191
192 server_rec *s = cmd->server;
193 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(s->module_config, &mod_dosblock_module);
194
195 if(word1 !=NULL && word2 !=NULL && word3 !=NULL)
196 {
197 headerrulestruct *h = NULL;
198 h = apr_palloc(cmd->pool, sizeof(headerrulestruct));
199 h->headername = word2;
200 h->headerpattern = word3;
201
202 /* We test the regexp and populate the table,hash only on success */
203 if (ap_pregcomp(cmd->pool, word3, AP_REG_EXTENDED) != NULL )
204 {
205 apr_hash_set(cfg->headerrules, word1, APR_HASH_KEY_STRING, h);
206 apr_table_set(cfg->ruletype, word1, RULEHEADER);
207 }
208 else {
209 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Failed to compile regexp %s", word3);
210 }
211
212 }
213 return NULL;
214 }
215
216 /* Initialise the requisite data structs for the real dos rule
217 * @param word1 The "dos rule name: subrule" format
218 * @param word2 The threshold rate
219 * @param word3 Time (epoch) till blockage
220 * */
221 static void *dosblockrule_config(cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3) {
222 server_rec *s = cmd->server;
223 char *dosrulename, *subrule, *saveptr1;
224 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(s->module_config, &mod_dosblock_module);
225
226 if(word1 !=NULL && word2 !=NULL && word3 !=NULL)
227 {
228 dosrulename = apr_strtok(word1, ":", &saveptr1);
229 subrule = apr_strtok(NULL, ":", &saveptr1);
230 dosrulestruct *d = NULL;
231 d = apr_palloc(cmd->pool, sizeof(dosrulestruct));
232 d->subrule = subrule;
233 d->threshold = word2;
234 d->timetoblock = word3;
235 /* apr_table_set ensures that the same rule is over-written, while for
236 * apr_hash_set we employ the following hack
237 */
238 apr_table_set(cfg->dosrulemap, dosrulename, subrule);
239 if (apr_hash_get(cfg->dosrules, dosrulename, APR_HASH_KEY_STRING) != NULL) {
240 /* There is an entry. Delete it. This is a hack so that we only store
241 * the latest entry of the same rule
242 */
243 apr_hash_set(cfg->dosrules, dosrulename, APR_HASH_KEY_STRING, NULL);
244
245 }
246 apr_hash_set(cfg->dosrules, dosrulename, APR_HASH_KEY_STRING, d);
247 }
248 return NULL;
249 }
250
251 /* Set the verbosity flag
252 */
253 static const char *dosblock_verbosityflag(cmd_parms *cmd, void *mconfig, int bool) {
254 server_rec *s = cmd->server;
255 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(s->module_config, &mod_dosblock_module);
256 cfg->verbosity = bool;
257 return NULL;
258 }
259
260 static command_rec mod_dosblock_cmds[] = {
261 AP_INIT_TAKE2("DosBlockUrl", dosblock_url_config, NULL, RSRC_CONF, "Url pattern to look for" ),
262 AP_INIT_TAKE3("DosBlockHeader", dosblock_header_config, NULL, RSRC_CONF, "Header pattern to look for" ),
263 AP_INIT_TAKE3("DosBlockRule", dosblockrule_config, NULL, RSRC_CONF, "Actual dos rule" ),
264 AP_INIT_FLAG("DosBlockVerbose", dosblock_verbosityflag, NULL, RSRC_CONF, "Sets the verbosity flag" ),
265
266 { NULL }
267 };
268
269 /*
270 * Clean up the shared memory blocks. This function is registered as
271 * cleanup function for the configuration pool, which gets called
272 * on restarts. It assures that the new children will not talk to a stale
273 * shared memory segments.
274 */
275 static apr_status_t shm_cleanup_wrapper() {
276 int i;
277
278 for (i=0; i<MAXRULE; i++) {
279 if (dosblockipc_shm[i]) {
280 apr_shm_destroy((apr_shm_t *)dosblockipc_shm[i]);
281 }
282 }
283 return OK;
284 }
285
286
287 /* Merge vserver configs */
288 static void *dosblock_config_server_merge(apr_pool_t *p, void *base_, void *vhost_) {
289
290 /* We do not support vhosts. The module configs are in the main apache conf */
291 return base_;
292 }
293
294 /*
295 * This routine is called in the parent, so we'll set up the shared
296 * memory segments and mutexs here.
297 */
298 static int dosblock_post_config(apr_pool_t *pconf, apr_pool_t *plog,
299 apr_pool_t *ptemp, server_rec *s)
300 {
301 void *data; /* These two help ensure that we only init once. */
302 const char *userdata_key;
303 apr_status_t rs;
304 const char *tempdir;
305 dosblockipc_data *base;
306 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(s->module_config, &mod_dosblock_module);
307 /*
308 * The following checks if this routine has been called before.
309 * This is necessary because the parent process gets initialized
310 * a couple of times as the server starts up, and we don't want
311 * to create any more mutexes and shared memory segments than
312 * we're actually going to use.
313 *
314 * The key needs to be unique for the entire web server, so put
315 * the module name in it.
316 */
317 userdata_key = "dosblock_ipc_init_module";
318 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
319
320 if (!data) {
321 /*
322 * If no data was found for our key, this must be the first
323 * time the module is initialized. Put some data under that
324 * key and return.
325 */
326 apr_pool_userdata_set((const void *) 1, userdata_key,
327 apr_pool_cleanup_null, s->process->pool);
328 return OK;
329 }
330
331 /*
332 * The shared memory allocation routines take a file name.
333 * Depending on system-specific implementation of these
334 * routines, that file may or may not actually be created. We'd
335 * like to store those files in the operating system's designated
336 * temporary directory, which APR can point us to.
337 */
338 rs = apr_temp_dir_get(&tempdir, pconf);
339 if (APR_SUCCESS != rs) {
340 ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
341 "Failed to find temporary directory");
342 return HTTP_INTERNAL_SERVER_ERROR;
343 }
344
345 /* Create the shared memory segments. We also populate a dosrule to shared
346 * memory mapping table with all the shared memory segments
347 */
348
349 const apr_array_header_t *tarr = apr_table_elts(cfg->dosrulemap);
350 const apr_table_entry_t *telts = (const apr_table_entry_t*)tarr->elts;
351 int i;
352
353 for (i = 0; i < tarr->nelts; ++i) {
354 /*
355 * Create a unique filename using our pid. This information is
356 * stashed in the global variable so the children inherit it.
357 */
358 shmfilename = apr_psprintf(pconf, "%s/httpd_shm_%s.%ld", tempdir,
359 telts[i].key, (long int)getpid());
360 mutex_filename = apr_psprintf(pconf, "%s/httpd_mutex_%s.%ld", tempdir,
361 telts[i].key, (long int)getpid());
362
363 /* Now create that shm segment. We prefer anonymous shm */
364 rs = apr_shm_create(&dosblockipc_shm[i], sizeof(dosblockipc_data),
365 NULL, pconf);
366 if (APR_ENOTIMPL == rs) {
367 rs = apr_shm_create(&dosblockipc_shm[i], sizeof(dosblockipc_data),
368 (const char *) shmfilename, pconf);
369 }
370 if (APR_SUCCESS != rs) {
371 ap_log_error(APLOG_MARK, APLOG_ERR, rs, s,
372 "Failed to create shared memory segment on file %s",
373 shmfilename);
374 return HTTP_INTERNAL_SERVER_ERROR;
375 }
376 apr_hash_set(cfg->dosrule_shm_map, telts[i].key, APR_HASH_KEY_STRING, apr_psprintf(pconf, "%d",i));
377 if (cfg->verbosity)
378 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Dos Rule configured is %s", telts[i].key);
379
380 base = (dosblockipc_data *)apr_shm_baseaddr_get(dosblockipc_shm[i]);
381 /* Now initialise the array of structs. Zero it out */
382 base->ds.t = 0;
383 base->ds.isblocked = 0;
384 base->ds.rate_when_blocked = 0;
385 base->next = 0;
386 int j;
387 for (j=0; j<ARSIZE; j++) {
388 base->dh[j].t = 0;
389 base->dh[j].counter = 0;
390
391 }
392 /* Create global mutex */
393
394 rs = apr_global_mutex_create(&dosblockipc_mutex[i], mutex_filename, APR_LOCK_DEFAULT, pconf);
395 if (APR_SUCCESS != rs) {
396 return HTTP_INTERNAL_SERVER_ERROR;
397 }
398 #ifdef AP_NEED_SET_MUTEX_PERMS
399 rs = unixd_set_global_mutex_perms(dosblockipc_mutex[i]);
400 if (rs != APR_SUCCESS) {
401 ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
402 "mod_dosblock: Parent could not set permissions "
403 "on shared memory; check User and Group directives");
404 return rs;
405 }
406 #endif
407
408 apr_table_set(cfg->dosrule_mutex_map, telts[i].key, mutex_filename);
409
410 }
411 /*
412 * Destroy the shm segment when the configuration pool gets destroyed. This
413 *happens on server restarts. The parent will then (above) allocate a new
414 * shm segment that the new children will bind to.
415 */
416
417 /*
418 apr_pool_cleanup_register(pconf, NULL, shm_cleanup_wrapper(),
419 apr_pool_cleanup_null);
420 */
421 return OK;
422 }
423
424 /*
425 * This routine gets called when a child inits. We use it to attach
426 * to the shared memory segments, and reinitialize the corresponding mutex.
427 */
428 static void dosblock_child_init(apr_pool_t *p, server_rec *s)
429 {
430 apr_status_t rs ;
431 const apr_table_entry_t *dosrule_mutex_map_elts = NULL;
432 const apr_array_header_t *dosrule_mutex_map_arr = NULL;
433 /*
434 * Re-open the mutexes for the child. Note we're reusing
435 * the mutex pointer global here.
436 */
437 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(s->module_config, &mod_dosblock_module);
438 dosrule_mutex_map_arr = apr_table_elts(cfg->dosrule_mutex_map);
439
440 if (dosrule_mutex_map_arr) {
441 dosrule_mutex_map_elts = (const apr_table_entry_t *)dosrule_mutex_map_arr->elts;
442 int i = 0;
443 for (i=0; i<dosrule_mutex_map_arr->nelts; ++i)
444 {
445 rs = apr_global_mutex_child_init(&dosblockipc_mutex[i], dosrule_mutex_map_elts[i].val, p);
446 if (APR_SUCCESS != rs) {
447 ap_log_error(APLOG_MARK, APLOG_CRIT, rs, s,
448 "Failed to reopen mutex %s in child",
449 dosblockipc_mutex[i]);
450 /* There's really nothing else we can do here, since This
451 * routine doesn't return a status. If this ever goes wrong,
452 * it will turn Apache into a fork bomb. Let's hope it never
453 * will.
454 */
455 exit(1); /* Ugly, but what else? */
456 }
457 }
458 }
459 }
460
461 /* Here-in lies the meat */
462 static int dosblock_main(request_rec *r) {
463 const apr_table_entry_t *dosrulemap_elts = NULL;
464 const apr_array_header_t *dosrulemap_arr = NULL;
465 char *subrule=NULL, *dosrulematch=NULL, *urlpattern=NULL;
466 char *req_header = NULL;
467 int matchfound = 0;
468 int gotlock = 0;
469 dosblockipc_data *base = NULL;
470 apr_status_t rs;
471 dosblock_cfg *cfg = (dosblock_cfg *)ap_get_module_config(r->server->module_config, &mod_dosblock_module);
472
473 /* Here we try to find if the request matches a dos rule */
474 dosrulemap_arr = apr_table_elts(cfg->dosrulemap);
475
476 if (dosrulemap_arr) {
477 dosrulemap_elts = (const apr_table_entry_t *)dosrulemap_arr->elts;
478 int i;
479 for (i=0; i<dosrulemap_arr->nelts; ++i)
480 {
481 subrule = dosrulemap_elts[i].val;
482 char *subrule_type = (char *)apr_table_get(cfg->ruletype, subrule);
483 if (subrule_type == NULL)
484 continue;
485 /* Check if it is a url based subrule */
486 if (0 == apr_strnatcmp(subrule_type, RULEURL))
487 {
488 urlpattern = (char *)apr_table_get(cfg->urlrules, subrule);
489 if (urlpattern == NULL)
490 continue;
491 ap_regex_t *pattern = ap_pregcomp(r->pool, urlpattern, AP_REG_EXTENDED);
492 if (0 == ap_regexec(pattern, r->uri, 0, NULL, 0))
493 /* This is where the dos url subrule would match */
494 {
495 dosrulematch = dosrulemap_elts[i].key;
496 matchfound = 1;
497 break;
498 }
499 }
500 /* Check if it is a header based subrule */
501 else if (0 == apr_strnatcmp(subrule_type, RULEHEADER))
502 {
503 headerrulestruct *h = NULL;
504 h = apr_hash_get(cfg->headerrules, subrule, APR_HASH_KEY_STRING);
505 if (h == NULL)
506 continue;
507 req_header = (char *)apr_table_get(r->headers_in, h->headername);
508 if (req_header == NULL)
509 continue;
510 ap_regex_t *pattern = ap_pregcomp(r->pool, h->headerpattern, AP_REG_EXTENDED);
511 if (0 == ap_regexec(pattern, req_header, 0, NULL, 0))
512 /* This is where the dos header subrule would match */
513 {
514 dosrulematch = dosrulemap_elts[i].key;
515 matchfound = 1;
516 break;
517 }
518 }
519 }
520 }
521
522 if (matchfound && dosrulematch) {
523 int blocked = 0;
524 /* apr_table_set(r->headers_out, "DOSRULE_MATCHED", dosrulematch);*/
525 /* Check if we have the corresponding shared memory segment for
526 * the matching dos rule. If not we do not go further.
527 */
528 if (!apr_hash_get(cfg->dosrule_shm_map, dosrulematch, APR_HASH_KEY_STRING))
529 {
530 return DECLINED;
531 }
532 apr_int64_t index = apr_atoi64(apr_hash_get(cfg->dosrule_shm_map, dosrulematch, APR_HASH_KEY_STRING));
533
534 /* Take the mutex lock here. Be careful of not 'returning' before releasing the lock*/
535 rs = apr_global_mutex_lock(dosblockipc_mutex[index]);
536 if (APR_SUCCESS == rs) {
537 gotlock = 1;
538 }
539 else {
540 /* Some error, log and bail */
541 ap_log_error(APLOG_MARK, APLOG_ERR, rs, r->server, "Child %ld failed to acquire lock", (long int)getpid());
542 }
543 base = (dosblockipc_data *)apr_shm_baseaddr_get(dosblockipc_shm[index]);
544 /* check if the Dos rule is already blocked */
545 if (base->ds.isblocked)
546 {
547 /* The dos rule is blocked at this moment. Need to check the time stamp
548 * (time till blockage) and act accordingly
549 */
550 apr_time_t time_now = apr_time_now();
551 apr_time_t time_to_blockage = base->ds.t;
552 /* apr_table_set(r->headers_out, "Blocked till", apr_psprintf(r->pool, "%d",time_to_blockage)); */
553 if(cfg->verbosity)
554 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Dos rule %s is blocked", dosrulematch);
555 if (time_now < time_to_blockage)
556 {
557 /* Keep blocking */
558 blocked = 1;
559 if(cfg->verbosity)
560 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Keep dos rule %s blocked", dosrulematch);
561
562 }
563 else {
564 /* Time to unblock */
565 base->ds.isblocked = 0;
566 base->ds.t = 0;
567 /* apr_table_set(r->headers_out, "Time-to-unblock", "Unblock"); */
568 if(cfg->verbosity)
569 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Time to unblock dos rule %s", dosrulematch);
570
571 }
572 }
573 /* Here we age entries, try to account for the matched hit, calculate the
574 * rate and take some action if needed.
575 */
576 int i;
577 apr_int64_t sum_counter = 0;
578 int hit_accounted = 0;
579
580 /* Loop through the array of structs */
581 for (i=0; i<ARSIZE; i++) {
582 if (base->dh[i].t) {
583 if ((r->request_time - base->dh[i].t)/APR_USEC_PER_SEC > ARSIZE ) {
584 /* Ageing entries */
585 base->dh[i].t = 0;
586 base->dh[i].counter = 0;
587 if(cfg->verbosity)
588 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Ageing entry for dos rule %s", dosrulematch);
589 continue;
590 }
591 if (base->dh[i].t/APR_USEC_PER_SEC == r->request_time/APR_USEC_PER_SEC )
592 {
593 /* There is already an entry for this sec. Increment the corresponding
594 * counter
595 */
596 base->dh[i].counter++;
597 sum_counter+= base->dh[i].counter;
598 hit_accounted = 1;
599 /*apr_table_set(r->headers_out, "Entry-for-this-sec", "Exists"); */
600 if(cfg->verbosity)
601 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "We have an Entry-for-this-sec for dos rule %s", dosrulematch);
602 continue;
603 }
604 sum_counter+= base->dh[i].counter;
605 }
606 }
607
608 /* Add an element in our array of structs for this second if the hit was not accounted above */
609 if (! hit_accounted) {
610 if(cfg->verbosity)
611 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Hit is not accounted for dos rule %s", dosrulematch);
612 if ((ARSIZE - base->next) == 0)
613 base->next = 0;
614 base->dh[base->next].t = r->request_time;
615 base->dh[base->next].counter = 1;
616 sum_counter+= base->dh[base->next].counter;
617 base->next++;
618 /*apr_table_set(r->headers_out, "Entry-for-this-sec", "Does-not-Exist");*/
619 if(cfg->verbosity)
620 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "We do not have an entry for this second (dos rule %s) and sum of counter is %" APR_INT64_T_FMT, dosrulematch, sum_counter);
621 }
622
623 if ( ! blocked) {
624 float rate = (float)sum_counter/ARSIZE;
625 /* Get the configured rate threshold */
626 dosrulestruct *d = apr_hash_get(cfg->dosrules, dosrulematch, APR_HASH_KEY_STRING);
627 if(cfg->verbosity) {
628 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Non Blocked dos rule %s sum of counter is %" APR_INT64_T_FMT, dosrulematch, sum_counter);
629 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Non Blocked dos rule %s rate is %f", dosrulematch, rate);
630 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Non Blocked dos rule %s configured rate is %" APR_INT64_T_FMT, dosrulematch, apr_atoi64(d->threshold));
631 }
632 if (rate > apr_atoi64(d->threshold))
633 {
634 /* Block it */
635 base->ds.isblocked = 1;
636 base->ds.t = (apr_time_now() + (apr_atoi64(d->timetoblock)*APR_USEC_PER_SEC));
637 base->ds.rate_when_blocked = rate;
638 if(cfg->verbosity) {
639 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Non Blocked dos rule %s getting blocked after rate calulation", dosrulematch);
640 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Non Blocked dos rule %s getting blocked till %" APR_TIME_T_FMT, dosrulematch, base->ds.t);
641 }
642 blocked = 1;
643 }
644 }
645
646 /* Release the lock. We have to be careful of not 'returning' before
647 * releasing the lock
648 */
649 if (gotlock)
650 rs = apr_global_mutex_unlock(dosblockipc_mutex[index]);
651 /* Swallowing the result because what are we going to do with it at
652 * this stage
653 */
654 if (blocked)
655 return HTTP_FORBIDDEN;
656 }
657 else {
658 /* apr_table_set(r->headers_out, "DOSRULE_MATCHED", "None");*/
659 }
660
661 return DECLINED;
662 }
663
664 static void register_hooks(apr_pool_t *p) {
665 ap_hook_post_config(dosblock_post_config, NULL, NULL, APR_HOOK_MIDDLE);
666 ap_hook_child_init(dosblock_child_init, NULL, NULL, APR_HOOK_MIDDLE);
667 ap_hook_post_read_request(dosblock_main, NULL, NULL, APR_HOOK_FIRST);
668
669 }
670
671 module AP_MODULE_DECLARE_DATA mod_dosblock_module = {
672 STANDARD20_MODULE_STUFF,
673 NULL, /* create per-directory config structure */
674 NULL, /* merge per-directory config structures */
675 dosblock_config, /* create per-server config structure */
676 dosblock_config_server_merge, /* merge per-server config structures */
677 mod_dosblock_cmds, /* command table */
678 register_hooks
679 };