/[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 596095 - (hide annotations)
Sun Nov 18 16:11:20 2007 UTC (2 years ago) by jm
File size: 20394 byte(s)
bug 5711: allow 'mass-check --reuse' without '--net' to reuse net-rule hits, and output mass-check results for scoreset 1; while lines that are not reusable use set 0.  Also, fix a few tests to use 'tflags net' if they use network lookups (including calls to lookup_ptr().)  Fix nightly mass-checks on the zone to use --reuse to gain this.
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 mmartinec 574664 use re 'taint';
132 jm 6898
133     use vars qw(@ISA);
134     @ISA = qw(Mail::SpamAssassin::Plugin);
135    
136     use constant LOG_COMPLETION_TIMES => 0;
137    
138     # constructor
139     sub new {
140     my $class = shift;
141     my $samain = shift;
142    
143     # some boilerplate...
144     $class = ref($class) || $class;
145     my $self = $class->SUPER::new($samain);
146     bless ($self, $class);
147    
148 jm 161778 # this can be effectively global, at least in each process, safely
149 jm 6898
150 jm 161778 $self->{finished} = { };
151 jm 6898
152     $self->register_eval_rule ("check_uridnsbl");
153 felicity 56728 $self->set_config($samain->{conf});
154 jm 6898
155     return $self;
156     }
157    
158     # this is just a placeholder; in fact the results are dealt with later
159     sub check_uridnsbl {
160     return 0;
161     }
162    
163     # ---------------------------------------------------------------------------
164    
165     # once the metadata is parsed, we can access the URI list. So start off
166     # the lookups here!
167     sub parsed_metadata {
168     my ($self, $opts) = @_;
169     my $scanner = $opts->{permsgstatus};
170    
171 jm 161778 if (!$scanner->is_dns_available()) {
172 jm 6898 $self->{dns_not_available} = 1;
173     return;
174 dos 518664 } else {
175     # due to re-testing dns may become available after being unavailable
176     # DOS: I don't think dns_not_available is even used anymore
177     $self->{dns_not_available} = 0;
178 jm 6898 }
179    
180 felicity 439873 $scanner->{'uridnsbl_activerules'} = { };
181     $scanner->{'uridnsbl_hits'} = { };
182     $scanner->{'uridnsbl_seen_domain'} = { };
183 jm 6898
184 parker 9571 # only hit DNSBLs for active rules (defined and score != 0)
185 felicity 439873 $scanner->{'uridnsbl_active_rules_rhsbl'} = { };
186     $scanner->{'uridnsbl_active_rules_revipbl'} = { };
187    
188 jm 6898 foreach my $rulename (keys %{$scanner->{conf}->{uridnsbls}}) {
189 felicity 47438 next unless ($scanner->{conf}->is_rule_active('body_evals',$rulename));
190 jm 9881
191 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
192 jm 9881 if ($rulecf->{is_rhsbl}) {
193 felicity 439873 $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename} = 1;
194 jm 9881 } else {
195 felicity 439873 $scanner->{uridnsbl_active_rules_revipbl}->{$rulename} = 1;
196 jm 9881 }
197 jm 6898 }
198    
199     # get all domains in message
200 felicity 155151
201 felicity 170124 # don't keep dereferencing this
202     my $skip_domains = $scanner->{main}->{conf}->{uridnsbl_skip_domains};
203    
204 dos 573117 # list of hashes to use in order
205 mmartinec 571893 my @uri_ordered;
206 felicity 155151
207     # Generate the full list of html-parsed domains.
208 felicity 169564 my $uris = $scanner->get_uri_detail_list();
209 felicity 155151
210 felicity 157209 # go from uri => info to uri_ordered
211     # 0: a
212     # 1: form
213     # 2: img
214     # 3: !a_empty
215     # 4: parsed
216     # 5: a_empty
217 felicity 169564 while (my($uri, $info) = each %{$uris}) {
218 felicity 170124 # we want to skip mailto: uris
219     next if ($uri =~ /^mailto:/);
220    
221     # no domains were found via this uri, so skip
222     next unless ($info->{domains});
223    
224 felicity 157209 my $entry = 3;
225    
226     if ($info->{types}->{a}) {
227     $entry = 5;
228    
229     # determine a vs a_empty
230     foreach my $at (@{$info->{anchor_text}}) {
231     if (length $at) {
232     $entry = 0;
233     last;
234     }
235     }
236 felicity 155151 }
237 felicity 157209 elsif ($info->{types}->{form}) {
238     $entry = 1;
239     }
240     elsif ($info->{types}->{img}) {
241     $entry = 2;
242     }
243 felicity 169570 elsif ($info->{types}->{parsed} && (keys %{$info->{types}} == 1)) {
244 felicity 169564 $entry = 4;
245     }
246 felicity 155151
247 felicity 170124 # take the usable domains and add to the ordered list
248     foreach ( keys %{ $info->{domains} } ) {
249     if (exists $skip_domains->{$_}) {
250     dbg("uridnsbl: domain $_ in skip list");
251     next;
252     }
253     $uri_ordered[$entry]->{$_} = 1;
254     }
255 felicity 155151 }
256    
257 felicity 170124 # at this point, @uri_ordered is an ordered array of uri hashes
258 felicity 155151
259 mmartinec 571893 my %domlist;
260 felicity 439873 my $umd = $scanner->{main}->{conf}->{uridnsbl_max_domains};
261     while (keys %domlist < $umd && @uri_ordered) {
262 felicity 155151 my $array = shift @uri_ordered;
263 felicity 157209 next unless $array;
264    
265 felicity 170124 # run through and find the new domains in this grouping
266     my @domains = grep(!$domlist{$_}, keys %{$array});
267     next unless @domains;
268 felicity 155151
269     # the new domains are all useful, just add them in
270 felicity 439873 if (keys(%domlist) + @domains <= $umd) {
271 felicity 170124 foreach (@domains) {
272 felicity 155151 $domlist{$_} = 1;
273 felicity 47380 }
274 felicity 155151 }
275     else {
276     # trim down to a limited number - pick randomly
277     my $i;
278 felicity 439873 while (@domains && keys %domlist < $umd) {
279 felicity 170124 my $r = int rand (scalar @domains);
280     $domlist{splice (@domains, $r, 1)} = 1;
281 felicity 47380 }
282     }
283 jm 6898 }
284    
285     # and query
286 felicity 155151 dbg("uridnsbl: domains to query: ".join(' ',keys %domlist));
287     foreach my $dom (keys %domlist) {
288 felicity 439873 $self->query_domain ($scanner, $dom);
289 jm 6898 }
290    
291     return 1;
292     }
293    
294 felicity 56728 sub set_config {
295     my($self, $conf) = @_;
296 mmartinec 571893 my @cmds;
297 jm 6898
298 felicity 56728 push(@cmds, {
299     setting => 'uridnsbl_max_domains',
300 dos 527740 is_admin => 1,
301 felicity 56728 default => 20,
302     type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
303     });
304 jm 6898
305 felicity 56728 push (@cmds, {
306     setting => 'uridnsbl',
307 dos 527740 is_priv => 1,
308 felicity 56728 code => sub {
309     my ($self, $key, $value, $line) = @_;
310     if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
311     my $rulename = $1;
312     my $zone = $2;
313     my $type = $3;
314     $self->{uridnsbls}->{$rulename} = {
315     zone => $zone, type => $type,
316     is_rhsbl => 0
317     };
318     }
319 dos 178359 elsif ($value =~ /^$/) {
320     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
321     }
322     else {
323     return $Mail::SpamAssassin::Conf::INVALID_VALUE;
324     }
325 jm 6898 }
326 felicity 56728 });
327 felicity 55981
328 felicity 56728 push (@cmds, {
329     setting => 'urirhsbl',
330 dos 527740 is_priv => 1,
331 felicity 56728 code => sub {
332     my ($self, $key, $value, $line) = @_;
333     if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)$/) {
334     my $rulename = $1;
335     my $zone = $2;
336     my $type = $3;
337     $self->{uridnsbls}->{$rulename} = {
338     zone => $zone, type => $type,
339     is_rhsbl => 1
340     };
341     }
342 dos 178359 elsif ($value =~ /^$/) {
343     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
344     }
345     else {
346     return $Mail::SpamAssassin::Conf::INVALID_VALUE;
347     }
348 jm 9881 }
349 felicity 56728 });
350 felicity 55981
351 felicity 56728 push (@cmds, {
352     setting => 'urirhssub',
353 dos 527740 is_priv => 1,
354 felicity 56728 code => sub {
355     my ($self, $key, $value, $line) = @_;
356 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})$/) {
357 felicity 56728 my $rulename = $1;
358     my $zone = $2;
359     my $type = $3;
360     my $subrule = $4;
361     $self->{uridnsbls}->{$rulename} = {
362     zone => $zone, type => $type,
363     is_rhsbl => 1, is_subrule => 1
364     };
365     $self->{uridnsbl_subs}->{$zone} ||= { };
366 dos 543724 push (@{$self->{uridnsbl_subs}->{$zone}->{$subrule}->{rulenames}}, $rulename);
367 felicity 56728 }
368 dos 178359 elsif ($value =~ /^$/) {
369     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
370     }
371     else {
372     return $Mail::SpamAssassin::Conf::INVALID_VALUE;
373     }
374 jm 21406 }
375 felicity 56728 });
376 felicity 55981
377 felicity 56728 push (@cmds, {
378     setting => 'uridnsbl_skip_domain',
379     default => {},
380     code => sub {
381     my ($self, $key, $value, $line) = @_;
382 dos 178359 if ($value =~ /^$/) {
383     return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
384     }
385 felicity 56728 foreach my $domain (split(/\s+/, $value)) {
386     $self->{uridnsbl_skip_domains}->{lc $domain} = 1;
387     }
388 felicity 47380 }
389 felicity 56728 });
390 felicity 55981
391 jm 527859 # obsolete
392     push(@cmds, {
393     setting => 'uridnsbl_timeout',
394     code => sub {
395     # not a lint_warn(), since it's pretty harmless and we don't want
396     # to break stuff like sa-update
397     warn("config: 'uridnsbl_timeout' is obsolete, use 'rbl_timeout' instead");
398     return 0;
399     }
400     });
401    
402 felicity 56728 $conf->{parser}->register_commands(\@cmds);
403 jm 6898 }
404    
405     # ---------------------------------------------------------------------------
406    
407     sub query_domain {
408 felicity 439873 my ($self, $scanner, $dom) = @_;
409 jm 6898
410 quinlan 51813 #warn "uridnsbl: domain $dom\n";
411     #return;
412 jm 6898
413     $dom = lc $dom;
414 felicity 439873 return if $scanner->{uridnsbl_seen_domain}->{$dom};
415     $scanner->{uridnsbl_seen_domain}->{$dom} = 1;
416 quinlan 160273 $self->log_dns_result("querying domain $dom");
417 jm 6898
418 mmartinec 567684 my $obj = { dom => $dom };
419 jm 6898
420 quinlan 160273 my $single_dnsbl = 0;
421 jm 394351 if ($dom =~ /^\d+\.\d+\.\d+\.\d+$/) {
422 quinlan 160273 my $IPV4_ADDRESS = IPV4_ADDRESS;
423     my $IP_PRIVATE = IP_PRIVATE;
424     # only look up the IP if it is public and valid
425     if ($dom =~ /^$IPV4_ADDRESS$/ && $dom !~ /^$IP_PRIVATE$/) {
426 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $obj, $dom);
427 quinlan 160273 # and check the IP in RHSBLs too
428     if ($dom =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
429     $dom = "$4.$3.$2.$1";
430     $single_dnsbl = 1;
431     }
432     }
433 jm 9881 }
434 felicity 47380 else {
435 quinlan 160273 $single_dnsbl = 1;
436     }
437    
438 jm 596095 my $rhsblrules = $scanner->{uridnsbl_active_rules_rhsbl};
439     my $reviprules = $scanner->{uridnsbl_active_rules_revipbl};
440    
441 quinlan 160273 if ($single_dnsbl) {
442 jm 9881 # look up the domain in the RHSBL subset
443 jm 596095 foreach my $rulename (keys %{$rhsblrules}) {
444 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
445     $self->lookup_single_dnsbl($scanner, $obj, $rulename,
446 quinlan 160273 $dom, $rulecf->{zone}, $rulecf->{type});
447 jm 394351
448     # see comment below
449 felicity 439873 $scanner->register_async_rule_start($rulename);
450 jm 9881 }
451    
452 jm 596095 # perform NS, A lookups to look up the domain in the non-RHSBL subset,
453     # but only if there are active reverse-IP-URIBL rules
454     if ($dom !~ /^\d+\.\d+\.\d+\.\d+$/ && (scalar keys %{$reviprules})) {
455 felicity 439873 $self->lookup_domain_ns($scanner, $obj, $dom);
456 quinlan 160273 }
457 jm 6898 }
458 jm 394351
459     # note that these rules are now underway. important: unless the
460     # rule hits, in the current design, these will not be considered
461     # "finished" until harvest_dnsbl_queries() completes
462 jm 596095 foreach my $rulename (keys %{$reviprules}) {
463 felicity 439873 $scanner->register_async_rule_start($rulename);
464 jm 394351 }
465 jm 6898 }
466    
467     # ---------------------------------------------------------------------------
468    
469     sub lookup_domain_ns {
470 felicity 439873 my ($self, $scanner, $obj, $dom) = @_;
471 jm 6898
472     my $key = "NS:".$dom;
473 felicity 439873 return if $scanner->{async}->get_lookup($key);
474 jm 6898
475     # dig $dom ns
476 mmartinec 585292 my $ent = $self->start_lookup($scanner, $dom, 'NS',
477 mmartinec 564636 $self->res_bgsend($scanner, $dom, 'NS', $key),
478     $key);
479 jm 6898 $ent->{obj} = $obj;
480     }
481    
482     sub complete_ns_lookup {
483 felicity 439873 my ($self, $scanner, $ent, $dom) = @_;
484 jm 7002
485 jm 161157 my $packet = $ent->{response_packet};
486 mmartinec 582903 my @answer = !defined $packet ? () : $packet->answer;
487 jm 6898
488 quinlan 160273 my $IPV4_ADDRESS = IPV4_ADDRESS;
489     my $IP_PRIVATE = IP_PRIVATE;
490    
491 jm 6898 foreach my $rr (@answer) {
492     my $str = $rr->string;
493     next unless (defined($str) && defined($dom));
494     $self->log_dns_result ("NSs for $dom: $str");
495    
496     if ($str =~ /IN\s+NS\s+(\S+)/) {
497 quinlan 160273 my $nsmatch = $1;
498    
499     if ($nsmatch =~ /^\d+\.\d+\.\d+\.\d+\.?$/) {
500     $nsmatch =~ s/\.$//;
501     # only look up the IP if it is public and valid
502     if ($nsmatch =~ /^$IPV4_ADDRESS$/ && $nsmatch !~ /^$IP_PRIVATE$/) {
503 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $nsmatch);
504 quinlan 160273 }
505     }
506     else {
507 felicity 439873 $self->lookup_a_record($scanner, $ent->{obj}, $nsmatch);
508 quinlan 160273 }
509 jm 6898 }
510     }
511     }
512    
513     # ---------------------------------------------------------------------------
514    
515     sub lookup_a_record {
516 felicity 439873 my ($self, $scanner, $obj, $hname) = @_;
517 jm 6898
518     my $key = "A:".$hname;
519 felicity 439873 return if $scanner->{async}->get_lookup($key);
520 jm 6898
521     # dig $hname a
522 mmartinec 585292 my $ent = $self->start_lookup($scanner, $hname, 'A',
523 mmartinec 564636 $self->res_bgsend($scanner, $hname, 'A', $key),
524     $key);
525 jm 6898 $ent->{obj} = $obj;
526     }
527    
528     sub complete_a_lookup {
529 felicity 439873 my ($self, $scanner, $ent, $hname) = @_;
530 jm 6898
531 mmartinec 582903 my $packet = $ent->{response_packet};
532     my @answer = !defined $packet ? () : $packet->answer;
533     foreach my $rr (@answer) {
534 jm 6898 my $str = $rr->string;
535     $self->log_dns_result ("A for NS $hname: $str");
536    
537     if ($str =~ /IN\s+A\s+(\S+)/) {
538 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $1);
539 jm 6898 }
540     }
541     }
542    
543     # ---------------------------------------------------------------------------
544    
545     sub lookup_dnsbl_for_ip {
546 felicity 439873 my ($self, $scanner, $obj, $ip) = @_;
547 jm 6898
548     $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
549     my $revip = "$4.$3.$2.$1";
550    
551 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_revipbl};
552 jm 6898 foreach my $rulename (keys %{$cf}) {
553 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
554     $self->lookup_single_dnsbl($scanner, $obj, $rulename,
555 quinlan 160273 $revip, $rulecf->{zone}, $rulecf->{type});
556 jm 6898 }
557     }
558    
559     sub lookup_single_dnsbl {
560 felicity 439873 my ($self, $scanner, $obj, $rulename, $lookupstr, $dnsbl, $qtype) = @_;
561 jm 6898
562 jm 9881 my $key = "DNSBL:".$dnsbl.":".$lookupstr;
563 felicity 439873 return if $scanner->{async}->get_lookup($key);
564 jm 9881 my $item = $lookupstr.".".$dnsbl;
565 jm 6898
566     # dig $ip txt
567 mmartinec 585292 my $ent = $self->start_lookup($scanner, $item, 'DNSBL',
568 mmartinec 564636 $self->res_bgsend($scanner, $item, $qtype, $key),
569     $key);
570 jm 6898 $ent->{obj} = $obj;
571     $ent->{rulename} = $rulename;
572 jm 21406 $ent->{zone} = $dnsbl;
573 jm 6898 }
574    
575     sub complete_dnsbl_lookup {
576 felicity 439873 my ($self, $scanner, $ent, $dnsblip) = @_;
577 jm 6898
578 felicity 439873 my $conf = $scanner->{conf};
579 mmartinec 571893 my @subtests;
580 jm 21406 my $rulename = $ent->{rulename};
581     my $rulecf = $conf->{uridnsbls}->{$rulename};
582    
583 jm 161157 my $packet = $ent->{response_packet};
584 mmartinec 582903 my @answer = !defined $packet ? () : $packet->answer;
585 jm 161157
586 quinlan 125719 my $uridnsbl_subs = $conf->{uridnsbl_subs}->{$ent->{zone}};
587 jm 21406 foreach my $rr (@answer)
588     {
589     next if ($rr->type ne 'A' && $rr->type ne 'TXT');
590    
591     my $rdatastr = $rr->rdatastr;
592 jm 6898 my $dom = $ent->{obj}->{dom};
593    
594 jm 21406 if (!$rulecf->{is_subrule}) {
595     # this zone is a simple rule, not a set of subrules
596 quinlan 125719 # skip any A record that isn't on 127/8
597 jm 158887 if ($rr->type eq 'A' && $rr->rdatastr !~ /^127\./) {
598 jm 165017 warn("uridnsbl: bogus rr for domain=$dom, rule=$rulename, id=" .
599     $packet->header->id." rr=".$rr->string);
600 quinlan 158898 next;
601 jm 158887 }
602 felicity 439873 $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $rulename);
603 jm 21406 }
604     else {
605     foreach my $subtest (keys (%{$uridnsbl_subs}))
606     {
607     if ($subtest eq $rdatastr) {
608 dos 543724 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
609     $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
610     }
611 jm 21406 }
612     # bitmask
613     elsif ($subtest =~ /^\d+$/) {
614 sidney 433917 if ($rdatastr =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ &&
615     Mail::SpamAssassin::Util::my_inet_aton($rdatastr) & $subtest)
616 jm 21406 {
617 dos 543724 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
618     $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
619     }
620 jm 21406 }
621     }
622     }
623     }
624 jm 6898 }
625     }
626    
627 jm 21406 sub got_dnsbl_hit {
628 felicity 439873 my ($self, $scanner, $ent, $str, $dom, $rulename) = @_;
629 jm 21406
630     $str =~ s/\s+/ /gs; # long whitespace => short
631 quinlan 51813 dbg("uridnsbl: domain \"$dom\" listed ($rulename): $str");
632 jm 21406
633 felicity 439873 if (!defined $scanner->{uridnsbl_hits}->{$rulename}) {
634     $scanner->{uridnsbl_hits}->{$rulename} = { };
635 jm 21406 };
636 felicity 439873 $scanner->{uridnsbl_hits}->{$rulename}->{$dom} = 1;
637 jm 394348
638 felicity 439873 if ($scanner->{uridnsbl_active_rules_revipbl}->{$rulename}
639     || $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename})
640 jm 394348 {
641     # TODO: this needs to handle multiple domain hits per rule
642 felicity 439873 $scanner->clear_test_state();
643     my $uris = join (' ', keys %{$scanner->{uridnsbl_hits}->{$rulename}});
644     $scanner->test_log ("URIs: $uris");
645     $scanner->got_hit ($rulename, "");
646 jm 394351
647     # note that this rule has completed (since it got at least 1 hit)
648 felicity 439873 $scanner->register_async_rule_finish($rulename);
649 jm 394348 }
650 jm 21406 }
651    
652 jm 6898 # ---------------------------------------------------------------------------
653    
654     sub start_lookup {
655 mmartinec 585292 my ($self, $scanner, $zone, $type, $id, $key) = @_;
656 jm 394348
657 jm 6898 my $ent = {
658 jm 394348 key => $key,
659 mmartinec 585292 zone => $zone, # serves to fetch other per-zone settings
660 jm 394348 type => "URI-".$type,
661     id => $id,
662     completed_callback => sub {
663     my $ent = shift;
664 mmartinec 580055 if (defined $ent->{response_packet}) { # not aborted or empty
665     $self->completed_lookup_callback ($scanner, $ent);
666     }
667 jm 394348 }
668 jm 6898 };
669 felicity 439873 $scanner->{async}->start_lookup($ent);
670 jm 394348 return $ent;
671 jm 6898 }
672    
673 jm 394348 sub completed_lookup_callback {
674 felicity 439873 my ($self, $scanner, $ent) = @_;
675 jm 394348 my $type = $ent->{type};
676     my $key = $ent->{key};
677     $key =~ /:(\S+?)$/; my $val = $1;
678 jm 6898
679 jm 394348 if ($type eq 'URI-NS') {
680 felicity 439873 $self->complete_ns_lookup ($scanner, $ent, $val);
681 jm 6898 }
682 jm 394348 elsif ($type eq 'URI-A') {
683 felicity 439873 $self->complete_a_lookup ($scanner, $ent, $val);
684 jm 6898 }
685 jm 394348 elsif ($type eq 'URI-DNSBL') {
686 felicity 439873 $self->complete_dnsbl_lookup ($scanner, $ent, $val);
687 jm 6898 }
688     }
689    
690     # ---------------------------------------------------------------------------
691    
692 jm 161148 sub res_bgsend {
693 mmartinec 564636 my ($self, $scanner, $host, $type, $key) = @_;
694 jm 161778
695     return $self->{main}->{resolver}->bgsend($host, $type, undef, sub {
696 mmartinec 564636 my ($pkt, $id, $timestamp) = @_;
697     $scanner->{async}->set_response_packet($id, $pkt, $key, $timestamp);
698 jm 161778 });
699 jm 161148 }
700    
701 jm 6898 sub log_dns_result {
702 felicity 57557 #my $self = shift;
703 quinlan 51813 #Mail::SpamAssassin::dbg("uridnsbl: ".join (' ', @_));
704 jm 6898 }
705    
706     # ---------------------------------------------------------------------------
707    
708     1;

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2