dbaa9dbd6c
on files in plugins dir: fixed a number of POD errors formatted some # comments into POD removed bare 1; (these are plugins, not perl modules) most instances of this were copy/pasted from a previous plugin that had it removed instances of # vim ts=N ... they weren't consistent, many didn't match .perltidyrc on modules that failed perl -c tests, added 'use Qpsmtpd::Constants;' Conflicts: plugins/async/check_earlytalker plugins/async/dns_whitelist_soft plugins/async/dnsbl plugins/async/queue/smtp-forward plugins/async/require_resolvable_fromhost plugins/async/rhsbl plugins/async/uribl plugins/auth/auth_checkpassword plugins/auth/auth_cvm_unix_local plugins/auth/auth_flat_file plugins/auth/auth_ldap_bind plugins/auth/auth_vpopmail plugins/auth/auth_vpopmail_sql plugins/auth/authdeny plugins/check_badmailfromto plugins/check_badrcptto_patterns plugins/check_bogus_bounce plugins/check_earlytalker plugins/check_norelay plugins/check_spamhelo plugins/connection_time plugins/dns_whitelist_soft plugins/dnsbl plugins/domainkeys plugins/greylisting plugins/hosts_allow plugins/http_config plugins/logging/adaptive plugins/logging/apache plugins/logging/connection_id plugins/logging/transaction_id plugins/logging/warn plugins/milter plugins/queue/exim-bsmtp plugins/queue/maildir plugins/queue/postfix-queue plugins/queue/smtp-forward plugins/quit_fortune plugins/random_error plugins/rcpt_map plugins/rcpt_regexp plugins/relay_only plugins/require_resolvable_fromhost plugins/rhsbl plugins/sender_permitted_from plugins/spamassassin plugins/tls plugins/tls_cert plugins/uribl plugins/virus/aveclient plugins/virus/bitdefender plugins/virus/clamav plugins/virus/clamdscan plugins/virus/hbedv plugins/virus/kavscanner plugins/virus/klez_filter plugins/virus/sophie plugins/virus/uvscan
161 lines
4.5 KiB
Perl
161 lines
4.5 KiB
Perl
#!perl -Tw
|
|
|
|
sub register {
|
|
my ($self, $qp, $denial ) = @_;
|
|
if ( defined $denial and $denial =~ /^disconnect$/i ) {
|
|
$self->{_rhsbl}->{DENY} = DENY_DISCONNECT;
|
|
}
|
|
else {
|
|
$self->{_rhsbl}->{DENY} = DENY;
|
|
}
|
|
|
|
}
|
|
|
|
sub hook_mail {
|
|
my ($self, $transaction, $sender, %param) = @_;
|
|
|
|
my $res = new Net::DNS::Resolver;
|
|
my $sel = IO::Select->new();
|
|
my %rhsbl_zones_map = ();
|
|
|
|
# Perform any RHS lookups in the background. We just send the query packets
|
|
# here and pick up any results in the RCPT handler.
|
|
# MTAs gets confused when you reject mail during MAIL FROM:
|
|
|
|
my %rhsbl_zones = map { (split /\s+/, $_, 2)[0,1] } $self->qp->config('rhsbl_zones');
|
|
|
|
if ($sender->format ne '<>' and %rhsbl_zones) {
|
|
push(my @hosts, $sender->host);
|
|
#my $helo = $self->qp->connection->hello_host;
|
|
#push(@hosts, $helo) if $helo && $helo ne $sender->host;
|
|
for my $host (@hosts) {
|
|
for my $rhsbl (keys %rhsbl_zones) {
|
|
# fix to find TXT records, if the rhsbl_zones line doesn't have second field
|
|
if (defined($rhsbl_zones{$rhsbl})) {
|
|
$self->log(LOGDEBUG, "Checking $host.$rhsbl for A record in the background");
|
|
$sel->add($res->bgsend("$host.$rhsbl"));
|
|
} else {
|
|
$self->log(LOGDEBUG, "Checking $host.$rhsbl for TXT record in the background");
|
|
$sel->add($res->bgsend("$host.$rhsbl", "TXT"));
|
|
}
|
|
$rhsbl_zones_map{"$host.$rhsbl"} = $rhsbl_zones{$rhsbl};
|
|
}
|
|
}
|
|
|
|
%{$self->{_rhsbl_zones_map}} = %rhsbl_zones_map;
|
|
$transaction->notes('rhsbl_sockets', $sel);
|
|
} else {
|
|
$self->log(LOGDEBUG, 'no RHS checks necessary');
|
|
}
|
|
|
|
return DECLINED;
|
|
}
|
|
|
|
sub hook_rcpt {
|
|
my ($self, $transaction, $rcpt) = @_;
|
|
my $host = $transaction->sender->host;
|
|
my $hello = $self->qp->connection->hello_host;
|
|
|
|
my $result = $self->process_sockets;
|
|
if ($result && defined($self->{_rhsbl_zones_map}{$result})) {
|
|
if ($result =~ /^$host\./ ) {
|
|
return ($self->{_rhsbl}->{DENY}, "Mail from $host rejected because it " . $self->{_rhsbl_zones_map}{$result});
|
|
} else {
|
|
return ($self->{_rhsbl}->{DENY}, "Mail from HELO $hello rejected because it " . $self->{_rhsbl_zones_map}{$result});
|
|
}
|
|
}
|
|
return ($self->{_rhsbl}->{DENY}, $result) if $result;
|
|
return DECLINED;
|
|
}
|
|
|
|
sub process_sockets {
|
|
my ($self) = @_;
|
|
my $trans = $self->transaction;
|
|
my $result = '';
|
|
|
|
return $trans->notes('rhsbl') if $trans->notes('rhsbl');
|
|
|
|
my $res = new Net::DNS::Resolver;
|
|
my $sel = $trans->notes('rhsbl_sockets') or return '';
|
|
|
|
$self->log(LOGDEBUG, 'waiting for rhsbl dns');
|
|
|
|
# don't wait more than 8 seconds here
|
|
my @ready = $sel->can_read(8);
|
|
|
|
$self->log(LOGDEBUG, 'DONE waiting for rhsbl dns, got ' , scalar @ready, ' answers ...') ;
|
|
return '' unless @ready;
|
|
|
|
for my $socket (@ready) {
|
|
my $query = $res->bgread($socket);
|
|
$sel->remove($socket);
|
|
undef $socket;
|
|
|
|
if ($query) {
|
|
foreach my $rr ($query->answer) {
|
|
$self->log(LOGDEBUG, 'got an ' . $rr->type . ' record ' . $rr->name);
|
|
if ($rr->type eq 'A') {
|
|
$result = $rr->name;
|
|
$self->log(LOGDEBUG, "A record found for $result with IP " . $rr->address);
|
|
last;
|
|
} elsif ($rr->type eq 'TXT') {
|
|
$result = $rr->txtdata;
|
|
$self->log(LOGDEBUG, "TXT record found: " . $rr->txtdata);
|
|
last;
|
|
}
|
|
}
|
|
} else {
|
|
$self->log(LOGCRIT, "query failed: ", $res->errorstring) unless $res->errorstring eq 'NXDOMAIN';
|
|
}
|
|
|
|
if ($result) {
|
|
#kill any other pending I/O
|
|
$trans->notes('rhsbl_sockets', undef);
|
|
return $trans->notes('rhsbl', $result);
|
|
}
|
|
}
|
|
|
|
if ($sel->count) {
|
|
# loop around if we have dns results left
|
|
return $self->process_sockets();
|
|
}
|
|
|
|
# if there was more to read; then forget it
|
|
$trans->notes('rhsbl_sockets', undef);
|
|
|
|
return $trans->notes('rhsbl', $result);
|
|
}
|
|
|
|
sub hook_disconnect {
|
|
my ($self, $transaction) = @_;
|
|
|
|
$transaction->notes('rhsbl_sockets', undef);
|
|
return DECLINED;
|
|
}
|
|
|
|
|
|
=head1 NAME
|
|
|
|
rhsbl - handle RHSBL lookups
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Pluging that checks the host part of the sender's address against a
|
|
configurable set of RBL services.
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
This plugin reads the lists to use from the rhsbl_zones configuration
|
|
file. Normal domain based dns blocking lists ("RBLs") which contain TXT
|
|
records are specified simply as:
|
|
|
|
dsn.rfc-ignorant.org
|
|
|
|
To configure RBL services which do not contain TXT records in the DNS,
|
|
but only A records, specify, after a whitespace, your own error message
|
|
to return in the SMTP conversation e.g.
|
|
|
|
abuse.rfc-ignorant.org does not support abuse@domain
|
|
|
|
=cut
|