Viewing file: mod_evasive20.c (17.81 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
mod_evasive for Apache 2
Copyright (c) by Jonathan A. Zdziarski
LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_request.h"
module AP_MODULE_DECLARE_DATA evasive20_module;
/* BEGIN DoS Evasive Maneuvers Definitions */
#define MAILER "/bin/mail %s"
#define LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
#define DEFAULT_HASH_TBL_SIZE 3097ul // Default hash table size
#define DEFAULT_PAGE_COUNT 2 // Default maximum page hit count per interval
#define DEFAULT_SITE_COUNT 50 // Default maximum site hit count per interval
#define DEFAULT_PAGE_INTERVAL 1 // Default 1 Second page interval
#define DEFAULT_SITE_INTERVAL 1 // Default 1 Second site interval
#define DEFAULT_BLOCKING_PERIOD 10 // Default for Detected IPs; blocked for 10 seconds
#define DEFAULT_LOG_DIR "/tmp" // Default temp directory
/* END DoS Evasive Maneuvers Definitions */
/* BEGIN NTT (Named Timestamp Tree) Headers */
enum { ntt_num_primes = 28 };
/* ntt root tree */
struct ntt {
long size;
long items;
struct ntt_node **tbl;
};
/* ntt node (entry in the ntt root tree) */
struct ntt_node {
char *key;
time_t timestamp;
long count;
struct ntt_node *next;
};
/* ntt cursor */
struct ntt_c {
long iter_index;
struct ntt_node *iter_next;
};
struct ntt *ntt_create(long size);
int ntt_destroy(struct ntt *ntt);
struct ntt_node *ntt_find(struct ntt *ntt, const char *key);
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
int ntt_delete(struct ntt *ntt, const char *key);
long ntt_hashcode(struct ntt *ntt, const char *key);
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
/* END NTT (Named Timestamp Tree) Headers */
/* BEGIN DoS Evasive Maneuvers Globals */
struct ntt *hit_list; // Our dynamic hash table
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
static int page_count = DEFAULT_PAGE_COUNT;
static int page_interval = DEFAULT_PAGE_INTERVAL;
static int site_count = DEFAULT_SITE_COUNT;
static int site_interval = DEFAULT_SITE_INTERVAL;
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
static char *email_notify = NULL;
static char *log_dir = NULL;
static char *system_command = NULL;
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip);
int is_whitelisted(const char *ip);
/* END DoS Evasive Maneuvers Globals */
static void * create_hit_list(apr_pool_t *p, server_rec *s)
{
/* Create a new hit list for this listener */
hit_list = ntt_create(hash_table_size);
}
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip)
{
char entry[128];
snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
ntt_insert(hit_list, entry, time(NULL));
return NULL;
}
static int access_checker(request_rec *r)
{
int ret = OK;
/* BEGIN DoS Evasive Maneuvers Code */
if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
char hash_key[2048];
struct ntt_node *n;
time_t t = time(NULL);
/* Check whitelist */
if (is_whitelisted(r->connection->remote_ip))
return OK;
/* First see if the IP itself is on "hold" */
n = ntt_find(hit_list, r->connection->remote_ip);
if (n != NULL && t-n->timestamptimestamp = time(NULL);
/* Not on hold, check hit stats */
} else {
/* Has URI been hit too much? */
snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
n = ntt_find(hit_list, hash_key);
if (n != NULL) {
/* If URI is being hit too much, add to "hold" list and 403 */
if (t-n->timestampcount>=page_count) {
ret = HTTP_FORBIDDEN;
ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
} else {
/* Reset our hit count list as necessary */
if (t-n->timestamp>=page_interval) {
n->count=0;
}
}
n->timestamp = t;
n->count++;
} else {
ntt_insert(hit_list, hash_key, t);
}
/* Has site been hit too much? */
snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
n = ntt_find(hit_list, hash_key);
if (n != NULL) {
/* If site is being hit too much, add to "hold" list and 403 */
if (t-n->timestampcount>=site_count) {
ret = HTTP_FORBIDDEN;
ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
} else {
/* Reset our hit count list as necessary */
if (t-n->timestamp>=site_interval) {
n->count=0;
}
}
n->timestamp = t;
n->count++;
} else {
ntt_insert(hit_list, hash_key, t);
}
}
/* Perform email notification and system functions */
if (ret == HTTP_FORBIDDEN) {
char filename[1024];
struct stat s;
FILE *file;
snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
if (stat(filename, &s)) {
file = fopen(filename, "w");
if (file != NULL) {
fprintf(file, "%ld\n", getpid());
fclose(file);
LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
if (email_notify != NULL) {
snprintf(filename, sizeof(filename), MAILER, email_notify);
file = popen(filename, "w");
if (file != NULL) {
fprintf(file, "To: %s\n", email_notify);
fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->remote_ip);
fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
pclose(file);
}
}
if (system_command != NULL) {
snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
system(filename);
}
} else {
LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
}
} /* if (temp file does not exist) */
} /* if (ret == HTTP_FORBIDDEN) */
} /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
/* END DoS Evasive Maneuvers Code */
if (ret == HTTP_FORBIDDEN
&& (ap_satisfies(r) != SATISFY_ANY bool(false)
|