Добрый день!
Сделал небольшую реализацию greylist в Exim'е. Данные хранятся в MySQL.Таблица в базе mail:
====================
CREATE TABLE Greylist (
id bigint(20) NOT NULL auto_increment,
relay_ip int unsigned NOT NULL,
sender char(32) default NULL,
recipient char(32) default NULL,
block_expires int NOT NULL,
record_expires int NOT NULL,
create_time int UNSIGNED NOT NULL,
pass_count int UNSIGNED NOT NULL default '0',
block_count int UNSIGNED NOT NULL default '0',
PRIMARY KEY (id),
UNIQUE KEY (relay_ip,sender,recipient)
);greylist.pl - скрипт вызываемый exim'ом
=======================================
#!/usr/bin/perl# 30 минут блокировка для новой записи.
$block_expires=60*30;# Время жизни неподтвержденной записи 24 часа.
$record_expires_init=60*60*24;# Время жизни подтвержденной записи 60 дней с последнего письма.
$record_expires_incl=60*60*24*60;$relay_ip=$ARGV[0];
$sender=$ARGV[1];
$recipient=$ARGV[2];# В качестве ключа я использовал тройку:
# (inet_ntoa(relay_ip),md5('sender'),md5('recipient') = 68 байт.
# Использовал md5 по соображению безопасности и ограничении длины
# на ключ. А совпадение 64 байт для разных пар адресов - чрезвычайно
# ничтожно.@p=split(/\t/,`/usr/local/bin/mysql -s mail -e \"select id,(unix_timestamp()-block_expires) from Greylist where relay_ip=inet_aton('$relay_ip') and sender=md5('$sender') and recipient=md5('$recipient') limit 1\"`);
if ($p[0] > 0) {
if ($p[1] > 0) {system("/usr/local/bin/mysql mail -e \"update Greylist set pass_count=pass_count+1,record_expires=unix_timestamp()+$record_expires_incl where id='$p[0]';\"");
exit 1;
}
else {system("/usr/local/bin/mysql mail -e \"update Greylist set block_count=block_count+1 where id='$p[0]';\"");
}
}else {
system("/usr/local/bin/mysql mail -e \"insert into Greylist (relay_ip,sender,recipient,block_expires,record_expires,create_time,block_count) values (inet_aton('$relay_ip'),md5('$sender'),md5('$recipient'),unix_timestamp()+$block_expires,unix_timestamp()+$record_expires_init,unix_timestamp(),1);\"");
}
exit 0;для чистки старых записей по крону раз в N минут запускаем:
===========================================================
/usr/local/bin/mysql mail -e "delete from Greylist where unix_timestamp()>record_expires and pass_count=0;"exim.conf
=========
acl_check_rcpt:
...skipped...defer domains = +local_domains
message = Currently can not deliver mail. Try again later.
condition = ${run{/usr/local/exim/bin/greylist.pl $sender_host_address $sender_address $local_part@$domain}{yes}{no}}...skipped...
При правильно написанных acl exim'а, greylist работает только на почту входящую из вне.
Есть одно НО: письмо, в котором указаны несколько адресатов моего домена, попадет каждому из них, как минимут, только через $block_expires*(кол-во адресатов). Здесь по обстоятельствам, если последнее не устраивает, то можно убрать из базы и скриптов адрес получателя.
Приму конструктивную критику :-)
Пока что из критических замечаний только одно: зачем это? :) :)
>Пока что из критических замечаний только одно: зачем это? :) :)Greylist - метод борьбы со спамом. При первом обращении неизвестного сервера, его письмо отвергается с кодом нефатальной ошибки 4xx, если отправитель нормальный MTA, то по RFC он должен повторят попытки отправки письма в течении некоторого времени. После заданного времени мой почтовик разрешает данную комбинацию IP_адрес_сервера_отправителя:Емайл_оправителя:Емайл_получателя и письма ходят без задержек.
Если же отправитель является программа рассылки спама, то врядли будет попытка повтора, тем более, что база у спамеров как правило на десятки тысяч разных адресов.В комбинации с SpamAssassin'ом и белыми и черными списками можно добиться неплохих результатов в борьбе со спамом.
Вот еще переписал скрипт на Си, теперь будет быстрее работать...greylist.c
================================================#include <stdio.h>
#include "mysql.h"/* Greylist parametrs */
#define BLOCK_EXPIRES 60*60*1
#define RECORD_EXPIRES_INITIALY 60*60*24
#define RECORD_EXPIRES_ELECTED 60*60*24*60/* MySQL parameters */
#define MYSQL_HOST NULL
#define MYSQL_DATABASE "mail"
#define MYSQL_USER "exim"
#define MYSQL_PASSWORD "passwd"
#define MYSQL_PORT 3306
#define MYSQL_UNIXSOCKET "/tmp/mysql.sock"
#define MYSQL_CLIENTFLAG 0void usage() {
printf ("Usage: greylist [relay_ip] [sender] [recipient]\n");
exit (0);
}int main(int argc, char ** argv) {
MYSQL mysql;
MYSQL_ROW row;
MYSQL_RES * result;unsigned long long id;
signed int delta_time;
char query[1024];
if (argc!=4) usage();mysql_init(&mysql);
if (!mysql_real_connect(&mysql,MYSQL_HOST,MYSQL_USER,MYSQL_PASSWORD,MYSQL_DATABASE,MYSQL_PORT,MYSQL_UNIXSOCKET,MYSQL_CLIENTFLAG)) {
fprintf(stderr, "Connection to MySQL failed: %s\n", mysql_error(&mysql));
exit(1);
}sprintf (query,"select id,unix_timestamp()-block_expires from Greylist where relay_ip=inet_aton('%s') and sender=md5('%s') and recipient=md5('%s') limit 1",argv[1],argv[2],argv[3]);
if (mysql_query(&mysql,query)) {
fprintf(stderr, "SQL: \"%s\" failed: %s\n",query, mysql_error(&mysql));
exit (1);
}result = mysql_store_result(&mysql);
if (mysql_num_rows(result)) {
row = mysql_fetch_row(result);
mysql_free_result(result);id=strtoull(row[0],0,10);
delta_time=strtol(row[1],0,10);if (delta_time>0) {
sprintf (query,"update Greylist set pass_count=pass_count+1,record_expires=unix_timestamp()+%lu where id='%llu'",RECORD_EXPIRES_ELECTED,id);
if (mysql_query (&mysql,query)) fprintf(stderr, "SQL: \"%s\" failed: %s\n",query, mysql_error(&mysql));
mysql_close(&mysql);
exit (1);
}
else {
sprintf (query,"update Greylist set block_count=block_count+1 where id='%llu'",id);
if (mysql_query (&mysql,query)) {
fprintf(stderr, "SQL: \"%s\" failed: %s\n",query, mysql_error(&mysql));
exit (1);
}
}
}
else {
sprintf (query,"insert into Greylist (relay_ip,sender,recipient,block_expires,record_expires,create_time,block_count) values (inet_aton('%s'),md5('%s'),md5('%s'),unix_timestamp()+%lu,unix_timestamp()+%lu,unix_timestamp(),1)",argv[1],argv[2],argv[3],BLOCK_EXPIRES,RECORD_EXPIRES_INITIALY);
if (mysql_query(&mysql,query)) {
fprintf(stderr, "SQL: \"%s\" failed: %s\n",query, mysql_error(&mysql));
exit (1);
}
}
mysql_close(&mysql);
exit(0);
}
>Если же отправитель является программа рассылки спама, то врядли будет попытка повтора,
>тем более, что база у спамеров как правило на десятки тысяч
>разных адресов.Спамеры наоборот более настойчивы, чем легальные почтовики. Вот например часть статистики отвергнутых за один из дней в феврале - число отлупов для данного хоста:
93 [211.43.219.88]
85 [209.124.86.69]
64 [12.223.230.255]
55 [64.168.26.236]
53 [68.234.144.70]
52 [66.189.37.38]
50 [69.165.39.222]
49 [24.90.189.218]
48 [68.250.71.189]
46 [200.214.177.160]
45 [81.86.92.204]
44 [68.200.80.26]
>Спамеры наоборот более настойчивы, чем легальные почтовики.Это скорее открытые релеи, а с ними rbl хорошо справляется. Рассылки, которые идут с временных адресов прекрасно блокируются.
>>Пока что из критических замечаний только одно: зачем это? :) :)
>
>Greylist - метод борьбы со спамом. При первом обращении неизвестного сервера, его
>письмо отвергается с кодом нефатальной ошибки 4xx, если отправитель нормальный MTA,
>то по RFC он должен повторят попытки отправки письма в течении
>некоторого времени. После заданного времени мой почтовик разрешает данную комбинацию IP_адрес_сервера_отправителя:Емайл_оправителя:Емайл_получателя
>и письма ходят без задержек.
>Если же отправитель является программа рассылки спама, то врядли будет попытка повтора,
>тем более, что база у спамеров как правило на десятки тысяч
>разных адресов.
>
>В комбинации с SpamAssassin'ом и белыми и черными списками можно добиться неплохих
>результатов в борьбе со спамом.
Что помешает спамером добавить повторное обращение к серверу? На поток такую защиту не поставить, а для себя ничего так. Удачное решение.
>Приму конструктивную критику :-)
Ради чего делать внешний скрипт, когда запросы к БД и всю логику работы можно реализовать средствами самого exim?
>>Приму конструктивную критику :-)
>Ради чего делать внешний скрипт, когда запросы к БД и всю логику
>работы можно реализовать средствами самого exim?Ваша правда :-).
wheel reinventing. look for GREYLISTING in
http://www.openbsd.org/cgi-bin/man.cgi?query=spamd