Когда конфигурируют 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;
Вобщем все, но лучше бы протестировать открывающий релей скрипт, потому, что мог допустить какую-то ошибку.