Отслеживание спама Теперь когда
На некоторых сайтах ведутся локальные черные списки узлов, известных как распространители спама. Такая тактика была взята на вооружение, когда спам только начал появляться, и некоторые провайдеры отказывались принимать меры даже против самых закоренелых клиентов-спамеров. В ответ на это в основных агентах передачи почты появились механизмы, отвергающие соединения от узлов и доменов, входящих в список антисоциальных.
Можно использовать такой черный список, чтобы разобраться, проходило ли сообщение через узлы, известные своим спамерством. Ясно, что в черном списке нет сервера, передавшего нам почту (иначе с ним просто не было бы установлено соединение), но любой из остальных почтовых серверов, указанный в заголовках Received:, вполне может там быть.
Не существует способа написать одну программу, проверяющую все возможные черные списки агентов передачи почты, поскольку разные агенты хранят эту информацию в различных форматах. Большая часть узлов в Интернете в настоящее время применяет в качестве агента передачи почты sendmail, так что в нашем примере будет применяться его формат черного списка. В новых версиях sendmail черный спи- сок хранится в базе данных при помощи библиотек Berkeley DB 2.X, доступных на http://www.sleepycat.com.
Поль Маркес (Paul Marquess) написал модуль BerkeleyDB, специально предназначенный для работы с библиотеками Berkeley 2.x/3.x. Это может сбить с толку, поскольку в документации по DB_File, еще одному известному модулю Маркеса, входящему в состав дистрибутива Perl, также рекомендуется применять библиотеки 2.х/З.х. DB_File использует библиотеки Berkeley DB 2.х/З.х в «режиме совместимости» (в частности, библиотека собирается с ключом --enable-compat185, так что доступен API версии 1.x API). Модуль BerkeleyDB позволяет программисту на Perl применять расширенные возможности из API версии 2.х/З.х.
Агент передачи sendmail использует формат BerkeleyDB 2.х/З.х, так что нужно включить модуль BerkeleyDB. Вот пример, который выводит содержимое локального черного списка:
Sblacklist = "/etc/mail/blacklist.db"; use BerkeleyDB;
Ясвяжем хэш %blist с черным списком, используя Berkeley DB
# для получения значений
tie %blist, 'BerkeleyDB::Hash', -Filename => Sblacklist or die
"Невозможно открыть файл $filenane: $! SBerkeleyDB::Error\n" ;
# обходим в цикле каждый ключ и значение из этого файла, и
выводя только записи REJECT while(($key,$value) = each %blist){
в списке также могут быть записи "OK", "RELAY" и др. next
if ($value ne "REJECT");
print "$key\n": }
Принимая за основу этот код, можно написать подпрограмму, проверяющую, находится ли данный узел или домен (содержащий этот узел) в черном списке. Если нужно узнать об узле mallserver.spam-mer.com, следует обойти в цикле все записи из черного списка (в котором могут находиться mailserver.spammer.com, spammer.com или даже просто spammer), чтобы проверить, содержатся ли в имени узла какие-либо записи из него.
Существует много способов написать на Perl программу, сравнивающую список значений с какими-либо данными. Но для того чтобы программа была эффективной и интересной, мы будем использовать две продвинутые технологии. Они созданы для уменьшения числа компиляций регулярных выражений, которые применяются в ходе выполнения программы. Каждый раз, когда программа использует «новое» регулярное выражение, Perl должен компилировать его заново. Например, в этом отрывке кода Perl вынужден обрабатывать новое значение на каждой итерации:
вообразите себе внешний цикл, в котором этот код вызывается
множество разforeach Smatch (qw(alewife davis porter harvard central кепааН park))
{
Sstation =" /Smatch/ and print "found our station stop'": }
Этот процесс требует больших вычислительных затрат, так что если бы можно было сократить количество вычислений, программа стала бы намного эффективней. Время, потраченное на компиляцию регулярных выражений, становится значительным в программах, где в цикле рассматривается список различных регулярных выражений.
Вот пример первой технологии, созданной для решения указанной проблемы:
use BerkeleyDB;
Sblacklist = "/etc/mail/blacklist.db";
&loadblist;
и принимаем имя узла в качестве аргумента командной строки и
сообщаем, если оно есть в черном списке
if (defined &checkblist($ARGV[0])){
print "*** $found найден в черном списке \п"; }
И
загружаем черный список в массив анонимных подпрограмм sub loadblist{tie %blist, 'BerkeleyDB::Hash', -Filename => Sblacklist or die
"Невозможно открыть Sfilename:
$! $BerkeleyDB::ErrorXn" ;
while(my($key,$value) = each %blist){
# в черном списке могут быть "OK", "RELAY" и пр. next if ($value ne "REJECT");
push(@blisttests, eval 'sub {$_[0] =~ \Q$key/o and $key}'); } }
sub checkblist{
my($line) = shift:
foreach Ssubref (@blisttests){
return Sfound if (Sfound = &$subref($line)); }
return undef: } В этом примере используются анонимные подпрограммы - технология, продемонстрированная в книге Джозефа Хола (Joseph Hall) «Effective Perl Programming» (Эффективное программирование на Perl) (Addison Wesley). Для каждой записи из черного списка создается анонимная подпрограмма. Каждая подпрограмма сверяет переданные ей данные с одним из элементов черного списка. Если они совпадают, такая запись возвращается. Ссылки на эти подпрограммы хранятся в списке. Вот строка, в которой создается подпрограмма и ссылка на нее добавляется к списку:
push(@blisttests, eval 'sub <$_[0] =" /\0$key/o and $key}');
Так что, если в черном списке есть запись spammer, ссылка на код, добавленная в массив, будет указывать на подпрограмму, по сути эквивалентную следующей:
sub {
$_[0] =" /\Qspammer/o and "spammer"; }
в начале регулярного выражения присутствует для того, чтобы точки (как в .сот) или другие зарезервированные знаки пунктуации не считались бы метасимволами регулярных выражений.
Позже в программе будет обойден в цикле список ссылок на код и выполнена каждая маленькая подпрограмма для переданных данных. Если результатом каких-либо из этих вычислений окажется значение «истина», мы вернем код возврата подпрограммы:
return $found if (Sfound = &$subref($line));
Компиляция регулярного выражения, которая нас так беспокоит, происходит всего один раз - при создании ссылки на подпрограмму. Можно вызывать каждую подпрограмму столько раз, сколько надо, не теряя время на компиляцию регулярного выражения.
Существует и другой, чуть менее продвинутый подход, который можно применять, если у вас Perl версии 5.005 или выше. В Perl 5.005 была введена новая синтаксическая конструкция, названная «прекомпи-лируемым регулярным выражением», которая делает подобную задачу несколько проще. Переписать код, используя эту новую конструкцию, можно было бы примерно так:
sub loadblist{
tie %blist, 'BerkeleyDB::Hash', -Filename => $blacklist or die
"Невозможно открыть файл $Шегше:
$BerkeleyDB: :Error\n" ;
while(my($key,$vaiue) = eac^- %blist){
# в черном списке могут бьть запис/ "OK". "RELAY" и пр. next
(Svalue ne "PEJECT") push('SDlisttests, [qr/\Q$i<ey/. $кеу]): }
sub checkblisu
my($iine) = shift;
foreach my Stest (§blisttests){
my($re,$kr;v) = a{$test}
return $key i* ($line =" /$re/): }
return undef; }
На этот раз ссылка была перенесена на анонимный массив в@blist test. Первый элемент этого массива - скомпилированное регулярное выражение, созданное с применением нового синтаксиса qr/Y. Это позволяет сохранить регулярное выражение после его компиляции. Такая форма обработки значительно увеличивает скорость выполнения программы при дальнейшем поиске соответствия. Второй элемент анонимного массива - сама запись из черного списка, которая будет возвращена при найденном соответствии скомпилированному регулярному выражению.