View source
<?php
define('AUTOBAN_BASE_URL', 'admin/config/people/autoban');
define('AUTOBAN_SINGLE_IP', 0);
define('AUTOBAN_RANGE_IP', 1);
define('AUTOBAN_MESSAGE_SEPARATOR', '|');
define('AUTOBAN_EXPORT_SEPARATOR', '||');
define('AUTOBAN_WHITELIST_SOURCE_VARIABLE', 0);
define('AUTOBAN_WHITELIST_SOURCE_FILE', 1);
function autoban_help($path, $arg) {
switch ($path) {
case 'admin/help#autoban':
$output = '<p>' . t("This module allows you to automatize IP ban by cron using module rules. You <a href='@admin_url'>create a rule</a> which finds IP in <a href='@dblog_url'>watchlog table entries</a> and module then inserts IP to <a href='@banned_url'>banned IP table</a>.", array(
'@admin_url' => url('admin/config/people/autoban'),
'@dblog_url' => url('admin/reports/dblog'),
'@banned_url' => url('admin/config/people/ip-blocking'),
)) . '</p>';
if (_autoban_ip_ranges_enable()) {
$output .= '<p>' . t("The module can also inserts IP to <a href='@banned_range_url'>IP range bans table</a>.", array(
'@banned_range_url' => url('admin/config/people/ip-ranges'),
)) . '</p>';
}
if (_autoban_blocked_ips_expire_enable()) {
$output .= '<p>' . t("The module can also expire inserted IPs using Blocked_ips_expire module.") . '</p>';
}
return $output;
}
}
function autoban_permission() {
return array(
'administer autoban' => array(
'title' => t('Administer the automatic ban'),
),
);
}
function autoban_menu() {
$items[AUTOBAN_BASE_URL] = array(
'title' => 'Autoban',
'description' => 'Configure automatic ban settings.',
'page callback' => 'autoban_page',
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/list'] = array(
'title' => 'Autoban rules',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[AUTOBAN_BASE_URL . '/add'] = array(
'title' => 'Add rule',
'type' => MENU_LOCAL_ACTION,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_form',
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/ban_all'] = array(
'title' => 'Ban all',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_ban_all_form',
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/clear'] = array(
'title' => 'Clear tables',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_clear_tables_form',
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/analyze'] = array(
'title' => 'Log analyze',
'type' => MENU_LOCAL_TASK,
'page callback' => 'autoban_analyze',
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/export'] = array(
'title' => 'Export',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_export_form',
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/import'] = array(
'title' => 'Import',
'type' => MENU_LOCAL_ACTION,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_import_form',
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/edit/%'] = array(
'title' => 'Edit autoban rule',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_form',
5,
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/delete/%'] = array(
'title' => 'Delete autoban rule',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_delete',
5,
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/test/%'] = array(
'title' => 'Test autoban rule',
'page callback' => 'autoban_test',
'page arguments' => array(
5,
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
$items[AUTOBAN_BASE_URL . '/ban/%/%'] = array(
'title' => 'IP ban',
'type' => MENU_CALLBACK,
'page callback' => 'autoban_ban_manual',
'page arguments' => array(
5,
6,
),
'access arguments' => array(
'administer autoban',
),
);
$items[AUTOBAN_BASE_URL . '/settings'] = array(
'title' => 'Settings',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(
'autoban_settings_form',
),
'access arguments' => array(
'administer autoban',
),
'file' => 'autoban.admin.inc',
);
return $items;
}
function autoban_cron() {
if (!variable_get('autoban_cron_enable', TRUE)) {
return;
}
$count = autoban_ban_all();
if ($count) {
watchdog('autoban', 'Banned IP by cron. Total count: @count. (cron IP @ip)', array(
'@ip' => ip_address(),
'@count' => $count,
));
}
}
function autoban_ban($rule, &$context = NULL) {
$count = 0;
if (!empty($rule)) {
$hostnames = autoban_get_hostnames($rule);
if (!empty($hostnames)) {
foreach ($hostnames as $hostname) {
$banned_ip = autoban_make_ip_style($hostname->hostname, $rule->ip_type);
if (isset($rule->rid)) {
$message_variables = array(
'@rule_url' => url(AUTOBAN_BASE_URL . '/edit/' . $rule->rid),
'@rule' => $rule->rid,
);
$message_rule = "by <a href='@rule_url'>rule @rule</a>";
}
else {
$message_variables = array();
$message_rule = '';
}
if (autoban_can_banned($banned_ip, $hostname->hostname)) {
$success = autoban_insert_banned_table($banned_ip, $rule->ip_type);
if ($success) {
$message_variables['@ip'] = $banned_ip;
watchdog('autoban', "Banned IP @ip {$message_rule}.", $message_variables);
$count++;
}
}
}
}
if (!empty($context)) {
$context['results'][] = t('Banned by rule @rid', array(
'@rid' => $rule->rid,
));
$context['message'] = t('Ban by rule @rid', array(
'@rid' => $rule->rid,
));
if (isset($_SESSION['autoban_ban_all_count'])) {
$_SESSION['autoban_ban_all_count'] += $count;
}
}
}
return $count;
}
function autoban_ban_all() {
$count = 0;
$rules = autoban_get_rules();
if (!empty($rules)) {
foreach ($rules as $rule) {
$count += autoban_ban($rule);
}
}
return $count;
}
function autoban_is_banned($ip, $ip_type) {
$ip_type = _autoban_ip_type_verification($ip_type);
$count = 0;
switch ($ip_type) {
case AUTOBAN_SINGLE_IP:
$count = db_select('blocked_ips', 't')
->condition('t.ip', $ip)
->countQuery()
->execute()
->fetchField();
break;
case AUTOBAN_RANGE_IP:
$ip = str_replace(' - ', '-', $ip);
$count = db_select('ip_ranges', 't')
->condition('t.ip', $ip)
->condition('t.type', 'blacklist')
->countQuery()
->execute()
->fetchField();
}
return $count > 0;
}
function autoban_insert_banned_table($ip, $ip_type) {
$ip_type = _autoban_ip_type_verification($ip_type);
switch ($ip_type) {
case AUTOBAN_SINGLE_IP:
if (!autoban_is_banned($ip, $ip_type)) {
$iid = db_insert('blocked_ips')
->fields(array(
'ip' => $ip,
))
->execute();
if (_autoban_blocked_ips_expire_enable()) {
$default_duration = variable_get('blocked_ips_expire_default_time', '+2 years');
$default_expiry_date = new DateTime($default_duration);
$expiry_date = $default_expiry_date
->getTimestamp();
db_merge('blocked_ips_expire')
->key(array(
'iid' => $iid,
))
->fields(array(
'expiry_date' => $expiry_date,
))
->execute();
}
return TRUE;
}
break;
case AUTOBAN_RANGE_IP:
if (!autoban_is_banned($ip, $ip_type)) {
$ip = str_replace(' - ', '-', $ip);
db_insert('ip_ranges')
->fields(array(
'ip' => $ip,
'type' => 'blacklist',
))
->execute();
return TRUE;
}
}
return FALSE;
}
function autoban_get_rules($rid = NULL, $header = array()) {
if (empty($header)) {
$stored_rules =& drupal_static(__FUNCTION__);
}
if (empty($stored_rules)) {
$query = db_select('autoban', 'list')
->fields('list');
if (!empty($header)) {
$query
->extend('TableSort')
->orderByHeader($header);
}
$stored_rules = $query
->execute()
->fetchAllAssoc('rid');
}
if ($rid !== NULL && empty($header)) {
if (!empty($stored_rules[$rid])) {
return $stored_rules[$rid];
}
else {
drupal_set_message(t('autoban_get_rules: Missing index @rid.', array(
'@rid' => $rid,
)), 'error');
return NULL;
}
}
return $stored_rules;
}
function _autoban_ip_type_verification($index) {
if ($index == AUTOBAN_RANGE_IP && !_autoban_ip_ranges_enable()) {
$index = AUTOBAN_SINGLE_IP;
}
return $index;
}
function _autoban_get_ip_type_names_list() {
$ip_type_list = array(
AUTOBAN_SINGLE_IP => t('Single'),
);
if (_autoban_ip_ranges_enable()) {
$ip_type_list[AUTOBAN_RANGE_IP] = t('Range');
}
return $ip_type_list;
}
function _autoban_get_ip_type_name($index) {
$index = _autoban_ip_type_verification($index);
$ip_type_list = _autoban_get_ip_type_names_list();
if (isset($ip_type_list[$index])) {
return $ip_type_list[$index];
}
return '';
}
function _autoban_get_user_type_name($index = NULL) {
$user_type_list = array(
t('Any'),
t('Anonymous'),
t('Authenticated'),
);
if ($index !== NULL) {
if (isset($user_type_list[$index])) {
return $user_type_list[$index];
}
else {
drupal_set_message(t('_autoban_get_user_type_name: Missing user type index @index.', array(
'@index' => $index,
)), 'error');
return '';
}
}
return $user_type_list;
}
function autoban_get_hostnames($options, $header = array()) {
$use_wildcards = variable_get('autoban_use_wildcards', FALSE);
$query = db_select('watchdog', 'log')
->fields('log', array(
'hostname',
))
->groupBy('log.hostname');
if (!empty($options->type)) {
$query
->condition('log.type', $options->type);
}
if (!empty($options->message)) {
$regexp_mode = variable_get('autoban_query_mode', 0) == 1;
$message_items = explode(AUTOBAN_MESSAGE_SEPARATOR, $options->message);
if (count($message_items) > 1) {
$or = db_or();
foreach ($message_items as $message_item) {
if ($regexp_mode) {
$or
->where("log.message REGEXP(:message)", array(
':message' => trim($message_item),
));
}
else {
$db_like = db_like(trim($message_item));
$db_like = str_replace("\\%", "%", $db_like);
if (!$use_wildcards) {
$db_like = '%' . $db_like . '%';
}
$or
->condition('log.message', $db_like, 'LIKE');
}
}
$query
->condition($or);
}
else {
if ($regexp_mode) {
$query
->where("log.message REGEXP(:message)", array(
':message' => $options->message,
));
}
else {
$db_like = db_like(trim($options->message));
$db_like = str_replace("\\%", "%", $db_like);
if (!$use_wildcards) {
$db_like = '%' . $db_like . '%';
}
$query
->condition('log.message', $db_like, 'LIKE');
}
}
}
if (!empty($options->referer)) {
$referer_items = explode(AUTOBAN_MESSAGE_SEPARATOR, $options->referer);
if (count($referer_items) > 1) {
$or = db_or();
foreach ($referer_items as $referer_item) {
$db_like = db_like(trim($referer_item));
$db_like = str_replace("\\%", "%", $db_like);
if (!$use_wildcards) {
$db_like = '%' . $db_like . '%';
}
$or
->condition('log.referer', $db_like, 'LIKE');
}
$query
->condition($or);
}
else {
$db_like = db_like(trim($options->referer));
$db_like = str_replace("\\%", "%", $db_like);
if (!$use_wildcards) {
$db_like = '%' . $db_like . '%';
}
$query
->condition('log.referer', $db_like, 'LIKE');
}
}
if ($options->user_type > 0) {
if ($options->user_type == 1) {
$query
->condition('log.uid', 0);
}
else {
$query
->condition('log.uid', 0, '>');
}
}
$query
->addExpression('COUNT(*)', 'hcount');
$query
->havingCondition('hcount', $options->threshold, '>=');
if (count($header)) {
$query
->extend('TableSort')
->orderByHeader($header);
}
return $query
->execute()
->fetchAll();
}
function autoban_make_ip_style($hostname, $ip_type) {
$ip_type = _autoban_ip_type_verification($ip_type);
switch ($ip_type) {
case AUTOBAN_SINGLE_IP:
return $hostname;
case AUTOBAN_RANGE_IP:
return _autoban_create_range($hostname);
default:
return NULL;
}
}
function _autoban_create_range($hostname) {
$parts = explode('.', $hostname);
if (count($parts) == 4) {
$parts[3] = '0';
$ip_start = implode('.', $parts);
$parts[3] = '255';
$ip_end = implode('.', $parts);
return "{$ip_start} - {$ip_end}";
}
return NULL;
}
function _autoban_is_own_ip($hostname) {
$my_ip = ip_address();
$parts = explode(' - ', $hostname);
if (count($parts) > 1) {
$my_ip = ip2long($my_ip);
return $my_ip <= ip2long($parts[1]) && $my_ip >= ip2long($parts[0]);
}
else {
return $hostname === $my_ip;
}
}
function _autoban_get_thresholds() {
$autoban_thresholds = explode(',', variable_get('autoban_thresholds', '1,2,3,5,10,20,50,100,200,500,1000'));
return $autoban_thresholds ? array_map('trim', $autoban_thresholds) : array(
1,
2,
3,
5,
10,
20,
50,
100,
200,
500,
1000,
);
}
function autoban_get_log_whitelist() {
$whitelist = explode(',', variable_get('autoban_dblog_type_exclude', 'autoban,cron,php,system,user'));
return $whitelist ? array_map('trim', $whitelist) : array(
'autoban',
'cron',
'php',
'system',
'user',
);
}
function autoban_var_json_export($var, $prefix = '') {
if (is_array($var) && $var) {
$use_array = $var == array_values($var);
$output = $use_array ? "[" : "{";
foreach ($var as $key => $value) {
if ($use_array) {
$values[] = autoban_var_json_export($value, ' ');
}
else {
$values[] = autoban_var_json_export((string) $key, ' ') . ' : ' . autoban_var_json_export($value, ' ');
}
}
if (strlen($content = implode(', ', $values)) > 70 && ($use_array || count($values) > 1)) {
$output .= "\n " . implode(",\n ", $values) . "\n";
}
elseif (strpos($content, "\n") !== FALSE) {
$output .= " " . $content . "\n";
}
else {
$output .= " " . $content . ' ';
}
$output .= $use_array ? ']' : '}';
}
else {
$output = drupal_json_encode($var);
}
if ($prefix) {
$output = str_replace("\n", "\n{$prefix}", $output);
}
return $output;
}
function autoban_get_ip_location($hostname, $get_real_hostname = FALSE) {
if (empty($hostname)) {
return '';
}
$access_key = variable_get('autoban_ipstack_apikey', '');
if (empty($access_key)) {
return '';
}
$options = array(
'access_key' => $access_key,
'output' => 'json',
'fields' => 'main',
);
if ($get_real_hostname) {
$options['hostname'] = 1;
}
$ipstack_url = url("http://api.ipstack.com/{$hostname}", array(
'query' => $options,
'absolute' => TRUE,
'external' => TRUE,
));
$req = drupal_http_request($ipstack_url);
if ($req->code != 200) {
return '';
}
$data = json_decode($req->data);
$res = array(
$data->country_name,
$data->region_name,
$data->city,
);
foreach ($res as $key => $item) {
if (empty($item)) {
unset($res[$key]);
}
}
$location = implode(', ', $res);
if ($get_real_hostname) {
return array(
'hostname' => $data->hostname,
'location' => $location,
);
}
else {
return $location;
}
}
function autoban_ban_manual($ip, $ip_type) {
if (autoban_can_banned($ip)) {
$ip = autoban_make_ip_style($ip, $ip_type);
$res = autoban_insert_banned_table($ip, $ip_type);
$msg = $res ? t('added') : t('has already');
drupal_set_message(t('@ip has been banned (@msg).', array(
'@ip' => $ip,
'@msg' => $msg,
)));
if ($res) {
watchdog('autoban', 'Banned @ip manually', array(
'@ip' => $ip,
));
}
}
else {
drupal_set_message(t('@ip was NOT banned.', array(
'@ip' => $ip,
)));
}
if (isset($_GET['destination'])) {
drupal_goto($_GET['destination']);
}
}
function _autoban_cidr_match($ip, $network, $cidr) {
$cidr = intval($cidr);
if ($cidr > 32) {
return FALSE;
}
return (ip2long($ip) & ~((1 << 32 - $cidr) - 1)) == ip2long(trim($network));
}
function _autoban_range_match($ip, $ip_begin, $ip_end) {
$ipBegin = ip2long(trim($ip_begin));
$ipEnd = ip2long(trim($ip_end));
if (empty($ipBegin) || empty($ipEnd)) {
return FALSE;
}
$ipLong = ip2long($ip);
return $ipLong >= $ipBegin && $ipLong <= $ipEnd;
}
function _autoban_get_whitelist() {
$whitelist =& drupal_static(__FUNCTION__);
if (!isset($whitelist)) {
$source = variable_get('autoban_whitelist_source', AUTOBAN_WHITELIST_SOURCE_VARIABLE);
switch ($source) {
case AUTOBAN_WHITELIST_SOURCE_VARIABLE:
$whitelist = variable_get('autoban_whitelist', '');
break;
case AUTOBAN_WHITELIST_SOURCE_FILE:
$whitelist = file_get_contents(drupal_realpath(variable_get('autoban_whitelist_file', '')));
break;
}
}
return $whitelist;
}
function autoban_whitelist_ip($ip) {
$autoban_whitelist_domains = variable_get('autoban_whitelist_domains', '');
if (!empty($autoban_whitelist_domains)) {
$real_host = autoban_gethostbyaddr($ip);
if ($real_host) {
$autoban_whitelist_domains_arr = explode(",", $autoban_whitelist_domains);
foreach ($autoban_whitelist_domains_arr as $whitelist_domain) {
$whitelist_domain = trim($whitelist_domain);
$real_host_arr = explode($whitelist_domain, $real_host);
if (count($real_host_arr) == 2 && empty($real_host_arr[1])) {
return TRUE;
}
}
}
}
$autoban_whitelist = _autoban_get_whitelist();
if (!empty($autoban_whitelist)) {
$autoban_whitelist_arr = explode("\n", $autoban_whitelist);
foreach ($autoban_whitelist_arr as $whitelist_ip) {
$whitelist_ip = trim($whitelist_ip);
if (drupal_substr($whitelist_ip, 0, 1) == '#') {
continue;
}
$whitelist_ip_arr = explode('#', $whitelist_ip);
if (count($whitelist_ip_arr) > 1) {
$whitelist_ip = trim($whitelist_ip_arr[0]);
}
$in_list = $whitelist_ip == $ip;
if (!$in_list) {
$whitelist_ip_arr = explode('/', $whitelist_ip);
if (count($whitelist_ip_arr) > 1) {
$in_list = _autoban_cidr_match($ip, $whitelist_ip_arr[0], (int) $whitelist_ip_arr[1]);
}
}
if (!$in_list) {
$whitelist_ip_arr = explode('-', $whitelist_ip);
if (count($whitelist_ip_arr) > 1) {
$in_list = _autoban_range_match($ip, $whitelist_ip_arr[0], (int) $whitelist_ip_arr[1]);
}
}
if (!$in_list) {
$whitelist_ip_arr = explode('...', $whitelist_ip);
if (count($whitelist_ip_arr) > 1) {
$in_list = _autoban_range_match($ip, $whitelist_ip_arr[0], (int) $whitelist_ip_arr[1]);
}
}
if ($in_list) {
return TRUE;
}
}
}
return FALSE;
}
function autoban_can_banned($ip, $hostname = NULL) {
if (empty($hostname)) {
$hostname = $ip;
}
$is_own_ip = _autoban_is_own_ip($ip);
if ($is_own_ip) {
return FALSE;
}
$ip_in_whitelist = autoban_whitelist_ip($hostname);
if ($ip_in_whitelist) {
return FALSE;
}
return TRUE;
}
function autoban_page_build(&$page) {
if (variable_get('autoban_force_enable', FALSE)) {
$count = autoban_ban_all();
if ($count) {
watchdog('autoban', 'Banned IP by force mode. Total count: @count.', array(
'@count' => $count,
));
}
}
}
function _autoban_short_referer($referer, $limit_width = 150) {
$item_referer = filter_xss($referer);
if (drupal_strlen($item_referer) > $limit_width) {
$item_referer = drupal_substr($item_referer, 0, $limit_width) . '...';
}
return $item_referer;
}
function _autoban_digexec($ip) {
$string = '';
exec("dig +short +retry=1 +timeout=1 +tries=1 -x {$ip} 2>&1", $output, $retval);
if ($retval != 0) {
return FALSE;
}
else {
$x = 0;
while ($x < sizeof($output)) {
$string .= $output[$x];
$x++;
}
}
if (empty($string)) {
$string = $ip;
}
else {
$string = substr($string, 0, -1);
}
return $string;
}
function autoban_gethostbyaddr($ip) {
$real_host = FALSE;
$stored_host =& drupal_static(__FUNCTION__);
if (isset($stored_host[$ip])) {
$real_host = $stored_host[$ip];
}
else {
$gethostbyaddr_function = variable_get('autoban_gethostbyaddr_function', 'gethostbyaddr');
$real_host_get = $gethostbyaddr_function($ip);
if ($real_host_get && $real_host_get != $ip) {
$stored_host[$ip] = $real_host_get;
$real_host = $real_host_get;
}
}
return $real_host;
}
function _autoban_ip_ranges_enable() {
return module_exists('ip_ranges');
}
function _autoban_blocked_ips_expire_enable() {
return module_exists('blocked_ips_expire');
}