Rewritten URIBL plugin take two
[email protected] ("Jared Johnson")
| Newsgroups | perl.qpsmtpd |
|---|---|
| Message-ID | <[email protected]> |
Attached:
uribl: the new URIBL plugin with some changes suggested on the list
uribl.diff: diff from the first copy of this plugin I posted to the list
for anyone who's been following it and prefers that. *not* a
diff from the original plugin, that would be too noisy to be
useful :)
parse_mime: new plugin that can optionally be added in order to decode MIME;
the uribl plugin will use the decoded MIME if it's there, else
it will use the old method. I didn't try to do anything cooler
with the old method yet as I haven't had time to set up a test
async.diff: diff necessary for the async plugin to work with this plugin.
UNTESTED!
gen_tld_lists: the script I've been using to generate tld_lists.pl, FTR
Feel free to reiterate any old requests, I may have had a reason not to do
it or I may have just forgotten :) Highlights:
- new config format (the old method is still supported). $zones is now
more genuinely a structure for 'service specific defaults'
- shifted things around to support async and patched the async plugin and
Q::Plugin::Async::DNSBLBase to work with the new plugin. I tried very
hard not to touch these things much because I have no way to test, but it
was necessary to make quite a lot of changes to support the new plugin,
especially to support lookup_a_record and lookup_ns_record. I am *sure*
there are serious bugs in this but I'm fairly convinced they will be
trivially found and fixed.
- some more entity un-munging and fixed the & thing
- a handful of other little things I did in the course of my own work
I haven't yet pulled the trigger on moving tld_lists.pl into an API, see
the thread on why.
I am somewhat interested in switching the non-async plugin to still use
ParaDNS, but it's not in my immediate plans; it doesn't make much
difference for performance of the non-async version, and it would mean a
new dependency on a module that apparently is not packaged for Debian in
our own Debian-based software. Plus ParaDNS or at least the DNSBLBase
wrapper is not quite as flexible and easy to work with as N::D::A.
This code is a bit fresher so there may be further updates as I get
further into testing. I'll probably wait to submit those until it's time
for hopefully one final shot. That seems easiest anyway considering that
I still don't have a timeline on getting my git on again. My apologies
once again for the lack of git. Happy reviewing!
-Jared
uribl
(application/octet-stream, 22.6 KB) - not displayed
uribl.diff
(text/x-patch, 16.3 KB)
--- dc-smtpd//plugins/dc/uribl.qp 2010-07-26 15:50:21.425571890 -0500
+++ dc-smtpd//plugins/dc/uribl 2010-07-27 04:59:27.685571284 -0500
@@ -15,13 +15,14 @@
To enable the plugin, add it to I<~qpsmtpd/config/plugins>. The list of
URIBLs to check should be placed in I<uribl_zones> in the config directory
-(typically I<~qpsmtpd/config>).
+(typically I<~qpsmtpd/config>). If no zones appear in this file, a default
+set of services is checked.
The format of the I<uribl_zones> file is a list of URIBL DNS zones, one per
line, consisting of one or more columns separated by whitespace. The first
column (the only mandatoy one) should consist of the URIBL zone.
-The second column may contain a comma-delimited list of integers selecting
+The second column may optionally contain a comma-delimited list of integers selecting
mask values to be applied to the A record(s) returned from a URIBL. This
enables the use of composite DNSBLs, such as multi.surbl.org, where several
lists are combined so they may be accessed with a single query; any returns
@@ -30,15 +31,74 @@
checked. URIBL operators prefer that you use the composite lists to reduce
their own query load, and it's more efficient for qpsmtpd as well.
+The next two columns may optionally contain a configuration directive and
+corresponding value as described below, to be applied either to the entire
+zone, or to the masks specified in the second column.
The third column specifies an action, which overrides the default action
configured with the I<action> setting discussed below.
+Any masks and parameters not listed will inherit default parameters recommended
+by the zone in question, if known.
+
For example:
=over 4
- multi.surbl.org 2,8 deny
- ob.surbl.org 1 add-header
+ dbl.spamhaus.org
+
+ multi.uribl.com action denysoft
+ multi.uribl.com 2,4 action deny
+
+ multi.surbl.org
+ multi.surbl.org 2,8 action deny
+ multi.surbl.org 4,16
+
+=back
+
+You may specify the following config option(s) in the I<uribl_zones> file:
+
+=over 4
+
+=item action
+
+Overrides the default action configured with the I<action> setting discussed
+below. This can be set either for the entire zone or for a specific set of
+masks.
+
+=item check_ip
+
+Submit IP addresses found in URIs to the URIBL service. Some services
+discourage sending IP addresses and some warn that they will result in false
+positives. SpamHaus DBL, for instance, strongly discourages it and will in fact
+always give a positive result when sent an IP address. Defaults to enabled
+unless service-specific defaults exists.
+
+=item lookup_a_record
+
+Look up A records on hostnames found in URIs and check the results against the
+zone in question. SBL-XBL is one service that specifically recommends doing
+this; others discourage it and may generate false positives if you try it.
+Defaults to disabled unless service-specific defaults exist.
+
+=item lookup_ns_record
+
+Look up NS records on hostnames found in URIs and check the results against the
+zone in question. Like lookup_a_records, this is recommended by SBL-XBL but
+could generate false positives on other services. Defaults to disabled unless
+service-specific defaults exist.
+
+=item check_host
+
+Submit hostnames found in URIs to the URIBL service. Some services, especially
+'RBL' services, only list IP addresses. SBL-XBL is an example of this; SpamHaus
+recommends checking IP addresses from URIs to this list. Defaults to enabled
+unless service-specific defaults exist.
+
+=item host_wildcards_supported
+
+The zone supports wildcards -- e.g. checking a.b.c.com will generate a hit if
+a.b.c.com, b.c.com, or c.com is listed. Spamhaus DBL supports wildcard; most
+services do not. Defaults to disabled unless service-specific defaults exist.
=back
@@ -105,27 +165,27 @@
my $zones = {
'dbl.spamhaus.org' => {
label => 'Spamhaus DBL',
- action => 'log',
+ # action => 'log'
check_ip => 0,
check_host => 1,
host_wildcards_supported => 1,
},
'sbl-xbl.spamhaus.org' => {
label => 'Spamhaus SBL/XBL',
- action => 'log',
check_ip => 1,
lookup_a_record => 1,
lookup_ns_record => 1,
check_host => 0,
+ host_wildcards_supported => 0,
},
'multi.surbl.org' => {
label => 'SURBL',
- masks => { 2 => { label => 'SpamCop', action => 'log' },
- 4 => { label => 'sa-blacklist', action => 'log' },
- 8 => { label => 'Phishing and Malware', action => 'log' },
- 16 => { label => 'Outblaze', action => 'log' },
- 32 => { label => 'AbuseButler', action => 'log' },
- 64 => { label => 'jwSpamSpy', action => 'log' } },
+ masks => { 2 => { label => 'SpamCop' }, # , action => 'log' },
+ 4 => { label => 'sa-blacklist' },
+ 8 => { label => 'Phishing and Malware' },
+ 16 => { label => 'Outblaze' },
+ 32 => { label => 'AbuseButler' },
+ 64 => { label => 'jwSpamSpy' } },
check_ip => 1,
check_host => 1,
host_wildcards_supported => 0,
@@ -134,10 +194,9 @@
},
'multi.uribl.com' => {
label => 'URIBL',
- masks => { 2 => { label => 'Black', action => 'log' },
- 4 => { label => 'Grey', action => 'log' },
- 8 => { label => 'Red', action => 'log' },
- 16 => { label => 'Gold', action => 'log' } },
+ masks => { 2 => { label => 'Black' },
+ 4 => { label => 'Grey' },
+ 8 => { label => 'Red' } },
check_ip => 1,
check_host => 1,
host_wildcards_supported => 0,
@@ -146,9 +205,10 @@
},
};
-# Derive individual lists from their 'multi' counterparts
+# Derive individual list defaults from their 'multi' counterparts
my $surbl = $zones->{'multi.surbl.org'};
-for ( [ 2 => 'sc' ], [ 4 => 'ws' ], [ 8 => 'ph' ], [ 16 => 'ob' ], [ 32 => 'ab' ], [ 64 => 'jp' ] ) {
+for ( [ 2 => 'sc' ], [ 4 => 'ws' ], [ 8 => 'ph' ],
+ [ 16 => 'ob' ], [ 32 => 'ab' ], [ 64 => 'jp' ] ) {
my $z = $zones->{ "$_->[1].surbl.org" } = { %$surbl };
my $descr = $z->{masks}->{ $_->[0] }->{label};
$z->{label} = "$z->{label} ($descr)";
@@ -164,27 +224,51 @@
-sub init {
+sub init { # used by async plugin
my ( $self, $qp, %args ) = @_;
- $self->{timeout} = $args{timeout} || 30;
+ $self->{timeout} = $args{timeout} || $qp->config('dns_timeout') || 30;
$self->{whitelist_zones} = { map { $_ => undef } $qp->config('uribl_whitelist_domains') };
+ # Includes support for legacy arg 'scan-headers'
( $self->{check_headers} ) = grep { defined } ( @args{qw( check-headers scan-headers )}, 0 );
my $default_action = $args{action} || 'add-header';
unless ( $qp->config('uribl_zones') ) {
- $self->{zones} = $zones;
+ $self->{zones} = { map { $_ => $zones->{$_} }
+ qw( dbl.spamhaus.org sbl-xbl.spamhaus.org
+ multi.surbl.org multi.uribl.com ) };
$self->init_masks;
return;
}
my $localzones;
for ( $self->qp->config('uribl_zones') ) {
- my ( $zone, $masks, $action ) = split /\s+/;
+ my ( $zone, @arg ) = split /\s+/;
my $z = $localzones->{$zone} ||= {};
- $z->{action} ||= $action if $action;
- for ( split /,/, $masks || '' ) {
- $z->{masks}->{$_}->{action} = $action if $action;
- next unless exists $zones->{$zone} and exists $zones->{$zone}->{masks};
- $z->{masks}->{$_}->{action} ||= $zones->{$zone}->{masks}->{$_}->{action} || $action;;
- $z->{masks}->{$_}->{label} = $zones->{$zone}->{masks}->{$_}->{label} || $_;
+ my @c = qw( action check_ip lookup_a_record lookup_ns_record
+ check_host host_wildcards_supported );
+ if ( grep { $arg[0] eq $_ } @c ) {
+ # Config directive for entire zone
+ my ( $key, $value ) = @arg;
+ die "Could not parse config line: $_" unless defined $value;
+ $z->{$_} = $value if defined $value;
+ next;
+ } elsif ( $arg[0] !~ /[\d,]/ ) {
+ die "Could not parse config line: $_";
+ }
+ # mask definitions / settings
+ my ( $masks, $key, $value ) = @arg;
+ $z->{masks}->{$_} ||= {} for split /,/, $masks;
+ next unless defined $key; # Mask definition with no additional prefs
+ if ( grep { $key eq $_ } qw( log add-header deny denysoft ) ) { # legacy definition
+ ( $key, $value ) = ( 'action', $key );
+ $z->{action} ||= $value;
+ }
+ die "Could not parse config line: $_" if ! defined $value or ! grep { $key eq $_ } @c;
+ for my $m ( split /,/, $masks ) {
+ $z->{masks}->{$m}->{$key} = $value;
+ next if ! exists $zones->{$zone}
+ or ! exists $zones->{$zone}->{masks}
+ or ! exists $zones->{$zone}->{masks}->{$m};
+ $z->{masks}->{$m}->{label} ||= $zones->{$zone}->{masks}->{$m}->{label} || $m;
+ $z->{masks}->{$m}->{action} ||= $zones->{$zone}->{masks}->{$m}->{action};
}
}
for my $zone ( keys %$localzones ) {
@@ -206,6 +290,8 @@
$self->init_masks;
}
+sub register { shift->register_hook( data_post => 'data_handler' ) }
+
sub init_masks {
for my $zone ( values %{ shift->{zones} } ) {
my $mask = 0;
@@ -216,27 +302,29 @@
}
}
-sub send_query {
+sub send_queries { # used async plugin
+ # Determine which queries to send to which zones and ship them off to the appropriate sub
my ( $self, $addr, $arg ) = @_;
return unless $addr;
- $self->{dns} ||= Net::DNS::Async->new( Timeout => $self->{timeout} ) or return;
+ # Allow the async version to use ParaDNS rather than Net::DNS::Async
+ my $sendq = $self->{async_version} ? 'queue_pdns_query' : 'send_query';
for my $z ( $arg->{zone} or keys %{ $self->{zones} } ) {
my $q = { zone => $z, name => $arg->{forward_ip} || $addr };
$self->{start_time} ||= time();
if ( $self->{zones}->{$z}->{lookup_ns_record} and ! $arg->{from_cb} ) {
- $self->add_lookup( $q, sub { $self->ns_record_cb( $q, @_ ) }, $addr, 'NS' );
+ $self->$sendq( $q, 'ns_record', $addr, 'NS' );
}
my $hostname;
if ( ! $arg->{forward_ip} ) { # hostname
next if $self->host_whitelisted( $addr );
- $self->add_lookup( $q, sub { $self->a_record_cb( $q, @_ ) }, $addr, 'A' )
+ $self->$sendq( $q, 'a_record', $addr, 'A' )
if $self->{zones}->{$z}->{lookup_a_record};
next unless $self->{zones}->{$z}->{check_host};
$hostname = $self->shorten_host( $addr, $z );
}
next if $arg->{forward_ip} and ! $self->{zones}->{$z}->{check_ip};
my $querystring = $hostname ? "$hostname.$z" : "$addr.$z";
- $self->add_lookup( $q, sub { $self->uri_cb( $q, @_ ) }, $querystring, $_ ) for qw( A TXT );
+ $self->$sendq( $q, 'uri', $querystring, $_ ) for qw( A TXT );
}
}
@@ -267,8 +355,11 @@
return join '.',@h[-2..-1];
}
-sub add_lookup {
- my ( $self, $q, $cb, $querystring, $type ) = @_;
+sub send_query { # NOT used by async plugin
+ my ( $self, $q, $cbtype, $querystring, $type ) = @_;
+ $cbtype = $cbtype . '_cb';
+ my $cb = sub { $self->$cbtype( $q, @_ ) };
+ $self->{dns} ||= Net::DNS::Async->new( Timeout => $self->{timeout} ) or return;
my $key = "$q->{zone}|$querystring|$type";
return if exists $self->{queries}->{$key};
$self->{queries}->{$key} = undef;
@@ -310,7 +401,7 @@
my $fwd = join '.', @fwd;
my $rev = join '.', reverse @fwd;
$self->log(LOGDEBUG,"uribl: looking up $fwd (A record of $q->{name})");
- $self->send_query( $rev, { forward_ip => $fwd, zone => $q->{zone}, from_cb => 1 } );
+ $self->send_queries( $rev, { forward_ip => $fwd, zone => $q->{zone}, from_cb => 1 } );
}
}
@@ -322,23 +413,24 @@
my $address = $answer->address or next;
if ( $address =~ /[^\d.]/ ) { # hostname
$self->log(LOGDEBUG,"uribl: looking up $address (NS record of $q->{name})");
- $self->send_query( $address, { zone => $q->{zone}, from_cb => 1 } );
+ $self->send_queries( $address, { zone => $q->{zone}, from_cb => 1 } );
} else { # IP address
my @fwd = split /\./, $address or next;
my $fwd = join '.', @fwd;
my $rev = join '.', reverse @fwd;
$self->log(LOGDEBUG,"uribl: looking up $fwd (NS record of $q->{name})");
- $self->send_query( $rev, { forward_ip => $fwd, zone => $q->zone, from_cb => 1 } );
+ $self->send_queries( $rev, { forward_ip => $fwd, zone => $q->{zone}, from_cb => 1 } );
}
}
}
-sub cleanup {
+sub cleanup { # used by async plugin
my ( $self ) = @_;
- delete @$self{qw( dns matched start_time queries_completed queries started_queries)};
+ delete @$self{qw( dns matched start_time queries_completed queries
+ started_queries a_lookups ns_lookups pdns_lookups )};
}
-sub evaluate {
+sub evaluate { # used by async plugin
my ( $self, $zone, $a ) = @_;
return unless $zone and $a;
my $z = $self->{zones}->{$zone};
@@ -376,7 +468,7 @@
$self->find_uris($line) while ( $line = $txn->body_getline );
}
-sub find_uris {
+sub find_uris { # used by async plugin
my $self = shift;
$_ = shift;
chomp;
@@ -384,11 +476,13 @@
s/[=%]([0-9A-Fa-f]{2,2})/chr(hex($1))/ge;
# Undo HTML entity munging (e.g. in parameterized redirects)
s/&#(\d{2,3});?/chr($1)/ge;
+ # Undo more entity munging
+ s/&#?x0*([0-9A-Fa-f]{2});?/chr(hex($1))/ge;
# Un-encode a few common important named entities and discard the rest
s/ / /go;
- s/&/&/go;
s/>/>/go;
s/</</go;
+ s/&/&/go;
s/&\w{2,6};//go;
while ( m{
@@ -408,7 +502,7 @@
my $rev = join('.', reverse @octets);
$self->log(LOGDEBUG, "uribl: matched pure-integer ipaddr $1 ($fwd)");
unless (exists $self->{pending}->{$rev}) {
- $self->send_query($rev, { forward_ip => $fwd });
+ $self->send_queries($rev, { forward_ip => $fwd });
$self->{pending}->{$rev} = undef;
}
}
@@ -431,7 +525,7 @@
my $rev = join('.', reverse @octets);
$self->log(LOGDEBUG, "uribl: matched URI ipaddr $fwd");
unless (exists $self->{pending}->{$rev}) {
- $self->send_query($rev, { forward_ip => $fwd });
+ $self->send_queries($rev, { forward_ip => $fwd });
$self->{pending}->{$rev} = undef;
}
}
@@ -471,13 +565,12 @@
}
next if exists $self->{pending}->{$host};
$self->log(LOGDEBUG, "uribl: matched hostname $host");
- $self->send_query( $host );
+ $self->send_queries( $host );
}
}
-sub hook_data_post {
- my ($self, $txn) = @_;
- $self->cleanup();
+sub scan_message { # used by async plugin
+ my ( $self, $txn ) = @_;
if ( $self->{check_headers} ) {
$self->find_uris($_) for map { $txn->header->get($_) } $txn->header->tags;
}
@@ -485,15 +578,26 @@
: $self->scan_body( $txn );
unless ( $self->{started_queries} ) {
$self->log(LOGINFO, "No URIs found in mail");
- return DECLINED;
+ return 0;
}
+ return 1;
+}
+
+sub data_handler { # NOT used by async plugin
+ my ( $self, $txn ) = @_;
+ $self->cleanup();
+ return DECLINED unless $self->scan_message( $txn );
$self->{dns}->await() if $self->{dns};
$self->log(LOGINFO, sprintf( "lookups: %2d matches: %2d time: %.6f",
$self->{queries_completed},
scalar @{ $self->{matches} || [] },
time() - $self->{start_time} ));
+ return $self->check_results( $txn, $self->{matches} );
+}
- for my $q ( @{ $self->{matches} || [] } ) {
+sub check_results { # used by async plugin
+ my ( $self, $txn, $matches ) = @_;
+ for my $q ( @{ $matches || [] } ) {
$self->log(LOGDEBUG, $q->{desc});
for ( @{ $q->{lists} } ) {
$self->log(LOGWARN, "$q->{name} FOUND IN $_->{label}");
parse_mime
(application/octet-stream, 1 KB) - not displayed
async.diff
(text/x-patch, 8.8 KB)
Index: plugins/async/uribl
===================================================================
--- plugins/async/uribl (revision 12753)
+++ plugins/async/uribl (working copy)
@@ -27,55 +27,58 @@
sub start_data_post {
my ($self, $transaction) = @_;
- my $class = ref $self;
-
- my @names;
-
- my $queries = $self->lookup_start($transaction, sub {
- my ($self, $name) = @_;
- push @names, $name;
- });
-
- my @hosts;
- foreach my $z (keys %{$self->{uribl_zones}}) {
- push @hosts, map { "$_.$z" } @names;
- }
-
+ $self->cleanup();
$transaction->notes(uribl_results => {});
- $transaction->notes(uribl_zones => $self->{uribl_zones});
-
- return DECLINED
- unless @hosts && $class->lookup($self->qp, [ @hosts ], [ @hosts ]);
-
+ $transaction->notes(uribl_zones => $self->{zones});
+ $self->scan_message( $txn );
+ return DECLINED unless $self->{pdns_lookups}
+ and $self->lookup( $self->qp, @{ $self->{pdns_lookups} } );
return YIELD;
}
sub finish_data_post {
my ($self, $transaction) = @_;
+ my $matches = $self->collect_results($transaction);
+ return $self->check_results($matches);
+}
- my $matches = $self->collect_results($transaction);
- for (@$matches) {
- $self->log(LOGWARN, $_->{desc});
- if ($_->{action} eq 'add-header') {
- $transaction->header->add('X-URIBL-Match', $_->{desc});
- } elsif ($_->{action} eq 'deny') {
- return (DENY, $_->{desc});
- } elsif ($_->{action} eq 'denysoft') {
- return (DENYSOFT, $_->{desc});
- }
+sub queue_pdns_query {
+ my ( $self, $q, $cbtype, $querystring, $type ) = @_;
+ my $key = "$q->{zone}|$querystring|$type";
+ return if exists $self->{queries}->{$key};
+ $self->{queries}->{$key} = undef;
+ $self->log(LOGDEBUG,"Looking up $type record for '$querystring'");
+ $self->{started_queries}++;
+ if ( $cbtype eq 'uri' ) {
+ push @{ $self->{pdns_lookups}->[0] }, $querystring;
+ push @{ $self->{pdns_lookups}->[1] }, $querystring;
+ } elsif ( $cbtype eq 'a_record' ) {
+ push @{ $self->{pdns_lookups}->[0] }, $querystring;
+ $self->{a_lookups}->{$querystring}->{ $q->{zone} = $q;
+ } elsif ( $cbtype eq 'ns_record' ) {
+ push @{ $self->{pdns_lookups}->[2] }, $querystring;
+ $self->{ns_lookups}->{$querystring}->{ $q->{zone} = $q;
}
- return DECLINED;
}
sub init_resolver { }
sub process_a_result {
- my ($class, $qp, $result, $query) = @_;
-
+ my ( $self, $qp, $result, $query ) = @_;
my $transaction = $qp->transaction;
my $results = $transaction->notes('uribl_results');
my $zones = $transaction->notes('uribl_zones');
-
+ $self->{queries_completed}++;
+ if ( exists $self->{a_lookups}->{$query} ) {
+ my @fwd = split /\./, $result or next;
+ my $fwd = join '.', @fwd;
+ my $rev = join '.', reverse @fwd;
+ for my $q ( values %{ $self->{a_lookups}->{$query} || {} } ) {
+ $self->log(LOGDEBUG,"uribl: looking up $fwd (A record of $q->{name})");
+ $self->send_queries( $rev, { forward_ip => $fwd, zone => $q->{zone},
+ from_cb => 1 } );
+ }
+ }
foreach my $z (keys %$zones) {
if ($query =~ /^(.*)\.$z$/) {
my $name = $1;
@@ -85,7 +88,8 @@
}
sub process_txt_result {
- my ($class, $qp, $result, $query) = @_;
+ my ( $self, $qp, $result, $query ) = @_;
+ $self->{queries_completed}++;
my $transaction = $qp->transaction;
my $results = $transaction->notes('uribl_results');
@@ -99,6 +103,26 @@
}
}
+sub process_ns_result {
+ my ( $self, $qp, $result, $query ) = @_;
+ $self->{queries_completed}++;
+ if ( $result =~ /[^\d.]/ ) { # hostname
+ for my $q ( values %{ $self->{ns_lookups}->{$query} || {} } ) {
+ $self->log(LOGDEBUG,"uribl: looking up $result (NS record of $q->{name})");
+ $self->send_queries( $result, { zone => $q->{zone}, from_cb => 1 } );
+ }
+ } else { # IP address
+ my @fwd = split /\./, $result or next;
+ my $fwd = join '.', @fwd;
+ my $rev = join '.', reverse @fwd;
+ for my $q ( values %{ $self->{ns_lookups}->{$query} || {} } ) {
+ $self->log(LOGDEBUG,"uribl: looking up $fwd (NS record of $q->{name})");
+ $self->send_queries( $rev, { forward_ip => $fwd, zone => $q->{zone},
+ from_cb => 1 } );
+ }
+ }
+}
+
sub collect_results {
my ($self, $transaction) = @_;
@@ -108,12 +132,14 @@
foreach my $z (keys %$results) {
foreach my $n (keys %{$results->{$z}}) {
if (exists $results->{$z}->{$n}->{a}) {
- if ($self->evaluate($z, $results->{$z}->{$n}->{a})) {
+ my @lists = $self->evaluate($z, $results->{$z}->{$n}->{a}) or next;
$self->log(LOGDEBUG, "match $n in $z");
push @matches, {
- action => $self->{uribl_zones}->{$z}->{action},
- desc => "$n in $z: " .
+ zone => $z,
+ name => $n,
+ desc => "$n in $z" .
($results->{$z}->{$n}->{txt} || $results->{$z}->{$n}->{a}),
+ lists => \@lists,
};
}
}
Index: lib/Qpsmtpd/Plugin/Async/DNSBLBase.pm
===================================================================
--- lib/Qpsmtpd/Plugin/Async/DNSBLBase.pm (revision 12753)
+++ lib/Qpsmtpd/Plugin/Async/DNSBLBase.pm (working copy)
@@ -8,64 +8,43 @@
use ParaDNS;
sub lookup {
- my ($class, $qp, $A_lookups, $TXT_lookups) = @_;
+ my ($class, $qp, $A_lookups, $TXT_lookups, $NS_lookups) = @_;
- my $total_zones = @$A_lookups + @$TXT_lookups;
+ my $total_zones = @$A_lookups + @$TXT_lookups + @$NS_lookups;
- my ($A_pdns, $TXT_pdns);
-
- if (@$A_lookups) {
+ my $pdns;
+ for my $type ( qw( A TXT NS ) ) {
+ my $lookups = { A => $A_lookups,
+ TXT => $TXT_lookups,
+ NS => $NS_lookups }->{$type};
+ next unless @$lookups;
$qp->log(LOGDEBUG, "Checking ",
- join(", ", @$A_lookups),
- " for A record in the background");
-
- $A_pdns = ParaDNS->new(
+ join(", ", @$lookups),
+ " for $type record in the background");
+ $pdns->{$type} = ParaDNS->new(
callback => sub {
my ($result, $query) = @_;
- return if $result !~ /^\d+\.\d+\.\d+\.\d+$/;
- $qp->log(LOGDEBUG, "Result for A $query: $result");
- $class->process_a_result($qp, $result, $query);
+ return if $type eq 'A' and $result !~ /^\d+\.\d+\.\d+\.\d+$/;
+ return if $type eq 'TXT' and $result !~ /[a-z]/;
+ $qp->log(LOGDEBUG, "Result for $type $query: $result");
+ my $sub = lc "process_${type}_result";
+ $class->$sub($qp, $result, $query);
},
finished => sub {
- $total_zones -= @$A_lookups;
+ $total_zones -= @$lookups;
$class->finished($qp, $total_zones);
},
- hosts => [@$A_lookups],
- type => 'A',
+ hosts => [@$lookups],
+ type => $type,
client => $qp->input_sock,
- );
- return unless defined $A_pdns;
+ );
}
- if (@$TXT_lookups) {
- $qp->log(LOGDEBUG, "Checking ",
- join(", ", @$TXT_lookups),
- " for TXT record in the background");
-
- $TXT_pdns = ParaDNS->new(
- callback => sub {
- my ($result, $query) = @_;
- return if $result !~ /[a-z]/;
- $qp->log(LOGDEBUG, "Result for TXT $query: $result");
- $class->process_txt_result($qp, $result, $query);
- },
- finished => sub {
- $total_zones -= @$TXT_lookups;
- $class->finished($qp, $total_zones);
- },
- hosts => [@$TXT_lookups],
- type => 'TXT',
- client => $qp->input_sock,
- );
-
- unless (defined $TXT_pdns) {
- undef $A_pdns;
- return;
- }
- }
-
- return 1;
+ # Is this really necessary? The URIBL plugin, at least, can use A results even
+ # if TXT fails...
+ undef $pdns->{A} if ! $defined $pdns->{TXT};
+ return grep { defined } values %$pdns ? 1 : 0;
}
sub finished {
@@ -84,4 +63,8 @@
my ($class, $qp, $result, $query) = @_;
}
+sub process_ns_result {
+ my ($class, $qp, $result, $query) = @_;
+}
+
1;
gen_tld_lists
(application/octet-stream, 4.4 KB) - not displayed