pop before smtp для popa3d(другие демоны pop-аутентификации можно дописать, о чем здесь ниже)

Когда конфигурируют sendmail, в файле /etc/mail/access содержатся правила для доставки почты, смысл которых заключается в том, что можно отправлять почту только с определенных айпиадресов, прописанных в файле /etc/mail/access. Что не очень удобно, если обладатель почтового ящика довольно часто перемещается по миру и из-за этих правил не может отправлять почту через свой адрес. Для преодоления данной трудности существуют два патча: SMTP AUTH и pop before smtp. Ни одну из них поставить не удалось, и пришлось поступить следующим образом: идем на сайт pop before smtp for Postfix, скачиваем тамошний модуль, распаковываем его и находим в распакованном файл popa3d-0.4.patch(там и для других демонов батчи есть, см. README). Согласно написанному правим сишные исходники демона popa3d, компилируем демон, получаем бинарник и перезаписываем его в /usr/sbin/popa3d. В результате в логах при pop3-аутентификации добавляется ip-адрес снявшего почту. Итого имеем имя и адрес снимающего почту. Дальше нужно написать некую программу, которая умеет смотреть, что добавляется в лог-файл, извлекать оттуда имя и ip-адрес и запысывать ip в /etc/mail/access: #!/usr/bin/perl -wT use strict; use File::Tail; use Fcntl ':flock'; use Date::Parse; my $l = '/var/log/messages'; #путь до логфайла my $file="/etc/mail/access"; #путь до файла с relay'ями на ip my (@q, $fi, $ip); my $relay = 60;#время открытого релея после popa3d-аутентификации для ip, снявшего почту &tail(); while (1){ m&^(.*\d+:\d+:\d+).*\[(.*?)\]$& if $_=$fi->read; #get ip and name my $time = str2time($1) + $relay or next; $ip=$2; #таймер next if $time < time; open A, ">>$file" or die "can't open: $!"; flock A, 2; print "\tВремя для $ip пошло(всего $relay секунд), relay открыт\n"; print A "$2\t\tRELAY\n"; close A; &makemap(); #закрываем и делаем ребилд для /etc/mail/access.db push @q, $time; while(1){ do{ my @file; open B, "<$file" or die "can't open: $!"; flock B, 1; do{ push @file, $_ unless m%^$ip%} while (<B>); #не нужны ip, у которых кончилось время close B; print "\tВремя для $ip закончилось, relay закрыт\n"; open C, ">$file" or die "can't open: $!"; flock C, 1; print C join "" => grep{!m&^$ip(\t)\1+RELAY$&} @file; close C; &proverka(); &makemap(); #закрываем и делаем ребилд для /etc/mail/access.db last } if $q[0] < time; sleep 1 } $#q=-1; } sub makemap{qx[makemap hash /etc/mail/access</etc/mail/access]} sub tail{ return $fi = File::Tail->new( name => $l, maxinterval => 1, adjustafter => 1000000000, interval => 4, tail => 0) } sub proverka{ open D, "<$file" or die "can't open: $!"; flock D, 1; my @debug=<D>; close D; open E, ">$file" or die "can't open: $!"; flock E, 1; print E join "" => grep{!m/Authentication passed/} @debug; close E; } Вобщем берется модуль File::Tail со спана и натравливается на логфайл. Соответственно по нему читаются маны и т.д. В чем прелесть, можно написать shell-script(взял из init-redhat-alex из pop before smtp для почтовика Postfix) и сделать приведенный скрипт демоном, который по добавлению в логфайл открывает на 30 секунд RELAY для того, кто перед этим снял почту, используя стандартные методы аутентификацииPOP3. можно релей открывать на 15 секунд... но, вобщем, вроде-бы защиту от спамеров оно гарантирует. Ниже методы проверки адреса на relay. Ну а на сервере, через который происходит отправка почты, выглядит это примерно так(результат отправки почты при помощи написанных на perl pop и smtp клиентов, см. ниже): [root@tv pop-before-smtp-1.28]# /root/pop-before-smtp-1.28/contrib/init-redhat-alex start Запуск pop-before-smtp: [ OK ] [root@tv pop-before-smtp-1.28]# Время для 194.218.214.122 пошло(всего 60 секунд), relay открыт Время для 194.218.214.122 закончилось, relay закрыт. Можно сделать так, чтобы эти сообщения писались в то-же /var/log/message. Программа очень нехитрая, написана на несложном языке, соответственно модификации для остальных видов демонов приветствуются.

Соответственно можно подправить sendmail.cf на предмет подсказки пользователю, что он должен сделать, чтобы отправить почту: # check client name: first: did it resolve? R$* $: < $&{client_resolve} > R<TEMP> $#error $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr} R<FORGED> $#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name} R<FAIL> $#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed, please, get your mail for check relay" $&{client_name} R$* $: <?> $&{client_name} Теперь пытаемся отправить почту из Нью-Йорка например через www.online.ru и получаем ответ(исходник smtp.pl см. ниже): [root@www devel]# ./smtp.pl SMTP RCPT command failed: 5.7.1 <user@last.my.server.ru>... Relaying denied. IP name lookup failed, please, get your mail for check relay[194.218.214.122] at ./smtp.pl line 18 [root@www devel]#

Логин телнетом на 25-й порт sendmail'а для открытого релея(при закрытом ругнется и напишет relaing denyed и разорвет соединение): [root@tv /root]# telnet tv 25 Trying 212.142.230.135... Connected to tv.server.ru (212.142.230.135). Escape character is '^]'. 220 tv.server.ru ESMTP Sendmail 8.11.2/8.11.2; Sun, 24 Mar 2002 02:10:00 +0300 mail from:<test@tv.server.ru> 250 2.1.0 <test@tv.server.ru>... Sender ok rcpt to:<user@last.my.server.ru> 250 2.1.5 <user@last.my.server.ru>... Recipient ok data 354 Enter mail, end with "." on a line by itself dfg . 250 2.0.0 g2NNANt15274 Message accepted for delivery Pop-3 клиент на perl: #!/usr/bin/perl use Net::POP3; $p=Net::POP3->new("tv.server.ru") or die "cant open connection to server: $!\n"; $p->login("test","rusalka") or die "Cant authentificate: $!\n"; $m=$p->list or die "cant get list of undeleted mesg: $!\n"; foreach $list(keys %$m){ $msg=$p -> get($list); print "@$msg\n"; } И smtp клиент: #!/usr/bin/perl $to = 'user@last.my.server.ru'; $from = 'test@tv.server.ru'; use MIME::Lite; $msg = MIME::Lite->new( To =>$to, From =>$from, Subject =>'Helloooooo, nurse!', Type =>'multipart/mixed' ); $msg->attach(Type =>'text', Data => qq{test} ); MIME::Lite->send('smtp', "tv.server.ru", Timeout=>60); $msg->send; Вобщем все, но лучше бы протестировать открывающий релей скрипт, потому, что мог допустить какую-то ошибку.