2003-06-27 14:25:52 +02:00
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
SPF - plugin to implement Sender Permitted From
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
|
|
|
# in config/plugins
|
|
|
|
sender_permitted_from
|
|
|
|
|
2003-06-27 19:27:35 +02:00
|
|
|
Or if you wish to issue 5xx on SPF fail:
|
|
|
|
|
|
|
|
sender_permitted_from spf_deny 1
|
|
|
|
|
2003-06-28 01:00:52 +02:00
|
|
|
See also http://spf.pobox.com/
|
|
|
|
|
2003-06-27 14:25:52 +02:00
|
|
|
=cut
|
|
|
|
|
|
|
|
use Mail::SPF::Query;
|
|
|
|
|
|
|
|
sub register {
|
2003-06-27 19:27:35 +02:00
|
|
|
my ($self, $qp, @args) = @_;
|
|
|
|
%{$self->{_args}} = @args;
|
2003-06-27 14:25:52 +02:00
|
|
|
$self->register_hook("mail", "mail_handler");
|
|
|
|
$self->register_hook("rcpt", "rcpt_handler");
|
2003-06-27 19:27:35 +02:00
|
|
|
$self->register_hook("data_post", "data_handler");
|
2003-06-27 14:25:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sub mail_handler {
|
|
|
|
my ($self, $transaction, $sender) = @_;
|
|
|
|
|
|
|
|
return (DECLINED) unless ($sender->format ne "<>"
|
|
|
|
and $sender->host && $sender->user);
|
|
|
|
|
|
|
|
my $host = lc $sender->host;
|
|
|
|
my $from = $sender->user . '@' . $host;
|
|
|
|
|
|
|
|
my $ip = $self->qp->connection->remote_ip;
|
|
|
|
my $query = Mail::SPF::Query->new(ip => $ip, sender => $from)
|
|
|
|
|| die "Couldn't construct Mail::SPF::Query object";
|
|
|
|
$transaction->notes('spfquery', $query);
|
|
|
|
|
|
|
|
return (DECLINED);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rcpt_handler {
|
|
|
|
my ($self, $transaction, $rcpt) = @_;
|
2003-06-28 01:00:52 +02:00
|
|
|
|
|
|
|
# special addresses don't get SPF-tested.
|
|
|
|
return DECLINED if $rcpt and $rcpt->user and $rcpt->user =~ /^(?:postmaster|abuse|mailer-daemon|root)$/i;
|
|
|
|
|
2003-06-27 14:25:52 +02:00
|
|
|
my $query = $transaction->notes('spfquery');
|
|
|
|
my ($result, $comment) = $query->result();
|
|
|
|
|
2003-06-28 01:00:52 +02:00
|
|
|
$self->qp->connection->notes('spf_result', $result);
|
2003-06-27 19:27:35 +02:00
|
|
|
$self->qp->connection->notes('spf_comment', $comment);
|
2003-06-28 01:00:52 +02:00
|
|
|
$self->qp->connection->notes('spf_header', "$result ($comment)");
|
2003-06-27 19:27:35 +02:00
|
|
|
|
|
|
|
if ($result eq "fail" and $self->{_args}{spf_deny}) {
|
2003-06-28 01:00:52 +02:00
|
|
|
my $ip = $self->qp->connection->remote_ip;
|
|
|
|
my $sender = $transaction->sender;
|
|
|
|
|
|
|
|
my $why = "http://spf.pobox.com/why?sender=" . _uri_escape($sender) . "&ip=$ip";
|
|
|
|
return (DENY, "SPF forgery ($comment; see $why)");
|
2003-06-27 14:25:52 +02:00
|
|
|
}
|
|
|
|
|
2003-06-28 01:00:52 +02:00
|
|
|
return DECLINED;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _uri_escape {
|
|
|
|
my $str = shift;
|
|
|
|
$str =~ s/([^A-Za-z0-9\-_.!~*\'()])/sprintf "%%%X", ord($1)/eg;
|
|
|
|
return $str;
|
2003-06-27 14:25:52 +02:00
|
|
|
}
|
|
|
|
|
2003-06-27 19:27:35 +02:00
|
|
|
sub data_handler {
|
|
|
|
my ($self, $transaction) = @_;
|
2003-07-08 05:12:04 +02:00
|
|
|
|
|
|
|
my $header = $self->qp->connection->notes('spf_header') || 'unknown';
|
|
|
|
|
|
|
|
$transaction->header->add('Received-SPF' => $header, 0);
|
2003-06-27 19:27:35 +02:00
|
|
|
|
|
|
|
return DECLINED;
|
|
|
|
}
|
|
|
|
|