Merge pull request #196 from msimerson/dmarc
dmarc: add error handling and tests
This commit is contained in:
commit
178c5f6884
@ -72,6 +72,7 @@ https://github.com/smtpd/qpsmtpd/wiki/DMARC-FAQ
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use English qw/-no_match_vars/;
|
||||
use Qpsmtpd::Constants;
|
||||
|
||||
sub register {
|
||||
@ -90,16 +91,16 @@ sub register {
|
||||
}
|
||||
else {
|
||||
$self->{_dmarc} = Mail::DMARC::PurePerl->new();
|
||||
$self->register_hook('data_post_headers', 'data_post_handler');
|
||||
$self->register_hook('data_post_headers', 'check_dmarc');
|
||||
};
|
||||
}
|
||||
|
||||
sub data_post_handler {
|
||||
sub check_dmarc {
|
||||
my ($self, $transaction) = @_;
|
||||
|
||||
if ( $self->qp->connection->relay_client() ) {
|
||||
$self->log(LOGINFO, "skip, relay client" );
|
||||
return DECLINED; # disable reporting to ourself
|
||||
return DECLINED; # don't report to ourself
|
||||
};
|
||||
|
||||
my $dmarc = $self->{_dmarc};
|
||||
@ -117,19 +118,15 @@ sub data_post_handler {
|
||||
my @recipients = $transaction->recipients;
|
||||
eval { $dmarc->envelope_to( lc $recipients[0]->host ); }; # optional
|
||||
eval { $dmarc->envelope_from( $transaction->sender->host ); }; # may be <>
|
||||
$dmarc->spf( $transaction->notes('dmarc_spf') );
|
||||
eval { $dmarc->spf( $transaction->notes('dmarc_spf') ); };
|
||||
my $dkim = $self->connection->notes('dkim_verifier');
|
||||
if ( $dkim ) {
|
||||
eval { $dmarc->dkim( $dkim ); }
|
||||
};
|
||||
if ( $dkim ) { eval { $dmarc->dkim( $dkim ); }; };
|
||||
$dmarc->source_ip( $self->qp->connection->remote_ip );
|
||||
eval { $dmarc->validate(); };
|
||||
if ( $@ ) {
|
||||
if ( $EVAL_ERROR ) {
|
||||
$self->log(LOGERROR, $@ );
|
||||
return DECLINED if $self->is_immune;
|
||||
$self->log(LOGINFO, "TODO: handle this validation failure");
|
||||
return DECLINED;
|
||||
return $self->get_reject( $@, $@ );
|
||||
return $self->get_reject( $@ );
|
||||
};
|
||||
|
||||
#$self->log(LOGINFO, "result: " . Dumper( $dmarc ) );
|
||||
|
@ -1,64 +1,46 @@
|
||||
#!perl -w
|
||||
|
||||
use strict;
|
||||
use English qw/-no_match_vars/;
|
||||
use POSIX qw(strftime);
|
||||
|
||||
use Qpsmtpd::Address;
|
||||
use Qpsmtpd::Constants;
|
||||
|
||||
my $remote_ip = '66.128.51.165';
|
||||
my $test_email = 'matt@tnpi.net';
|
||||
|
||||
sub register_tests {
|
||||
my $self = shift;
|
||||
|
||||
# TODO: test against newer DMARC plugin that uses Mail::DMARC
|
||||
eval 'use Mail::DMARC';
|
||||
if ($EVAL_ERROR) {
|
||||
warn 'unable to load Mail::DMARC';
|
||||
return;
|
||||
}
|
||||
|
||||
sub setup_test_headers {
|
||||
$self->register_test('_check_dmarc');
|
||||
}
|
||||
|
||||
sub _check_dmarc {
|
||||
my $self = shift;
|
||||
|
||||
my $transaction = $self->qp->transaction;
|
||||
my $address = Qpsmtpd::Address->new( "<$test_email>" );
|
||||
my $header = Mail::Header->new(Modify => 0, MailFrom => "COERCE");
|
||||
my $now = strftime "%a %b %e %H:%M:%S %Y", localtime time;
|
||||
$self->qp->connection->remote_ip($remote_ip);
|
||||
my $t = $self->qp->transaction;
|
||||
$t->header(Mail::Header->new(Modify => 0, MailFrom => "COERCE"));
|
||||
$t->sender(Qpsmtpd::Address->new( "<$test_email>" ));
|
||||
$t->header->add('Date', strftime "%a %b %e %H:%M:%S %Y", localtime time);
|
||||
$t->body_write( "test message body " );
|
||||
|
||||
$transaction->sender($address);
|
||||
$transaction->header($header);
|
||||
$transaction->header->add('From', "<$test_email>");
|
||||
$transaction->header->add('Date', $now );
|
||||
$transaction->body_write( "test message body " );
|
||||
# no From header, reject as invalid message
|
||||
my ($rc, $msg) = $self->check_dmarc($t);
|
||||
cmp_ok($rc, '==', DENY, "no From header, $msg");
|
||||
|
||||
$self->qp->connection->relay_client(0);
|
||||
}
|
||||
|
||||
sub test_fetch_dmarc_record {
|
||||
my $self = shift;
|
||||
|
||||
foreach ( qw/ tnpi.net nictool.com / ) {
|
||||
my @matches = $self->fetch_dmarc_record($_);
|
||||
cmp_ok( scalar @matches, '==', 1, 'fetch_dmarc_record');
|
||||
}
|
||||
foreach ( qw/ example.com / ) {
|
||||
my @matches = $self->fetch_dmarc_record($_);
|
||||
cmp_ok( scalar @matches, '==', 0, 'fetch_dmarc_record');
|
||||
}
|
||||
}
|
||||
|
||||
sub test_get_organizational_domain {
|
||||
my $self = shift;
|
||||
|
||||
$self->setup_test_headers();
|
||||
my $transaction = $self->qp->transaction;
|
||||
|
||||
cmp_ok( $self->get_organizational_domain('test.www.tnpi.net'), 'eq', 'tnpi.net' );
|
||||
cmp_ok( $self->get_organizational_domain('www.example.co.uk'), 'eq', 'example.co.uk' );
|
||||
cmp_ok( $self->get_organizational_domain('plus.google.com'), 'eq', 'google.com' );
|
||||
}
|
||||
|
||||
sub test_discover_policy {
|
||||
my $self = shift;
|
||||
|
||||
$self->setup_test_headers();
|
||||
|
||||
ok( $self->discover_policy( 'tnpi.net' ), 'discover_policy' );
|
||||
|
||||
$t->header->add('From', "<$test_email>");
|
||||
($rc, $msg) = $self->check_dmarc($t);
|
||||
cmp_ok($rc, '==', DENY, "$msg");
|
||||
cmp_ok($msg, 'eq', 'failed DMARC policy', 'check_dmarc, no SPF');
|
||||
|
||||
#warn $self->qp->connection->notes('authentication_results');
|
||||
}
|
Loading…
Reference in New Issue
Block a user