=head1 NAME SPF - plugin to implement Sender Permitted From =head1 SYNOPSIS # in config/plugins sender_permitted_from Or if you wish to issue 5xx on SPF fail: sender_permitted_from spf_deny 1 =cut use Mail::SPF::Query; sub register { my ($self, $qp, @args) = @_; %{$self->{_args}} = @args; $self->register_hook("mail", "mail_handler"); $self->register_hook("rcpt", "rcpt_handler"); $self->register_hook("data_post", "data_handler"); } 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) = @_; my $query = $transaction->notes('spfquery'); my ($result, $comment) = $query->result(); $self->qp->connection->notes('spf_result', $result); $self->qp->connection->notes('spf_comment', $comment); if ($result eq "fail" and $self->{_args}{spf_deny}) { return (DENY, "SPF forgery ($comment)"); } return (DECLINED); } sub data_handler { my ($self, $transaction) = @_; my $spf = $self->qp->connection->notes('spf_result'); my $host = $self->qp->connection->remote_host; my $ip = $self->qp->connection->remote_ip; my $sender = $transaction->sender; my $details = ''; if ($spf eq 'fail') { $details = "fail (client $host[$ip] is not a designated mailer for domain of sender $sender)"; } elsif ($spf eq 'softfail') { $details = "error (temporary failure while resolving designated mailer status for domain of sender $sender)"; } elsif ($spf eq 'pass') { $details = "pass (client $host[$ip] is designated mailer for domain of sender $sender)"; } else { $details = "unknown (domain of sender $sender does not designate mailers)"; } $transaction->header->add('Received-SPF' => $details); return DECLINED; }