<?php
/*
*--------------------------------------------------------
* Модуль antiddos.php V3.0.4 Чт 14 Май 2009
* Copyright © Андрей Якушев, 2006.
http://avy.ru *--------------------------------------------------------
* Модуль предназначен для ограничения доступа к сайту или
* к страницам, где он включён.
* Принцип работы в том, что запоминается ip-адрес и время
* обращения с этого адреса. И если в течение заданного
* времени происходит обращение с того же адреса, то ему
* выдаётся ошибка 503.
* Если количество недопустимых обращений подряд превышает
* определённое число, ip-адрес закрывается через файл
* .htaccess
* Модуль необходимо подключать к скрипту самым первым.
* Этим обеспечивается быстрота его работы.
*--------------------------------------------------------
*/
/* Директория для временных файлов. Необходимо указать
отдельную директорию, т.к. большое количество файлов
в одной папке змедляет скорость обращения к ней. */
define ('AD_DIRNAME', $_SERVER['DOCUMENT_ROOT'].'/tmp_path');
/* Время задержки в секуднах, в течение которого нельзя
обращаться к сайту. */
define ('AD_DELAY', 2);
/* Количество запрещённых повторений, после которых ip-адрес
будет забанен. Нужно обратить внимание на то, что некоторые
программы чтения RSS-каналов считывают все ссылки, помещённые
в канале сразу. Поэтому, если на сайте есть такие каналы,
это число необходимо поставить больше, чем максимальное
количество элементов в канале. */
define ('AD_TRYING', 35);
/*
*---------------------------------------------------------------
* Список поисковых роботов.
* Очень не хорошо, если поисковый робот будет натыкаться
* на ошибки на сайте. Ему это может сильно не понравиться.
* Поэтому пишем список ip-адресов роботов; добавляем или
* удаляем, что нужно.
* IP-адреса:
* Alta Vista - см. Yahoo
* Aport - 194.67.18.
* Gigabot - 66.231.188. (66.231.188.0/24)
* Google - 209.85.128.0 - 209.85.255.255 (209.85.128.0/17),
* 72.14.192.0 - 72.14.255.255 (72.14.192.0/18),
* 66.249.64.0 - 66.249.95.255 (66.249.64.0/19),
* 64.68.80.0 - 64.68.87.255 (64.68.80.0/21),
* 66.102.0.0 - 66.102.15.255 (66.102.0.0/20),
* 64.233.160.0 - 64.233.175.255 (64.233.160.0/19),
* 216.239.32.0 - 216.239.63.255 (216.239.32.0/19)
* Mail.Ru - 195.239.211.0 (195.239.211.0/24),
* 94.100.181.128 - 94.100.181.255 (94.100.181.128/25)
* msnbot - 65.52.0.0 - 65.55.255.255 (65.52.0.0/14)
* Rambler - 81.19.64.0 - 81.19.66.255 (81.19.64.0/19)
* Yahoo - 74.6.0.0 - 74.6.255.255 (74.6.0.0/16),
* 69.147.64.0 - 69.147.127.255 (69.147.64.0/18)
* Yandex - 213.180.214.128 - 213.180.214.255 (213.180.192.0/19),
* 77.88.22.0 - 77.88.23.255 (77.88.0.0/18)
* 93.158.128.0 - 93.158.191.255 (93.158.128.0/18)
* LiveInternet - 88.212.202.0 - 88.212.202.63 (88.212.202.0/26)
* Ip-адреса и маски к ним кодируются в виде строки из 4 символов
* Это сделано из-за того, что невозможно в PHP использовать
* стандартными (простыми) процедурами 32-битное целое число
* без знака. А использование специальных библиотек усложнит
* работу и сделает скрипт зависимым от этих библиотек.
*---------------------------------------------------------------
*/
$ad_Robots_IP = array(
'Aport' => array(
sprintf('%c%c%c%c', 194, 67, 18, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
'Gigabot' => array(
sprintf('%c%c%c%c', 66, 231, 188, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
'Google1' => array(
sprintf('%c%c%c%c', 209, 85, 128, 0),
sprintf('%c%c%c%c', 255, 255, 128, 0)
),
'Google2' => array(
sprintf('%c%c%c%c', 72, 14, 192, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Google3' => array(
sprintf('%c%c%c%c', 66, 249, 64, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Google4' => array(
sprintf('%c%c%c%c', 64, 68, 80, 0),
sprintf('%c%c%c%c', 255, 255, 248, 0)
),
'Google5' => array(
sprintf('%c%c%c%c', 66, 102, 0, 0),
sprintf('%c%c%c%c', 255, 255, 240, 0)
),
'Google6' => array(
sprintf('%c%c%c%c', 64, 233, 160, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Google7' => array(
sprintf('%c%c%c%c', 216, 239, 32, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Mail.Ru1' => array(
sprintf('%c%c%c%c', 195, 239, 211, 0),
sprintf('%c%c%c%c', 255, 255, 255, 0)
),
'Mail.Ru2' => array(
sprintf('%c%c%c%c', 94, 100, 181, 128),
sprintf('%c%c%c%c', 255, 255, 255, 128)
),
'msnbot' => array(
sprintf('%c%c%c%c', 65, 52, 0, 0),
sprintf('%c%c%c%c', 255, 252, 0, 0)
),
'Rambler' => array(
sprintf('%c%c%c%c', 81, 19, 64, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Yahoo1' => array(
sprintf('%c%c%c%c', 74, 6, 0, 0),
sprintf('%c%c%c%c', 255, 255, 0, 0)
),
'Yahoo2' => array(
sprintf('%c%c%c%c', 69, 147, 64, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex1' => array(
sprintf('%c%c%c%c', 213, 180, 192, 0),
sprintf('%c%c%c%c', 255, 255, 224, 0)
),
'Yandex2' => array(
sprintf('%c%c%c%c', 77, 88, 0, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'Yandex3' => array(
sprintf('%c%c%c%c', 93, 158, 128, 0),
sprintf('%c%c%c%c', 255, 255, 192, 0)
),
'LiveInternet' => array(
sprintf('%c%c%c%c', 88, 212, 202, 0),
sprintf('%c%c%c%c', 255, 255, 255, 192)
),
);
/*
*----------------------------------------------------------
* Функция создаёт в указанной директории файл, начинающийся
* с буквы a (для отличия от других возможных файлов) и
* содержащий в имени ip-адрес клиента.
* Если количество обращений подряд с данного адреса
* превысило допустимый предел, адрес закрывается через файл
* .htaccess
* При желании может быть отослано письмо на специальный
* адрес, в котором будет передана информация о действиях
* с заблокированного адреса.
*----------------------------------------------------------
*/
function ad_WriteIP($counter)
{
global $Parabl_cnfg;
$counter++;
if ($counter > AD_TRYING)
{
//Баним ip-адрес
$f = fopen($_SERVER['DOCUMENT_ROOT'] . '/.htaccess', 'a');
fwrite($f, "\ndeny from " . $_SERVER['REMOTE_ADDR']);
fclose($f);
// Открыть комментарии, если нужно уведомлять по почте
/*
//Получаем хост (в некоторых логах он может быть вместо ip
$host = gethostbyaddr($_SERVER['REMOTE_ADDR']);
$mess = 'Заблокирован адрес ' . $_SERVER['REMOTE_ADDR'] .
' (' . $host . ")\n\n";
//Выполняем запрос к логам. Нужно указать путь и имя лог-файла
exec('cat /home/your_dir/logs/access_log | egrep \'(' .
str_replace('.', '\\.', $_SERVER['REMOTE_ADDR']) . ')|(' .
str_replace('.', '\\.', $host) . ')\' | sort -k 4 >' .
AD_DIRNAME . '/dump.txt');
$mess .= file_get_contents(AD_DIRNAME . '/dump.txt');
@ unlink(AD_DIRNAME . '/dump.txt');
$email = 'my_box@myhost.ru'; //Укажите свой e-mail
mail($email, 'IP-address was banned', $mess, "From: " . $email .
"\nReply-To: " . $email .
"\nContent-Type: text/plain; charset=Windows-1251" .
"\nContent-Transfer-Encoding: 8bit");
*/
}
else
{
//Записываем файл с ip-адресом и количеством обращений
$f = fopen(AD_DIRNAME . '/a' . $_SERVER['REMOTE_ADDR'] . '_' .
$counter, 'w');
fclose($f);
}
}
/*
*----------------------------------------------------
* Проверка на отношение ip-адреса к сетям поисковиков
*----------------------------------------------------
*/
$ad_IsRobot = false;
$ad_IP = explode('.', $_SERVER['REMOTE_ADDR']);
$ad_IPMatch = sprintf('%c%c%c%c', $ad_IP[0], $ad_IP[1], $ad_IP[2], $ad_IP[3]);
foreach ($ad_Robots_IP as $ad_match)
{
//Если на входящий адрес наложить маску операцией "и",
//то он должен будет совпасть с начальным адресом сети.
if (($ad_IPMatch & $ad_match[1]) == $ad_match[0])
{
$ad_IsRobot = true;
break;
}
}
/*
*---------------------------------------------------------
* Поисковые роботы не любят, когда к адресу страницы
* добавляется переменная сессии. Поэтому, если на сайте
* используются сессии, то их лучше включать, если агент -
* не робот.
* Если сессии не используются, то этот кусок можно убрать.
*---------------------------------------------------------
*/
/*
if (!$ad_IsRobot)
{
session_start();
}
*/
if (!$ad_IsRobot)
{
/*** Чтение каталога и удаление старых файлов ***/
$ad_dir = opendir(AD_DIRNAME) or
die('Отсутствует директория для временных файлов');
$ad_forbid = time() - AD_DELAY;
/* IP-адрес в имени файла, начинающегося на букву a,
а время обращения - время изменения файла */
$ad_before_trying = 0;
while (false !== ($ad_FName = readdir($ad_dir)))
{
if (ereg('^a[1-9]', $ad_FName))
{
if (@ filemtime(AD_DIRNAME . '/' . $ad_FName) < $ad_forbid){
@ unlink(AD_DIRNAME . '/' . $ad_FName);
}
elseif (ereg('^a' . str_replace('.', '\\.',
$_SERVER['REMOTE_ADDR']) . '_([0-9]+)$', $ad_FName, $ad_match))
{
//Если файл есть, то читаем, сколько раз к нму обращались
$ad_before_trying = intval($ad_match[1]);
@ unlink(AD_DIRNAME . '/' . $ad_FName);
}
}
}
closedir($ad_dir);
/*** Выводить или не выводить сообщение об ошибке ***/
if ($ad_before_trying > 0)
{
/* Если обращение было недавно, то выводим сообщение об ошибке */
header ('HTTP/1.0 503 Service Unavailable');
header ('Status: 503 Service Unavailable');
header ('Retry-After: ' . $ad_delay * 3);
?>
<!doctype html public "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Ошибка 503</title>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1251" />
</head>
<body>
<h1>Ошибка 503 (Service Unavailable)</h1>
<p>Сервер не может в данный момент выдать запрашиваемую Вами страницу.
Попробуйте вызвать эту страницу позже (клавиша F5).</p>
</body>
</html>
<?php
ad_WriteIP($ad_before_trying); // Перед выходом записываем ip
exit;
}else{
ad_WriteIP($ad_before_trying);
}
}
?>