/[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 574664 - (hide annotations)
Tue Sep 11 18:46:40 2007 UTC (2 years, 2 months ago) by mmartinec
File size: 20031 byte(s)
add a:  use re taint  to every module, see bug 5645
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     if ($single_dnsbl) {
439 jm 9881 # look up the domain in the RHSBL subset
440 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_rhsbl};
441 jm 9881 foreach my $rulename (keys %{$cf}) {
442 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
443     $self->lookup_single_dnsbl($scanner, $obj, $rulename,
444 quinlan 160273 $dom, $rulecf->{zone}, $rulecf->{type});
445 jm 394351
446     # see comment below
447 felicity 439873 $scanner->register_async_rule_start($rulename);
448 jm 9881 }
449    
450     # perform NS, A lookups to look up the domain in the non-RHSBL subset
451 quinlan 160273 if ($dom !~ /^\d+\.\d+\.\d+\.\d+$/) {
452 felicity 439873 $self->lookup_domain_ns($scanner, $obj, $dom);
453 quinlan 160273 }
454 jm 6898 }
455 jm 394351
456     # note that these rules are now underway. important: unless the
457     # rule hits, in the current design, these will not be considered
458     # "finished" until harvest_dnsbl_queries() completes
459 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_revipbl};
460 jm 394351 foreach my $rulename (keys %{$cf}) {
461 felicity 439873 $scanner->register_async_rule_start($rulename);
462 jm 394351 }
463 jm 6898 }
464    
465     # ---------------------------------------------------------------------------
466    
467     sub lookup_domain_ns {
468 felicity 439873 my ($self, $scanner, $obj, $dom) = @_;
469 jm 6898
470     my $key = "NS:".$dom;
471 felicity 439873 return if $scanner->{async}->get_lookup($key);
472 jm 6898
473     # dig $dom ns
474 mmartinec 564636 my $ent = $self->start_lookup($scanner, 'NS',
475     $self->res_bgsend($scanner, $dom, 'NS', $key),
476     $key);
477 jm 6898 $ent->{obj} = $obj;
478     }
479    
480     sub complete_ns_lookup {
481 felicity 439873 my ($self, $scanner, $ent, $dom) = @_;
482 jm 7002
483 jm 161157 my $packet = $ent->{response_packet};
484 jm 6898 my @answer = $packet->answer;
485    
486 quinlan 160273 my $IPV4_ADDRESS = IPV4_ADDRESS;
487     my $IP_PRIVATE = IP_PRIVATE;
488    
489 jm 6898 foreach my $rr (@answer) {
490     my $str = $rr->string;
491     next unless (defined($str) && defined($dom));
492     $self->log_dns_result ("NSs for $dom: $str");
493    
494     if ($str =~ /IN\s+NS\s+(\S+)/) {
495 quinlan 160273 my $nsmatch = $1;
496    
497     if ($nsmatch =~ /^\d+\.\d+\.\d+\.\d+\.?$/) {
498     $nsmatch =~ s/\.$//;
499     # only look up the IP if it is public and valid
500     if ($nsmatch =~ /^$IPV4_ADDRESS$/ && $nsmatch !~ /^$IP_PRIVATE$/) {
501 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $nsmatch);
502 quinlan 160273 }
503     }
504     else {
505 felicity 439873 $self->lookup_a_record($scanner, $ent->{obj}, $nsmatch);
506 quinlan 160273 }
507 jm 6898 }
508     }
509     }
510    
511     # ---------------------------------------------------------------------------
512    
513     sub lookup_a_record {
514 felicity 439873 my ($self, $scanner, $obj, $hname) = @_;
515 jm 6898
516     my $key = "A:".$hname;
517 felicity 439873 return if $scanner->{async}->get_lookup($key);
518 jm 6898
519     # dig $hname a
520 mmartinec 564636 my $ent = $self->start_lookup($scanner, 'A',
521     $self->res_bgsend($scanner, $hname, 'A', $key),
522     $key);
523 jm 6898 $ent->{obj} = $obj;
524     }
525    
526     sub complete_a_lookup {
527 felicity 439873 my ($self, $scanner, $ent, $hname) = @_;
528 jm 6898
529 jm 161778 foreach my $rr ($ent->{response_packet}->answer) {
530 jm 6898 my $str = $rr->string;
531     $self->log_dns_result ("A for NS $hname: $str");
532    
533     if ($str =~ /IN\s+A\s+(\S+)/) {
534 felicity 439873 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $1);
535 jm 6898 }
536     }
537     }
538    
539     # ---------------------------------------------------------------------------
540    
541     sub lookup_dnsbl_for_ip {
542 felicity 439873 my ($self, $scanner, $obj, $ip) = @_;
543 jm 6898
544     $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
545     my $revip = "$4.$3.$2.$1";
546    
547 felicity 439873 my $cf = $scanner->{uridnsbl_active_rules_revipbl};
548 jm 6898 foreach my $rulename (keys %{$cf}) {
549 felicity 439873 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
550     $self->lookup_single_dnsbl($scanner, $obj, $rulename,
551 quinlan 160273 $revip, $rulecf->{zone}, $rulecf->{type});
552 jm 6898 }
553     }
554    
555     sub lookup_single_dnsbl {
556 felicity 439873 my ($self, $scanner, $obj, $rulename, $lookupstr, $dnsbl, $qtype) = @_;
557 jm 6898
558 jm 9881 my $key = "DNSBL:".$dnsbl.":".$lookupstr;
559 felicity 439873 return if $scanner->{async}->get_lookup($key);
560 jm 9881 my $item = $lookupstr.".".$dnsbl;
561 jm 6898
562     # dig $ip txt
563 mmartinec 564636 my $ent = $self->start_lookup($scanner, 'DNSBL',
564     $self->res_bgsend($scanner, $item, $qtype, $key),
565     $key);
566 jm 6898 $ent->{obj} = $obj;
567     $ent->{rulename} = $rulename;
568 jm 21406 $ent->{zone} = $dnsbl;
569 jm 6898 }
570    
571     sub complete_dnsbl_lookup {
572 felicity 439873 my ($self, $scanner, $ent, $dnsblip) = @_;
573 jm 6898
574 felicity 439873 my $conf = $scanner->{conf};
575 mmartinec 571893 my @subtests;
576 jm 21406 my $rulename = $ent->{rulename};
577     my $rulecf = $conf->{uridnsbls}->{$rulename};
578    
579 jm 161157 my $packet = $ent->{response_packet};
580 jm 6898 my @answer = $packet->answer;
581 jm 161157
582 quinlan 125719 my $uridnsbl_subs = $conf->{uridnsbl_subs}->{$ent->{zone}};
583 jm 21406 foreach my $rr (@answer)
584     {
585     next if ($rr->type ne 'A' && $rr->type ne 'TXT');
586    
587     my $rdatastr = $rr->rdatastr;
588 jm 6898 my $dom = $ent->{obj}->{dom};
589    
590 jm 21406 if (!$rulecf->{is_subrule}) {
591     # this zone is a simple rule, not a set of subrules
592 quinlan 125719 # skip any A record that isn't on 127/8
593 jm 158887 if ($rr->type eq 'A' && $rr->rdatastr !~ /^127\./) {
594 jm 165017 warn("uridnsbl: bogus rr for domain=$dom, rule=$rulename, id=" .
595     $packet->header->id." rr=".$rr->string);
596 quinlan 158898 next;
597 jm 158887 }
598 felicity 439873 $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $rulename);
599 jm 21406 }
600     else {
601     foreach my $subtest (keys (%{$uridnsbl_subs}))
602     {
603     if ($subtest eq $rdatastr) {
604 dos 543724 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
605     $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
606     }
607 jm 21406 }
608     # bitmask
609     elsif ($subtest =~ /^\d+$/) {
610 sidney 433917 if ($rdatastr =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ &&
611     Mail::SpamAssassin::Util::my_inet_aton($rdatastr) & $subtest)
612 jm 21406 {
613 dos 543724 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
614     $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
615     }
616 jm 21406 }
617     }
618     }
619     }
620 jm 6898 }
621     }
622    
623 jm 21406 sub got_dnsbl_hit {
624 felicity 439873 my ($self, $scanner, $ent, $str, $dom, $rulename) = @_;
625 jm 21406
626     $str =~ s/\s+/ /gs; # long whitespace => short
627 quinlan 51813 dbg("uridnsbl: domain \"$dom\" listed ($rulename): $str");
628 jm 21406
629 felicity 439873 if (!defined $scanner->{uridnsbl_hits}->{$rulename}) {
630     $scanner->{uridnsbl_hits}->{$rulename} = { };
631 jm 21406 };
632 felicity 439873 $scanner->{uridnsbl_hits}->{$rulename}->{$dom} = 1;
633 jm 394348
634 felicity 439873 if ($scanner->{uridnsbl_active_rules_revipbl}->{$rulename}
635     || $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename})
636 jm 394348 {
637     # TODO: this needs to handle multiple domain hits per rule
638 felicity 439873 $scanner->clear_test_state();
639     my $uris = join (' ', keys %{$scanner->{uridnsbl_hits}->{$rulename}});
640     $scanner->test_log ("URIs: $uris");
641     $scanner->got_hit ($rulename, "");
642 jm 394351
643     # note that this rule has completed (since it got at least 1 hit)
644 felicity 439873 $scanner->register_async_rule_finish($rulename);
645 jm 394348 }
646 jm 21406 }
647    
648 jm 6898 # ---------------------------------------------------------------------------
649    
650     sub start_lookup {
651 felicity 439873 my ($self, $scanner, $type, $id, $key) = @_;
652 jm 394348
653 jm 6898 my $ent = {
654 jm 394348 key => $key,
655 mmartinec 564636 timeout => $scanner->{conf}->{rbl_timeout},
656 jm 394348 type => "URI-".$type,
657     id => $id,
658     completed_callback => sub {
659     my $ent = shift;
660 felicity 439873 $self->completed_lookup_callback ($scanner, $ent);
661 jm 394348 }
662 jm 6898 };
663 felicity 439873 $scanner->{async}->start_lookup($ent);
664 jm 394348 return $ent;
665 jm 6898 }
666    
667 jm 394348 sub completed_lookup_callback {
668 felicity 439873 my ($self, $scanner, $ent) = @_;
669 jm 394348 my $type = $ent->{type};
670     my $key = $ent->{key};
671     $key =~ /:(\S+?)$/; my $val = $1;
672 jm 6898
673 jm 394348 if ($type eq 'URI-NS') {
674 felicity 439873 $self->complete_ns_lookup ($scanner, $ent, $val);
675 jm 6898 }
676 jm 394348 elsif ($type eq 'URI-A') {
677 felicity 439873 $self->complete_a_lookup ($scanner, $ent, $val);
678 jm 6898 }
679 jm 394348 elsif ($type eq 'URI-DNSBL') {
680 felicity 439873 $self->complete_dnsbl_lookup ($scanner, $ent, $val);
681 jm 6898 }
682     }
683    
684     # ---------------------------------------------------------------------------
685    
686 jm 161148 sub res_bgsend {
687 mmartinec 564636 my ($self, $scanner, $host, $type, $key) = @_;
688 jm 161778
689     return $self->{main}->{resolver}->bgsend($host, $type, undef, sub {
690 mmartinec 564636 my ($pkt, $id, $timestamp) = @_;
691     $scanner->{async}->set_response_packet($id, $pkt, $key, $timestamp);
692 jm 161778 });
693 jm 161148 }
694    
695 jm 6898 sub log_dns_result {
696 felicity 57557 #my $self = shift;
697 quinlan 51813 #Mail::SpamAssassin::dbg("uridnsbl: ".join (' ', @_));
698 jm 6898 }
699    
700     # ---------------------------------------------------------------------------
701    
702     1;

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2