Merge pull request #187 from msimerson/fcrdns
fcrdns: add tests and improved localhost detection
This commit is contained in:
commit
07fad0ffd0
@ -8,7 +8,8 @@ Forward Confirmed RDNS - http://en.wikipedia.org/wiki/FCrDNS
|
|||||||
|
|
||||||
Determine if the SMTP sender has matching forward and reverse DNS.
|
Determine if the SMTP sender has matching forward and reverse DNS.
|
||||||
|
|
||||||
Sets the connection note fcrdns.
|
Adds the 'iprev' section to the Authentication-Results header when
|
||||||
|
there's sufficient DNS (at least rDNS) to be meaningful.
|
||||||
|
|
||||||
=head1 WHY IT WORKS
|
=head1 WHY IT WORKS
|
||||||
|
|
||||||
@ -141,22 +142,20 @@ sub register {
|
|||||||
$self->{_args}{reject} = 0;
|
$self->{_args}{reject} = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->register_hook('connect', 'connect_handler');
|
$self->register_hook('connect', 'fcrdns_tests');
|
||||||
}
|
}
|
||||||
|
|
||||||
sub connect_handler {
|
sub fcrdns_tests {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
return DECLINED if $self->is_immune();
|
return DECLINED if $self->is_immune();
|
||||||
|
|
||||||
# run a couple cheap tests before the more expensive DNS tests
|
# run cheap tests before the more expensive DNS tests
|
||||||
foreach my $test (qw/ is_valid_localhost is_not_fqdn /) {
|
foreach my $test (
|
||||||
|
qw/ is_valid_localhost is_fqdn has_reverse_dns has_forward_dns / ) {
|
||||||
$self->$test() or return DECLINED;
|
$self->$test() or return DECLINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->has_reverse_dns() or return DECLINED;
|
|
||||||
$self->has_forward_dns() or return DECLINED;
|
|
||||||
|
|
||||||
$self->log(LOGINFO, "pass");
|
$self->log(LOGINFO, "pass");
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
}
|
}
|
||||||
@ -168,35 +167,35 @@ sub is_valid_localhost {
|
|||||||
$self->adjust_karma(1);
|
$self->adjust_karma(1);
|
||||||
$self->log(LOGDEBUG, "pass, is localhost");
|
$self->log(LOGDEBUG, "pass, is localhost");
|
||||||
return 1;
|
return 1;
|
||||||
};
|
}
|
||||||
|
|
||||||
my $rh = $self->qp->connection->remote_host;
|
my $rh = $self->qp->connection->remote_host;
|
||||||
if ($rh && lc $self->qp->connection->remote_host eq 'localhost') {
|
return 0 if ! $rh;
|
||||||
$self->log(LOGDEBUG, "pass, remote_host is localhost");
|
return 0 if lc $self->qp->connection->remote_host ne 'localhost';
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
$self->adjust_karma(-1);
|
$self->adjust_karma(-1);
|
||||||
$self->log(LOGINFO, "fail, not localhost");
|
$self->log(LOGINFO, "fail, not localhost");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub is_not_fqdn {
|
sub is_fqdn {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $host = $self->qp->connection->remote_host or return 1;
|
my $host = $self->qp->connection->remote_host or return 0;
|
||||||
return 1 if $host eq 'Unknown'; # QP assigns this to a "no DNS result"
|
return 0 if $host eq 'Unknown'; # QP assigns this to a "no DNS result"
|
||||||
|
|
||||||
# Since QP looked it up, perform some quick validation
|
# Since QP looked it up, perform some quick validation
|
||||||
if ($host !~ /\./) { # has no dots
|
if ($host !~ /\./) { # has no dots
|
||||||
$self->adjust_karma(-1);
|
$self->adjust_karma(-1);
|
||||||
$self->log(LOGINFO, "fail, not FQDN");
|
$self->log(LOGINFO, "fail, not FQDN");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
if ($host =~ /[^a-zA-Z0-9\-\.]/) {
|
if ($host =~ /[^a-zA-Z0-9\-\.]/) {
|
||||||
$self->adjust_karma(-1);
|
$self->adjust_karma(-1);
|
||||||
$self->log(LOGINFO, "fail, invalid FQDN chars");
|
$self->log(LOGINFO, "fail, invalid FQDN chars");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$self->log(LOGDEBUG, "pass, is FQDN");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ hosts_allow
|
|||||||
ident/geoip
|
ident/geoip
|
||||||
ident/p0f /tmp/.p0f_socket version 3
|
ident/p0f /tmp/.p0f_socket version 3
|
||||||
connection_time
|
connection_time
|
||||||
|
fcrdns
|
||||||
|
|
||||||
# enable to accept MAIL FROM:/RCPT TO: addresses without surrounding <>
|
# enable to accept MAIL FROM:/RCPT TO: addresses without surrounding <>
|
||||||
dont_require_anglebrackets
|
dont_require_anglebrackets
|
||||||
|
60
t/plugin_tests/fcrdns
Normal file
60
t/plugin_tests/fcrdns
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!perl -w
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
#use POSIX qw(strftime);
|
||||||
|
|
||||||
|
#use Qpsmtpd::Address;
|
||||||
|
use Qpsmtpd::Constants;
|
||||||
|
|
||||||
|
my $test_email = 'matt@tnpi.net';
|
||||||
|
|
||||||
|
sub register_tests {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->register_test('_is_valid_localhost');
|
||||||
|
$self->register_test('_is_fqdn');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub _is_valid_localhost {
|
||||||
|
my $self = shift;
|
||||||
|
my @passes = qw/ 127.0.0.1 127.1.1.1 127.255.255.255 /;
|
||||||
|
my @fails = qw/ 128.0.0.1 126.1.1.1 /;
|
||||||
|
|
||||||
|
foreach my $pass (@passes) {
|
||||||
|
$self->qp->connection->remote_ip($pass);
|
||||||
|
cmp_ok( $self->is_valid_localhost(), '==', 1, "$pass, true");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $fail (@fails) {
|
||||||
|
$self->qp->connection->remote_ip($fail);
|
||||||
|
cmp_ok( $self->is_valid_localhost(), '==', 0, "$fail, false");
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->qp->connection->remote_host('localhost');
|
||||||
|
cmp_ok( $self->is_valid_localhost(), '==', 0, "localhost, non-loopback IP, false");
|
||||||
|
|
||||||
|
$self->qp->connection->remote_ip('127.0.0.1');
|
||||||
|
$self->qp->connection->remote_host('localhost');
|
||||||
|
cmp_ok( $self->is_valid_localhost(), '==', 1, "localhost, loop IP, true");
|
||||||
|
|
||||||
|
$self->qp->connection->remote_ip('::1');
|
||||||
|
$self->qp->connection->remote_host('localhost');
|
||||||
|
cmp_ok( $self->is_valid_localhost(), '==', 1, "localhost, IPv6 loop, true");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _is_fqdn {
|
||||||
|
my $self = shift;
|
||||||
|
my @passes = qw/ foo.com example.com /;
|
||||||
|
my @fails = qw/ com net edu boogers /;
|
||||||
|
|
||||||
|
foreach my $pass (@passes) {
|
||||||
|
$self->qp->connection->remote_host($pass);
|
||||||
|
cmp_ok( $self->is_fqdn(), '==', 1, "$pass, true");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $fail (@fails) {
|
||||||
|
$self->qp->connection->remote_host($fail);
|
||||||
|
cmp_ok( $self->is_fqdn(), '==', 0, "$fail, false");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user