resolvable_fromhost: log message updates
This commit is contained in:
parent
2804afeb2d
commit
caceda6d06
@ -15,7 +15,7 @@ my $has_ipv6 = Qpsmtpd::TcpServer::has_ipv6();
|
|||||||
|
|
||||||
sub register {
|
sub register {
|
||||||
my ( $self, $qp ) = @_;
|
my ( $self, $qp ) = @_;
|
||||||
|
|
||||||
foreach my $i ( $self->qp->config("invalid_resolvable_fromhost") ) {
|
foreach my $i ( $self->qp->config("invalid_resolvable_fromhost") ) {
|
||||||
$i =~ s/^\s*//;
|
$i =~ s/^\s*//;
|
||||||
$i =~ s/\s*$//;
|
$i =~ s/\s*$//;
|
||||||
@ -35,7 +35,7 @@ sub register {
|
|||||||
|
|
||||||
sub hook_mail_start {
|
sub hook_mail_start {
|
||||||
my ( $self, $transaction, $sender ) = @_;
|
my ( $self, $transaction, $sender ) = @_;
|
||||||
|
|
||||||
return DECLINED
|
return DECLINED
|
||||||
if ($self->connection->notes('whitelisthost'));
|
if ($self->connection->notes('whitelisthost'));
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ sub hook_mail_start {
|
|||||||
|
|
||||||
sub hook_mail_done {
|
sub hook_mail_done {
|
||||||
my ( $self, $transaction, $sender ) = @_;
|
my ( $self, $transaction, $sender ) = @_;
|
||||||
|
|
||||||
return DECLINED
|
return DECLINED
|
||||||
if ( $self->connection->notes('whitelisthost') );
|
if ( $self->connection->notes('whitelisthost') );
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ sub check_dns {
|
|||||||
|
|
||||||
my $qp = $self->qp;
|
my $qp = $self->qp;
|
||||||
$qp->input_sock->pause_read;
|
$qp->input_sock->pause_read;
|
||||||
|
|
||||||
my $a_records = [];
|
my $a_records = [];
|
||||||
my $num_queries = 1; # queries in progress
|
my $num_queries = 1; # queries in progress
|
||||||
my $mx_found = 0;
|
my $mx_found = 0;
|
||||||
@ -159,7 +159,7 @@ sub finish_up {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unless ($num_queries) {
|
unless ($num_queries) {
|
||||||
# all queries returned no valid response
|
# all queries returned no valid response
|
||||||
$qp->transaction->notes('resolvable_fromhost', 0);
|
$qp->transaction->notes('resolvable_fromhost', 0);
|
||||||
|
@ -51,11 +51,11 @@ Default: temp (temporary, aka soft, aka 4xx).
|
|||||||
|
|
||||||
=head1 EXAMPLE LOG ENTRIES
|
=head1 EXAMPLE LOG ENTRIES
|
||||||
|
|
||||||
80072 (mail) resolvable_fromhost: googlegroups.com has valid MX at gmr-smtp-in.l.google.com
|
80072 (mail) resolvable_fromhost: pass, googlegroups.com has MX at gmr-smtp-in.l.google.com
|
||||||
80108 (mail) resolvable_fromhost: zerobarriers.net has valid MX at zerobarriers.net
|
80108 (mail) resolvable_fromhost: pass, zerobarriers.net has MX at zerobarriers.net
|
||||||
80148 (mail) resolvable_fromhost: uhin.com has valid MX at filter.itsafemail.com
|
80148 (mail) resolvable_fromhost: pass, uhin.com has MX at filter.itsafemail.com
|
||||||
86627 (mail) resolvable_fromhost: no MX records for palmalar.com
|
86627 (mail) resolvable_fromhost: palmalar.com has no MX
|
||||||
86627 (mail) resolvable_fromhost: fail: palmalar.com (SERVFAIL)
|
86627 (mail) resolvable_fromhost: fail, palmalar.com (SERVFAIL)
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
@ -65,7 +65,6 @@ Default: temp (temporary, aka soft, aka 4xx).
|
|||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -95,32 +94,36 @@ sub register {
|
|||||||
sub hook_mail {
|
sub hook_mail {
|
||||||
my ($self, $transaction, $sender, %param) = @_;
|
my ($self, $transaction, $sender, %param) = @_;
|
||||||
|
|
||||||
$self->populate_invalid_networks();
|
return DECLINED if $self->is_immune();
|
||||||
|
|
||||||
# check first, so results are noted for other plugins
|
if ( $sender eq '<>' ) {
|
||||||
|
$transaction->notes('resolvable_fromhost', 'null');
|
||||||
|
$self->log(LOGINFO, "pass, null sender");
|
||||||
|
return DECLINED;
|
||||||
|
};
|
||||||
|
|
||||||
|
$self->populate_invalid_networks();
|
||||||
my $resolved = $self->check_dns($sender->host, $transaction);
|
my $resolved = $self->check_dns($sender->host, $transaction);
|
||||||
|
|
||||||
return DECLINED if $resolved; # success, no need to continue
|
return DECLINED if $resolved; # success, no need to continue
|
||||||
return DECLINED if $self->is_immune( $sender, $transaction );
|
#return DECLINED if $sender->host; # reject later
|
||||||
return DECLINED if ! $self->{_args}{reject};
|
|
||||||
|
|
||||||
return DECLINED if $sender->host; # reject later
|
if ( ! $self->{_args}{reject} ) {;
|
||||||
|
$self->log(LOGINFO, 'skip, reject disabled' );
|
||||||
|
return DECLINED;
|
||||||
|
};
|
||||||
|
|
||||||
$self->log(LOGWARN, "FQDN required in envelope sender");
|
my $result = $transaction->notes('resolvable_fromhost') or do {
|
||||||
return Qpsmtpd::DSN->addr_bad_from_system( $self->get_reject_type(),
|
return Qpsmtpd::DSN->temp_resolver_failed( $self->get_reject_type(), '' );
|
||||||
"FQDN required in the envelope sender");
|
};
|
||||||
}
|
|
||||||
|
|
||||||
sub hook_rcpt {
|
return DECLINED if $result =~ /^(?:a|ip|mx)$/; # success
|
||||||
my ($self, $transaction, $recipient, %args) = @_;
|
return DECLINED if $result =~ /^(?:whitelist|null|naughty)$/; # immunity
|
||||||
|
|
||||||
my $result = $transaction->notes('resolvable_fromhost');
|
|
||||||
return DECLINED if ! $self->{_args}{reject}; # no reject policy
|
|
||||||
return DECLINED if $result =~ /^(a|ip|mx)$/; # success
|
|
||||||
return DECLINED if $result =~ /^(whitelist|null|config)$/; # immunity
|
|
||||||
|
|
||||||
$self->log(LOGINFO, $result ); # log error
|
$self->log(LOGINFO, $result ); # log error
|
||||||
return Qpsmtpd::DSN->temp_resolver_failed( $self->get_reject_type(), $result );
|
|
||||||
|
return Qpsmtpd::DSN->addr_bad_from_system( $self->get_reject_type(),
|
||||||
|
"FQDN required in the envelope sender");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_dns {
|
sub check_dns {
|
||||||
@ -135,7 +138,7 @@ sub check_dns {
|
|||||||
$transaction->notes('resolvable_fromhost_host', $host);
|
$transaction->notes('resolvable_fromhost_host', $host);
|
||||||
|
|
||||||
if ( $host =~ m/^\[(\d{1,3}\.){3}\d{1,3}\]$/ ) {
|
if ( $host =~ m/^\[(\d{1,3}\.){3}\d{1,3}\]$/ ) {
|
||||||
$self->log(LOGINFO, "skip: $host is an IP");
|
$self->log(LOGINFO, "skip, $host is an IP");
|
||||||
$transaction->notes('resolvable_fromhost', 'ip');
|
$transaction->notes('resolvable_fromhost', 'ip');
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@ -151,12 +154,12 @@ sub check_dns {
|
|||||||
my @host_answers = $self->get_host_records( $res, $host, $transaction );
|
my @host_answers = $self->get_host_records( $res, $host, $transaction );
|
||||||
foreach my $rr (@host_answers) {
|
foreach my $rr (@host_answers) {
|
||||||
if ( $rr->type eq 'A' || $rr->type eq 'AAAA' ) {
|
if ( $rr->type eq 'A' || $rr->type eq 'AAAA' ) {
|
||||||
$self->log(LOGINFO, "pass: found valid A for $host");
|
$self->log(LOGINFO, "pass, found A for $host");
|
||||||
$transaction->notes('resolvable_fromhost', 'a');
|
$transaction->notes('resolvable_fromhost', 'a');
|
||||||
return $self->ip_is_valid($rr->address);
|
return $self->ip_is_valid($rr->address);
|
||||||
};
|
};
|
||||||
if ( $rr->type eq 'MX' ) {
|
if ( $rr->type eq 'MX' ) {
|
||||||
$self->log(LOGINFO, "pass: found valid MX for $host");
|
$self->log(LOGINFO, "pass, found MX for $host");
|
||||||
$transaction->notes('resolvable_fromhost', 'mx');
|
$transaction->notes('resolvable_fromhost', 'mx');
|
||||||
return $self->mx_address_resolves($rr->exchange, $host);
|
return $self->mx_address_resolves($rr->exchange, $host);
|
||||||
};
|
};
|
||||||
@ -184,21 +187,21 @@ sub get_and_validate_mx {
|
|||||||
|
|
||||||
my @mx = mx($res, $host);
|
my @mx = mx($res, $host);
|
||||||
if ( ! scalar @mx ) { # no mx records
|
if ( ! scalar @mx ) { # no mx records
|
||||||
$self->log(LOGINFO, "no MX records for $host");
|
$self->log(LOGINFO, "$host has no MX");
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach my $mx (@mx) {
|
foreach my $mx (@mx) {
|
||||||
# if any MX is valid, then we consider the domain resolvable
|
# if any MX is valid, then we consider the domain resolvable
|
||||||
if ( $self->mx_address_resolves($mx->exchange, $host) ) {
|
if ( $self->mx_address_resolves($mx->exchange, $host) ) {
|
||||||
$self->log(LOGINFO, "pass: $host has valid MX at " . $mx->exchange);
|
$self->log(LOGINFO, "pass, $host has MX at " . $mx->exchange);
|
||||||
$transaction->notes('resolvable_fromhost', 'mx');
|
$transaction->notes('resolvable_fromhost', 'mx');
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
# if there are MX records, and we got here, none are valid
|
# if there are MX records, and we got here, none are valid
|
||||||
$self->log(LOGINFO, "fail: invalid MX for $host");
|
$self->log(LOGINFO, "fail, invalid MX for $host");
|
||||||
$transaction->notes('resolvable_fromhost', "invalid MX for $host");
|
$transaction->notes('resolvable_fromhost', "invalid MX for $host");
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
@ -226,7 +229,7 @@ sub get_host_records {
|
|||||||
|
|
||||||
if ( ! scalar @answers) {
|
if ( ! scalar @answers) {
|
||||||
if ( $res->errorstring ne 'NXDOMAIN' ) {
|
if ( $res->errorstring ne 'NXDOMAIN' ) {
|
||||||
$self->log(LOGWARN, "$$ query for $host failed: ", $res->errorstring);
|
$self->log(LOGWARN, "fail, query for $host, ", $res->errorstring);
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -257,8 +260,9 @@ sub mx_address_resolves {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! @mx_answers) {
|
if (! @mx_answers) {
|
||||||
$self->log(LOGWARN, "query for $fromhost failed: ", $res->errorstring)
|
if ( $res->errorstring eq 'NXDOMAIN' ) {
|
||||||
unless $res->errorstring eq "NXDOMAIN";
|
$self->log(LOGWARN, "fail, query for $fromhost, ", $res->errorstring);
|
||||||
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,37 +286,3 @@ sub populate_invalid_networks {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
sub is_immune {
|
|
||||||
my ($self, $sender, $transaction) = @_;
|
|
||||||
|
|
||||||
if ( $self->qp->connection->notes('whitelisthost') ) {
|
|
||||||
$transaction->notes('resolvable_fromhost', 'whitelist');
|
|
||||||
$self->log(LOGINFO, "pass: whitelisted");
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( $sender eq '<>' ) {
|
|
||||||
$transaction->notes('resolvable_fromhost', 'null');
|
|
||||||
$self->log(LOGINFO, "pass: null sender");
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( ! $self->{_args}{reject} ) {
|
|
||||||
$transaction->notes('resolvable_fromhost', 'config');
|
|
||||||
$self->log(LOGINFO, "skip: reject not enabled in config.");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
sub get_reject_type {
|
|
||||||
my $self = shift;
|
|
||||||
my $default = shift || DENYSOFT;
|
|
||||||
my $deny = $self->{_args}{reject_type} or return $default;
|
|
||||||
|
|
||||||
return $deny =~ /^(temp|soft)$/i ? DENYSOFT
|
|
||||||
: $deny =~ /^(perm|hard)$/i ? DENY
|
|
||||||
: $deny eq 'disconnect' ? DENY_DISCONNECT
|
|
||||||
: $default;
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user