#!/usr/bin/perl use Danga::DNS; my %invalid = (); sub init { my ($self, $qp) = @_; foreach my $i ($qp->config("invalid_resolvable_fromhost")) { $i =~ s/^\s*//; $i =~ s/\s*$//; if ($i =~ m#^((\d{1,3}\.){3}\d{1,3})/(\d\d?)#) { $invalid{$1} = $3; } } } sub hook_mail { my ($self, $transaction, $sender) = @_; return DECLINED if ($self->qp->connection->notes('whitelistclient')); $self->transaction->notes('resolvable', 1); return DECLINED if $sender->format eq "<>"; return $self->check_dns($sender->host); } sub check_dns { my ($self, $host) = @_; # for stuff where we can't even parse a hostname out of the address return DECLINED unless $host; if( $host =~ m/^\[(\d{1,3}\.){3}\d{1,3}\]$/ ) { $self->transaction->notes('resolvable', 1); return DECLINED; } my $total_queries = 2; my $qp = $self->qp; $self->log(LOGDEBUG, "Checking $host for MX record in the background"); Danga::DNS->new( callback => sub { dns_result($qp, @_) }, finished => sub { $total_queries--; finished($qp, $total_queries) }, host => $host, type => "MX", client => $qp->input_sock, ); $self->log(LOGDEBUG, "Checking $host for A record in the background"); Danga::DNS->new( callback => sub { dns_result($qp, @_) }, finished => sub { $total_queries--; finished($qp, $total_queries) }, host => $host, client => $qp->input_sock, ); return CONTINUATION; } sub finished { my ($qp, $total_zones) = @_; $qp->finish_continuation unless $total_zones; } sub dns_result { my ($qp, $result, $query) = @_; if ($result =~ /^[A-Z]+$/) { # probably an error $qp->log(LOGDEBUG, "DNS error: $result looking up $query"); } else { $qp->transaction->notes('resolvable', 1); $qp->log(LOGDEBUG, "DNS lookup $query returned: $result"); } } sub hook_rcpt { my ($self, $transaction) = @_; if (!$transaction->notes('resolvable')) { my $sender = $transaction->sender; $self->log(LOGDEBUG, "Could not resolve " .$sender->host) if $sender->host; return (DENYSOFT, ($sender->host ? "Could not resolve ". $sender->host : "FQDN required in the envelope sender")); } return DECLINED; }