/[Apache-SVN]/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm
ViewVC logotype

Contents of /spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 573117 - (hide annotations)
Thu Sep 6 00:27:12 2007 UTC (2 years, 2 months ago) by dos
File size: 20015 byte(s)
it's an array/list of hashes, not an array/list of arrays
1 quinlan 149177 # <@LICENSE>
2 felicity 431796 # Licensed to the Apache Software Foundation (ASF) under one or more
3     # contributor license agreements. See the NOTICE file distributed with
4     # this work for additional information regarding copyright ownership.
5     # The ASF licenses this file to you under the Apache License, Version 2.0
6     # (the "License"); you may not use this file except in compliance with
7     # the License. You may obtain a copy of the License at:
8 quinlan 149177 #
9     # http://www.apache.org/licenses/LICENSE-2.0
10     #
11     # Unless required by applicable law or agreed to in writing, software
12     # distributed under the License is distributed on an "AS IS" BASIS,
13     # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14     # See the License for the specific language governing permissions and
15     # limitations under the License.
16     # </@LICENSE>
17    
18 jm 6898 =head1 NAME
19    
20     URIDNSBL - look up URLs against DNS blocklists
21    
22 quinlan 149177 =head1 SYNOPSIS
23    
24     loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
25     uridnsbl URIBL_SBLXBL sbl-xbl.spamhaus.org. TXT
26    
27     =head1 DESCRIPTION
28    
29 jm 6898 This works by analysing message text and HTML for URLs, extracting the
30     domain names from those, querying their NS records in DNS, resolving
31     the hostnames used therein, and querying various DNS blocklists for
32     those IP addresses. This is quite effective.
33    
34 dos 527740 =head1 USER SETTINGS
35 jm 6898
36     =over 4
37    
38 dos 527740 =item uridnsbl_skip_domain domain1 domain2 ...
39    
40     Specify a domain, or a number of domains, which should be skipped for the
41     URIBL checks. This is very useful to specify very common domains which are
42     not going to be listed in URIBLs.
43    
44     =back
45    
46     =head1 RULE DEFINITIONS AND PRIVILEGED SETTINGS
47    
48     =over 4
49    
50 jm 9881 =item uridnsbl NAME_OF_RULE dnsbl_zone lookuptype
51 jm 6898
52     Specify a lookup. C<NAME_OF_RULE> is the name of the rule to be
53     used, C<dnsbl_zone> is the zone to look up IPs in, and C<lookuptype>
54     is the type of lookup (B<TXT> or B<A>). Note that you must also
55 felicity 54021 define a body-eval rule calling C<check_uridnsbl()> to use this.
56 jm 6898
57     Example:
58    
59     uridnsbl URIBL_SBLXBL sbl-xbl.spamhaus.org. TXT
60 felicity 54021 body URIBL_SBLXBL eval:check_uridnsbl('URIBL_SBLXBL')
61 jm 6898 describe URIBL_SBLXBL Contains a URL listed in the SBL/XBL blocklist
62    
63 jm 9881 =item urirhsbl NAME_OF_RULE rhsbl_zone lookuptype
64    
65     Specify a RHSBL-style domain lookup. C<NAME_OF_RULE> is the name of the rule
66     to be used, C<rhsbl_zone> is the zone to look up domain names in, and
67     C<lookuptype> is the type of lookup (B<TXT> or B<A>). Note that you must also
68 felicity 54021 define a body-eval rule calling C<check_uridnsbl()> to use this.
69 jm 9881
70     An RHSBL zone is one where the domain name is looked up, as a string; e.g. a
71 quinlan 160276 URI using the domain C<foo.com> will cause a lookup of
72     C<foo.com.uriblzone.net>. Note that hostnames are stripped from the domain
73     used in the URIBL lookup, so the domain C<foo.bar.com> will look up
74     C<bar.com.uriblzone.net>, and C<foo.bar.co.uk> will look up
75     C<bar.co.uk.uriblzone.net>.
76 jm 9881
77 mmartinec 565567 If an URI consists of an IP address instead of a hostname, the IP address is
78     looked up (using the standard reversed quads method) in each C<rhsbl_zone>.
79 quinlan 160276
80 jm 21406 Example:
81    
82     urirhsbl URIBL_RHSBL rhsbl.example.org. TXT
83    
84     =item urirhssub NAME_OF_RULE rhsbl_zone lookuptype subtest
85    
86     Specify a RHSBL-style domain lookup with a sub-test. C<NAME_OF_RULE> is the
87     name of the rule to be used, C<rhsbl_zone> is the zone to look up domain names
88     in, and C<lookuptype> is the type of lookup (B<TXT> or B<A>).
89    
90     C<subtest> is the sub-test to run against the returned data. The sub-test may
91 sidney 433917 either be an IPv4 dotted address for RHSBLs that return multiple A records or a
92 jm 21406 non-negative decimal number to specify a bitmask for RHSBLs that return a
93 sidney 433917 single A record containing a bitmask of results.
94 jm 21406
95 felicity 54021 Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
96 jm 21406 C<check_uridnsbl()> to use this.
97    
98     Example:
99    
100     urirhssub URIBL_RHSBL_4 rhsbl.example.org. A 127.0.0.4
101     urirhssub URIBL_RHSBL_8 rhsbl.example.org. A 8
102    
103 dos 527740 =back
104    
105     =head1 ADMINISTRATOR SETTINGS
106    
107     =over 4
108    
109 jm 6898 =item uridnsbl_max_domains N (default: 20)
110    
111     The maximum number of domains to look up.
112    
113     =back
114    
115 dos 527786 =head1 NOTES
116    
117     The C<uridnsbl_timeout> option has been obsoleted by the C<rbl_timeout>
118     option. See the C<Mail::SpamAssassin::Conf> POD for details on C<rbl_timeout>.
119    
120 jm 6898 =cut
121    
122     package Mail::SpamAssassin::Plugin::URIDNSBL;
123    
124     use Mail::SpamAssassin::Plugin;
125 quinlan 160273 use Mail::SpamAssassin::Constants qw(:ip);
126 quinlan 7001 use Mail::SpamAssassin::Util;
127 quinlan 162095 use Mail::SpamAssassin::Logger;
128 jm 6898 use strict;
129 quinlan 55260 use warnings;
130 jm 6898 use bytes;
131    
132     use vars qw(@ISA);
133     @ISA = qw(Mail::SpamAssassin::Plugin);
134    
135     use constant LOG_COMPLETION_TIMES => 0;
136    
137     # constructor
138     sub new {
139     my $class = shift;
140     my $samain = shift;
141    
142     # some boilerplate...
143     $class = ref($class) || $class;
144     my $self = $class->SUPER::new($samain);
145     bless ($self, $class);
146    
147 jm 161778 # this can be effectively global, at least in each process, safely
148 jm 6898
149 jm 161778 $self->{finished} = { };
150 jm 6898
151     $self->register_eval_rule ("check_uridnsbl");
152 felicity 56728 $self->set_config($samain->{conf});
153 jm 6898
154     return $self;
155     }
156    
157     # this is just a placeholder; in fact the results are dealt with later
158     sub check_uridnsbl {
159     return 0;
160     }
161    
162     # ---------------------------------------------------------------------------
163    
164     # once the metadata is parsed, we can access the URI list. So start off
165     # the lookups here!
166     sub parsed_metadata {
167     my ($self, $opts) = @_;
168     my $scanner = $opts->{permsgstatus};
169    
170 jm 161778 if (!$scanner->is_dns_available()) {
171 jm 6898 $self->{dns_not_available} = 1;
172     return;
173 dos 518664 } else {
174     # due to re-testing dns may become available after being unavailable
175     # DOS: I don't think dns_not_available is even used anymore
176     $self->{dns_not_available} = 0;
177 jm 6898 }
178    
179 felicity 439873 $scanner->{'uridnsbl_activerules'} = { };
180     $scanner->{'uridnsbl_hits'} = { };
181     $scanner->{'uridnsbl_seen_domain'} = { };
182 jm 6898
183 parker 9571 # only hit DNSBLs for active rules (defined and score != 0)
184 felicity 439873 $scanner->{'uridnsbl_active_rules_rhsbl'} = { };
185     $scanner->{'uridnsbl_active_rules_revipbl'} = { };
186    
187 jm 6898 foreach my $rulename (keys %{$scanner->{conf}->{uridnsbls}}) {
188 felicity 47438 next unless ($scanner->{conf}->is_rule_active('body_evals',$rulename));
189 jm 9881
190 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
191 jm 9881 if ($rulecf->{is_rhsbl}) {
192 felicity 439873 $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename} = 1;
193 jm 9881 } else {
194 felicity 439873 $scanner->{uridnsbl_active_rules_revipbl}->{$rulename} = 1;
195 jm 9881 }
196 jm 6898 }
197    
198     # get all domains in message
199 felicity 155151
200 felicity 170124 # don't keep dereferencing this
201     my $skip_domains = $scanner->{main}->{conf}->{uridnsbl_skip_domains};
202    
203 dos 573117 # list of hashes to use in order
204 mmartinec 571893 my @uri_ordered;
205 felicity 155151
206     # Generate the full list of html-parsed domains.
207 felicity 169564 my $uris = $scanner->get_uri_detail_list();
208 felicity 155151
209 felicity 157209 # go from uri => info to uri_ordered
210     # 0: a
211     # 1: form
212     # 2: img
213     # 3: !a_empty
214     # 4: parsed
215     # 5: a_empty
216 felicity 169564 while (my($uri, $info) = each %{$uris}) {
217 felicity 170124 # we want to skip mailto: uris
218     next if ($uri =~ /^mailto:/);
219    
220     # no domains were found via this uri, so skip
221     next unless ($info->{domains});
222    
223 felicity 157209 my $entry = 3;
224    
225     if ($info->{types}->{a}) {
226     $entry = 5;
227    
228     # determine a vs a_empty
229     foreach my $at (@{$info->{anchor_text}}) {
230     if (length $at) {
231     $entry = 0;
232     last;
233     }
234     }
235 felicity 155151 }
236 felicity 157209 elsif ($info->{types}->{form}) {
237     $entry = 1;
238     }
239     elsif ($info->{types}->{img}) {
240     $entry = 2;
241     }
242 felicity 169570 elsif ($info->{types}->{parsed} && (keys %{$info->{types}} == 1)) {
243 felicity 169564 $entry = 4;
244     }
245 felicity 155151
246 felicity 170124 # take the usable domains and add to the ordered list
247     foreach ( keys %{ $info->{domains} } ) {
248     if (exists $skip_domains->{$_}) {
249     dbg("uridnsbl: domain $_ in skip list");
250     next;
251     }
252     $uri_ordered[$entry]->{$_} = 1;
253     }
254 felicity 155151 }
255    
256 felicity 170124 # at this point, @uri_ordered is an ordered array of uri hashes
257 felicity 155151
258 mmartinec 571893 my %domlist;
259 felicity 439873 my $umd = $scanner->{main}->{conf}->{uridnsbl_max_domains};
260     while (keys %domlist < $umd && @uri_ordered) {
261 felicity 155151 my $array = shift @uri_ordered;
262 felicity 157209 next unless $array;
263    
264 felicity 170124 # run through and find the new domains in this grouping
265     my @domains = grep(!$domlist{$_}, keys %{$array});
266     next unless @domains;
267 felicity 155151
268     # the new domains are all useful, just add them in
269 felicity 439873 if (keys(%domlist) + @domains <= $umd) {
270 felicity 170124 foreach (@domains) {
271 felicity 155151 $domlist{$_} = 1;
272 felicity 47380 }
273 felicity 155151 }
274     else {
275     # trim down to a limited number - pick randomly
276     my $i;
277 felicity 439873 while (@domains && keys %domlist < $umd) {
278 felicity 170124 my $r = int rand (scalar @domains);
279     $domlist{splice (@domains, $r, 1)} = 1;
280 felicity 47380 }
281     }
282 jm 6898 }
283    
284     # and query
285 felicity 155151 dbg("uridnsbl: domains to query: ".join(' ',keys %domlist));
286     foreach my $dom (keys %domlist) {
287 felicity 439873 $self->query_domain ($scanner, $dom);
288 jm 6898 }
289    
290     return 1;
291     }
292    
293 felicity 56728 sub set_config {
294     my($self, $conf) = @_;
295 mmartinec 571893 my @cmds;
296 jm 6898
297 felicity 56728 push(@cmds, {
298     setting => 'uridnsbl_max_domains',
299 dos 527740 is_admin => 1,
300 felicity 56728 default => 20,
301     type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
302     });
303 jm 6898
304 felicity 56728 push (@cmds, {
305     setting => 'uridnsbl',
306 dos 527740 is_priv => 1,
307 felicity 56728 code => sub {
308     my ($self, $key, $value, $line) = @_;
309     if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
310     my $rulename = $1;
311     my $zone = $2;
312     my $type = $3;
313     $self->{uridnsbls}->{$rulename} = {
314     zone => $zone, type => $type,
315     is_rhsbl => 0
316     };
317     }
318 dos 178359 elsif ($value =~ /^$/) {
319     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
320     }
321     else {
322     return $Mail::SpamAssassin::Conf::INVALID_VALUE;
323     }
324 jm 6898 }
325 felicity 56728 });
326 felicity 55981
327 felicity 56728 push (@cmds, {
328     setting => 'urirhsbl',
329 dos 527740 is_priv => 1,
330 felicity 56728 code => sub {
331     my ($self, $key, $value, $line) = @_;
332     if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
333     my $rulename = $1;
334     my $zone = $2;
335     my $type = $3;
336     $self->{uridnsbls}->{$rulename} = {
337     zone => $zone, type => $type,
338     is_rhsbl => 1
339     };
340     }
341 dos 178359 elsif ($value =~ /^$/) {
342     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
343     }
344     else {
345     return $Mail::SpamAssassin::Conf::INVALID_VALUE;
346     }
347 jm 9881 }
348 felicity 56728 });
349 felicity 55981
350 felicity 56728 push (@cmds, {
351     setting => 'urirhssub',
352 dos 527740 is_priv => 1,
353 felicity 56728 code => sub {
354     my ($self, $key, $value, $line) = @_;
355 sidney 433917 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\d{1,10}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
356 felicity 56728 my $rulename = $1;
357     my $zone = $2;
358     my $type = $3;
359     my $subrule = $4;
360     $self->{uridnsbls}->{$rulename} = {
361     zone => $zone, type => $type,
362     is_rhsbl => 1, is_subrule => 1
363     };
364     $self->{uridnsbl_subs}->{$zone} ||= { };
365 dos 543724 push (@{$self->{uridnsbl_subs}->{$zone}->{$subrule}->{rulenames}}, $rulename);
366 felicity 56728 }
367 dos 178359 elsif ($value =~ /^$/) {
368     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
369     }
370     else {
371     return $Mail::SpamAssassin::Conf::INVALID_VALUE;
372     }
373 jm 21406 }
374 felicity 56728 });
375 felicity 55981
376 felicity 56728 push (@cmds, {
377     setting => 'uridnsbl_skip_domain',
378     default => {},
379     code => sub {
380     my ($self, $key, $value, $line) = @_;
381 dos 178359 if ($value =~ /^$/) {
382     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
383     }
384 felicity 56728 foreach my $domain (split(/\s+/, $value)) {
385     $self->{uridnsbl_skip_domains}->{lc $domain} = 1;
386     }
387 felicity 47380 }
388 felicity 56728 });
389 felicity 55981
390 jm 527859 # obsolete
391     push(@cmds, {
392     setting => 'uridnsbl_timeout',
393     code => sub {
394     # not a lint_warn(), since it's pretty harmless and we don't want
395     # to break stuff like sa-update
396     warn("config: 'uridnsbl_timeout' is obsolete, use 'rbl_timeout' instead");
397     return 0;
398     }
399     });
400    
401 felicity 56728 $conf->{parser}->register_commands(\@cmds);
402 jm 6898 }
403    
404     # ---------------------------------------------------------------------------
405    
406     sub query_domain {
407 felicity 439873 my ($self, $scanner, $dom) = @_;
408 jm 6898
409 quinlan 51813 #warn "uridnsbl: domain $dom\n";
410     #return;
411 jm 6898
412     $dom = lc $dom;
413 felicity 439873 return if $scanner->{uridnsbl_seen_domain}->{$dom};
414     $scanner->{uridnsbl_seen_domain}->{$dom} = 1;
415 quinlan 160273 $self->log_dns_result("querying domain $dom");
416 jm 6898
417 mmartinec 567684 my $obj = { dom => $dom };
418 jm 6898
419 quinlan 160273 my $single_dnsbl = 0;
420 jm 394351 if ($dom =~ /^\d+\.\d+\.\d+\.\d+$/) {
421 quinlan 160273 my $IPV4_ADDRESS = IPV4_ADDRESS;
422     my $IP_PRIVATE = IP_PRIVATE;
423     # only look up the IP if it is public and valid
424     if ($dom =~ /^$IPV4_ADDRESS$/ && $dom !~ /^$IP_PRIVATE$/) {
425 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $obj, $dom);
426 quinlan 160273 # and check the IP in RHSBLs too
427     if ($dom =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
428     $dom = "$4.$3.$2.$1";
429     $single_dnsbl = 1;
430     }
431     }
432 jm 9881 }
433 felicity 47380 else {
434 quinlan 160273 $single_dnsbl = 1;
435     }
436    
437     if ($single_dnsbl) {
438 jm 9881 # look up the domain in the RHSBL subset
439 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_rhsbl};
440 jm 9881 foreach my $rulename (keys %{$cf}) {
441 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
442     $self->lookup_single_dnsbl($scanner, $obj, $rulename,
443 quinlan 160273 $dom, $rulecf->{zone}, $rulecf->{type});
444 jm 394351
445     # see comment below
446 felicity 439873 $scanner->register_async_rule_start($rulename);
447 jm 9881 }
448    
449     # perform NS, A lookups to look up the domain in the non-RHSBL subset
450 quinlan 160273 if ($dom !~ /^\d+\.\d+\.\d+\.\d+$/) {
451 felicity 439873 $self->lookup_domain_ns($scanner, $obj, $dom);
452 quinlan 160273 }
453 jm 6898 }
454 jm 394351
455     # note that these rules are now underway. important: unless the
456     # rule hits, in the current design, these will not be considered
457     # "finished" until harvest_dnsbl_queries() completes
458 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_revipbl};
459 jm 394351 foreach my $rulename (keys %{$cf}) {
460 felicity 439873 $scanner->register_async_rule_start($rulename);
461 jm 394351 }
462 jm 6898 }
463    
464     # ---------------------------------------------------------------------------
465    
466     sub lookup_domain_ns {
467 felicity 439873 my ($self, $scanner, $obj, $dom) = @_;
468 jm 6898
469     my $key = "NS:".$dom;
470 felicity 439873 return if $scanner->{async}->get_lookup($key);
471 jm 6898
472     # dig $dom ns
473 mmartinec 564636 my $ent = $self->start_lookup($scanner, 'NS',
474     $self->res_bgsend($scanner, $dom, 'NS', $key),
475     $key);
476 jm 6898 $ent->{obj} = $obj;
477     }
478    
479     sub complete_ns_lookup {
480 felicity 439873 my ($self, $scanner, $ent, $dom) = @_;
481 jm 7002
482 jm 161157 my $packet = $ent->{response_packet};
483 jm 6898 my @answer = $packet->answer;
484    
485 quinlan 160273 my $IPV4_ADDRESS = IPV4_ADDRESS;
486     my $IP_PRIVATE = IP_PRIVATE;
487    
488 jm 6898 foreach my $rr (@answer) {
489     my $str = $rr->string;
490     next unless (defined($str) && defined($dom));
491     $self->log_dns_result ("NSs for $dom: $str");
492    
493     if ($str =~ /IN\s+NS\s+(\S+)/) {
494 quinlan 160273 my $nsmatch = $1;
495    
496     if ($nsmatch =~ /^\d+\.\d+\.\d+\.\d+\.?$/) {
497     $nsmatch =~ s/\.$//;
498     # only look up the IP if it is public and valid
499     if ($nsmatch =~ /^$IPV4_ADDRESS$/ && $nsmatch !~ /^$IP_PRIVATE$/) {
500 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $nsmatch);
501 quinlan 160273 }
502     }
503     else {
504 felicity 439873 $self->lookup_a_record($scanner, $ent->{obj}, $nsmatch);
505 quinlan 160273 }
506 jm 6898 }
507     }
508     }
509    
510     # ---------------------------------------------------------------------------
511    
512     sub lookup_a_record {
513 felicity 439873 my ($self, $scanner, $obj, $hname) = @_;
514 jm 6898
515     my $key = "A:".$hname;
516 felicity 439873 return if $scanner->{async}->get_lookup($key);
517 jm 6898
518     # dig $hname a
519 mmartinec 564636 my $ent = $self->start_lookup($scanner, 'A',
520     $self->res_bgsend($scanner, $hname, 'A', $key),
521     $key);
522 jm 6898 $ent->{obj} = $obj;
523     }
524    
525     sub complete_a_lookup {
526 felicity 439873 my ($self, $scanner, $ent, $hname) = @_;
527 jm 6898
528 jm 161778 foreach my $rr ($ent->{response_packet}->answer) {
529 jm 6898 my $str = $rr->string;
530     $self->log_dns_result ("A for NS $hname: $str");
531    
532     if ($str =~ /IN\s+A\s+(\S+)/) {
533 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $1);
534 jm 6898 }
535     }
536     }
537    
538     # ---------------------------------------------------------------------------
539    
540     sub lookup_dnsbl_for_ip {
541 felicity 439873 my ($self, $scanner, $obj, $ip) = @_;
542 jm 6898
543     $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
544     my $revip = "$4.$3.$2.$1";
545    
546 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_revipbl};
547 jm 6898 foreach my $rulename (keys %{$cf}) {
548 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
549     $self->lookup_single_dnsbl($scanner, $obj, $rulename,
550 quinlan 160273 $revip, $rulecf->{zone}, $rulecf->{type});
551 jm 6898 }
552     }
553    
554     sub lookup_single_dnsbl {
555 felicity 439873 my ($self, $scanner, $obj, $rulename, $lookupstr, $dnsbl, $qtype) = @_;
556 jm 6898
557 jm 9881 my $key = "DNSBL:".$dnsbl.":".$lookupstr;
558 felicity 439873 return if $scanner->{async}->get_lookup($key);
559 jm 9881 my $item = $lookupstr.".".$dnsbl;
560 jm 6898
561     # dig $ip txt
562 mmartinec 564636 my $ent = $self->start_lookup($scanner, 'DNSBL',
563     $self->res_bgsend($scanner, $item, $qtype, $key),
564     $key);
565 jm 6898 $ent->{obj} = $obj;
566     $ent->{rulename} = $rulename;
567 jm 21406 $ent->{zone} = $dnsbl;
568 jm 6898 }
569    
570     sub complete_dnsbl_lookup {
571 felicity 439873 my ($self, $scanner, $ent, $dnsblip) = @_;
572 jm 6898
573 felicity 439873 my $conf = $scanner->{conf};
574 mmartinec 571893 my @subtests;
575 jm 21406 my $rulename = $ent->{rulename};
576     my $rulecf = $conf->{uridnsbls}->{$rulename};
577    
578 jm 161157 my $packet = $ent->{response_packet};
579 jm 6898 my @answer = $packet->answer;
580 jm 161157
581 quinlan 125719 my $uridnsbl_subs = $conf->{uridnsbl_subs}->{$ent->{zone}};
582 jm 21406 foreach my $rr (@answer)
583     {
584     next if ($rr->type ne 'A' && $rr->type ne 'TXT');
585    
586     my $rdatastr = $rr->rdatastr;
587 jm 6898 my $dom = $ent->{obj}->{dom};
588    
589 jm 21406 if (!$rulecf->{is_subrule}) {
590     # this zone is a simple rule, not a set of subrules
591 quinlan 125719 # skip any A record that isn't on 127/8
592 jm 158887 if ($rr->type eq 'A' && $rr->rdatastr !~ /^127\./) {
593 jm 165017 warn("uridnsbl: bogus rr for domain=$dom, rule=$rulename, id=" .
594     $packet->header->id." rr=".$rr->string);
595 quinlan 158898 next;
596 jm 158887 }
597 felicity 439873 $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $rulename);
598 jm 21406 }
599     else {
600     foreach my $subtest (keys (%{$uridnsbl_subs}))
601     {
602     if ($subtest eq $rdatastr) {
603 dos 543724 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
604     $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
605     }
606 jm 21406 }
607     # bitmask
608     elsif ($subtest =~ /^\d+$/) {
609 sidney 433917 if ($rdatastr =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ &&
610     Mail::SpamAssassin::Util::my_inet_aton($rdatastr) & $subtest)
611 jm 21406 {
612 dos 543724 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
613     $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
614     }
615 jm 21406 }
616     }
617     }
618     }
619 jm 6898 }
620     }
621    
622 jm 21406 sub got_dnsbl_hit {
623 felicity 439873 my ($self, $scanner, $ent, $str, $dom, $rulename) = @_;
624 jm 21406
625     $str =~ s/\s+/ /gs; # long whitespace => short
626 quinlan 51813 dbg("uridnsbl: domain \"$dom\" listed ($rulename): $str");
627 jm 21406
628 felicity 439873 if (!defined $scanner->{uridnsbl_hits}->{$rulename}) {
629     $scanner->{uridnsbl_hits}->{$rulename} = { };
630 jm 21406 };
631 felicity 439873 $scanner->{uridnsbl_hits}->{$rulename}->{$dom} = 1;
632 jm 394348
633 felicity 439873 if ($scanner->{uridnsbl_active_rules_revipbl}->{$rulename}
634     || $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename})
635 jm 394348 {
636     # TODO: this needs to handle multiple domain hits per rule
637 felicity 439873 $scanner->clear_test_state();
638     my $uris = join (' ', keys %{$scanner->{uridnsbl_hits}->{$rulename}});
639     $scanner->test_log ("URIs: $uris");
640     $scanner->got_hit ($rulename, "");
641 jm 394351
642     # note that this rule has completed (since it got at least 1 hit)
643 felicity 439873 $scanner->register_async_rule_finish($rulename);
644 jm 394348 }
645 jm 21406 }
646    
647 jm 6898 # ---------------------------------------------------------------------------
648    
649     sub start_lookup {
650 felicity 439873 my ($self, $scanner, $type, $id, $key) = @_;
651 jm 394348
652 jm 6898 my $ent = {
653 jm 394348 key => $key,
654 mmartinec 564636 timeout => $scanner->{conf}->{rbl_timeout},
655 jm 394348 type => "URI-".$type,
656     id => $id,
657     completed_callback => sub {
658     my $ent = shift;
659 felicity 439873 $self->completed_lookup_callback ($scanner, $ent);
660 jm 394348 }
661 jm 6898 };
662 felicity 439873 $scanner->{async}->start_lookup($ent);
663 jm 394348 return $ent;
664 jm 6898 }
665    
666 jm 394348 sub completed_lookup_callback {
667 felicity 439873 my ($self, $scanner, $ent) = @_;
668 jm 394348 my $type = $ent->{type};
669     my $key = $ent->{key};
670     $key =~ /:(\S+?)$/; my $val = $1;
671 jm 6898
672 jm 394348 if ($type eq 'URI-NS') {
673 felicity 439873 $self->complete_ns_lookup ($scanner, $ent, $val);
674 jm 6898 }
675 jm 394348 elsif ($type eq 'URI-A') {
676 felicity 439873 $self->complete_a_lookup ($scanner, $ent, $val);
677 jm 6898 }
678 jm 394348 elsif ($type eq 'URI-DNSBL') {
679 felicity 439873 $self->complete_dnsbl_lookup ($scanner, $ent, $val);
680 jm 6898 }
681     }
682    
683     # ---------------------------------------------------------------------------
684    
685 jm 161148 sub res_bgsend {
686 mmartinec 564636 my ($self, $scanner, $host, $type, $key) = @_;
687 jm 161778
688     return $self->{main}->{resolver}->bgsend($host, $type, undef, sub {
689 mmartinec 564636 my ($pkt, $id, $timestamp) = @_;
690     $scanner->{async}->set_response_packet($id, $pkt, $key, $timestamp);
691 jm 161778 });
692 jm 161148 }
693    
694 jm 6898 sub log_dns_result {
695 felicity 57557 #my $self = shift;
696 quinlan 51813 #Mail::SpamAssassin::dbg("uridnsbl: ".join (' ', @_));
697 jm 6898 }
698    
699     # ---------------------------------------------------------------------------
700    
701     1;

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2