/[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 - (show 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 # <@LICENSE>
2 # 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 #
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 =head1 NAME
19
20 URIDNSBL - look up URLs against DNS blocklists
21
22 =head1 SYNOPSIS
23
24 loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
25 uridnsbl URIBL_SBLXBL sbl-xbl.spamhaus.org. TXT
26
27 =head1 DESCRIPTION
28
29 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 =head1 USER SETTINGS
35
36 =over 4
37
38 =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 =item uridnsbl NAME_OF_RULE dnsbl_zone lookuptype
51
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 define a body-eval rule calling C<check_uridnsbl()> to use this.
56
57 Example:
58
59 uridnsbl URIBL_SBLXBL sbl-xbl.spamhaus.org. TXT
60 body URIBL_SBLXBL eval:check_uridnsbl('URIBL_SBLXBL')
61 describe URIBL_SBLXBL Contains a URL listed in the SBL/XBL blocklist
62
63 =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 define a body-eval rule calling C<check_uridnsbl()> to use this.
69
70 An RHSBL zone is one where the domain name is looked up, as a string; e.g. a
71 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
77 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
80 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 either be an IPv4 dotted address for RHSBLs that return multiple A records or a
92 non-negative decimal number to specify a bitmask for RHSBLs that return a
93 single A record containing a bitmask of results.
94
95 Note that, as with C<urirhsbl>, you must also define a body-eval rule calling
96 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 =back
104
105 =head1 ADMINISTRATOR SETTINGS
106
107 =over 4
108
109 =item uridnsbl_max_domains N (default: 20)
110
111 The maximum number of domains to look up.
112
113 =back
114
115 =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 =cut
121
122 package Mail::SpamAssassin::Plugin::URIDNSBL;
123
124 use Mail::SpamAssassin::Plugin;
125 use Mail::SpamAssassin::Constants qw(:ip);
126 use Mail::SpamAssassin::Util;
127 use Mail::SpamAssassin::Logger;
128 use strict;
129 use warnings;
130 use bytes;
131 use re 'taint';
132
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 # this can be effectively global, at least in each process, safely
149
150 $self->{finished} = { };
151
152 $self->register_eval_rule ("check_uridnsbl");
153 $self->set_config($samain->{conf});
154
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 if (!$scanner->is_dns_available()) {
172 $self->{dns_not_available} = 1;
173 return;
174 } 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 }
179
180 $scanner->{'uridnsbl_activerules'} = { };
181 $scanner->{'uridnsbl_hits'} = { };
182 $scanner->{'uridnsbl_seen_domain'} = { };
183
184 # only hit DNSBLs for active rules (defined and score != 0)
185 $scanner->{'uridnsbl_active_rules_rhsbl'} = { };
186 $scanner->{'uridnsbl_active_rules_revipbl'} = { };
187
188 foreach my $rulename (keys %{$scanner->{conf}->{uridnsbls}}) {
189 next unless ($scanner->{conf}->is_rule_active('body_evals',$rulename));
190
191 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
192 if ($rulecf->{is_rhsbl}) {
193 $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename} = 1;
194 } else {
195 $scanner->{uridnsbl_active_rules_revipbl}->{$rulename} = 1;
196 }
197 }
198
199 # get all domains in message
200
201 # don't keep dereferencing this
202 my $skip_domains = $scanner->{main}->{conf}->{uridnsbl_skip_domains};
203
204 # list of hashes to use in order
205 my @uri_ordered;
206
207 # Generate the full list of html-parsed domains.
208 my $uris = $scanner->get_uri_detail_list();
209
210 # 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 while (my($uri, $info) = each %{$uris}) {
218 # 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 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 }
237 elsif ($info->{types}->{form}) {
238 $entry = 1;
239 }
240 elsif ($info->{types}->{img}) {
241 $entry = 2;
242 }
243 elsif ($info->{types}->{parsed} && (keys %{$info->{types}} == 1)) {
244 $entry = 4;
245 }
246
247 # 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 }
256
257 # at this point, @uri_ordered is an ordered array of uri hashes
258
259 my %domlist;
260 my $umd = $scanner->{main}->{conf}->{uridnsbl_max_domains};
261 while (keys %domlist < $umd && @uri_ordered) {
262 my $array = shift @uri_ordered;
263 next unless $array;
264
265 # run through and find the new domains in this grouping
266 my @domains = grep(!$domlist{$_}, keys %{$array});
267 next unless @domains;
268
269 # the new domains are all useful, just add them in
270 if (keys(%domlist) + @domains <= $umd) {
271 foreach (@domains) {
272 $domlist{$_} = 1;
273 }
274 }
275 else {
276 # trim down to a limited number - pick randomly
277 my $i;
278 while (@domains && keys %domlist < $umd) {
279 my $r = int rand (scalar @domains);
280 $domlist{splice (@domains, $r, 1)} = 1;
281 }
282 }
283 }
284
285 # and query
286 dbg("uridnsbl: domains to query: ".join(' ',keys %domlist));
287 foreach my $dom (keys %domlist) {
288 $self->query_domain ($scanner, $dom);
289 }
290
291 return 1;
292 }
293
294 sub set_config {
295 my($self, $conf) = @_;
296 my @cmds;
297
298 push(@cmds, {
299 setting => 'uridnsbl_max_domains',
300 is_admin => 1,
301 default => 20,
302 type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
303 });
304
305 push (@cmds, {
306 setting => 'uridnsbl',
307 is_priv => 1,
308 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 elsif ($value =~ /^$/) {
320 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
321 }
322 else {
323 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
324 }
325 }
326 });
327
328 push (@cmds, {
329 setting => 'urirhsbl',
330 is_priv => 1,
331 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 elsif ($value =~ /^$/) {
343 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
344 }
345 else {
346 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
347 }
348 }
349 });
350
351 push (@cmds, {
352 setting => 'urirhssub',
353 is_priv => 1,
354 code => sub {
355 my ($self, $key, $value, $line) = @_;
356 if ($value =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\d{1,10}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
357 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 push (@{$self->{uridnsbl_subs}->{$zone}->{$subrule}->{rulenames}}, $rulename);
367 }
368 elsif ($value =~ /^$/) {
369 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
370 }
371 else {
372 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
373 }
374 }
375 });
376
377 push (@cmds, {
378 setting => 'uridnsbl_skip_domain',
379 default => {},
380 code => sub {
381 my ($self, $key, $value, $line) = @_;
382 if ($value =~ /^$/) {
383 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
384 }
385 foreach my $domain (split(/\s+/, $value)) {
386 $self->{uridnsbl_skip_domains}->{lc $domain} = 1;
387 }
388 }
389 });
390
391 # 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 $conf->{parser}->register_commands(\@cmds);
403 }
404
405 # ---------------------------------------------------------------------------
406
407 sub query_domain {
408 my ($self, $scanner, $dom) = @_;
409
410 #warn "uridnsbl: domain $dom\n";
411 #return;
412
413 $dom = lc $dom;
414 return if $scanner->{uridnsbl_seen_domain}->{$dom};
415 $scanner->{uridnsbl_seen_domain}->{$dom} = 1;
416 $self->log_dns_result("querying domain $dom");
417
418 my $obj = { dom => $dom };
419
420 my $single_dnsbl = 0;
421 if ($dom =~ /^\d+\.\d+\.\d+\.\d+$/) {
422 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 $self->lookup_dnsbl_for_ip($scanner, $obj, $dom);
427 # 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 }
434 else {
435 $single_dnsbl = 1;
436 }
437
438 my $rhsblrules = $scanner->{uridnsbl_active_rules_rhsbl};
439 my $reviprules = $scanner->{uridnsbl_active_rules_revipbl};
440
441 if ($single_dnsbl) {
442 # look up the domain in the RHSBL subset
443 foreach my $rulename (keys %{$rhsblrules}) {
444 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
445 $self->lookup_single_dnsbl($scanner, $obj, $rulename,
446 $dom, $rulecf->{zone}, $rulecf->{type});
447
448 # see comment below
449 $scanner->register_async_rule_start($rulename);
450 }
451
452 # 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 $self->lookup_domain_ns($scanner, $obj, $dom);
456 }
457 }
458
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 foreach my $rulename (keys %{$reviprules}) {
463 $scanner->register_async_rule_start($rulename);
464 }
465 }
466
467 # ---------------------------------------------------------------------------
468
469 sub lookup_domain_ns {
470 my ($self, $scanner, $obj, $dom) = @_;
471
472 my $key = "NS:".$dom;
473 return if $scanner->{async}->get_lookup($key);
474
475 # dig $dom ns
476 my $ent = $self->start_lookup($scanner, $dom, 'NS',
477 $self->res_bgsend($scanner, $dom, 'NS', $key),
478 $key);
479 $ent->{obj} = $obj;
480 }
481
482 sub complete_ns_lookup {
483 my ($self, $scanner, $ent, $dom) = @_;
484
485 my $packet = $ent->{response_packet};
486 my @answer = !defined $packet ? () : $packet->answer;
487
488 my $IPV4_ADDRESS = IPV4_ADDRESS;
489 my $IP_PRIVATE = IP_PRIVATE;
490
491 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 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 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $nsmatch);
504 }
505 }
506 else {
507 $self->lookup_a_record($scanner, $ent->{obj}, $nsmatch);
508 }
509 }
510 }
511 }
512
513 # ---------------------------------------------------------------------------
514
515 sub lookup_a_record {
516 my ($self, $scanner, $obj, $hname) = @_;
517
518 my $key = "A:".$hname;
519 return if $scanner->{async}->get_lookup($key);
520
521 # dig $hname a
522 my $ent = $self->start_lookup($scanner, $hname, 'A',
523 $self->res_bgsend($scanner, $hname, 'A', $key),
524 $key);
525 $ent->{obj} = $obj;
526 }
527
528 sub complete_a_lookup {
529 my ($self, $scanner, $ent, $hname) = @_;
530
531 my $packet = $ent->{response_packet};
532 my @answer = !defined $packet ? () : $packet->answer;
533 foreach my $rr (@answer) {
534 my $str = $rr->string;
535 $self->log_dns_result ("A for NS $hname: $str");
536
537 if ($str =~ /IN\s+A\s+(\S+)/) {
538 $self->lookup_dnsbl_for_ip($scanner, $ent->{obj}, $1);
539 }
540 }
541 }
542
543 # ---------------------------------------------------------------------------
544
545 sub lookup_dnsbl_for_ip {
546 my ($self, $scanner, $obj, $ip) = @_;
547
548 $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
549 my $revip = "$4.$3.$2.$1";
550
551 my $cf = $scanner->{uridnsbl_active_rules_revipbl};
552 foreach my $rulename (keys %{$cf}) {
553 my $rulecf = $scanner->{conf}->{uridnsbls}->{$rulename};
554 $self->lookup_single_dnsbl($scanner, $obj, $rulename,
555 $revip, $rulecf->{zone}, $rulecf->{type});
556 }
557 }
558
559 sub lookup_single_dnsbl {
560 my ($self, $scanner, $obj, $rulename, $lookupstr, $dnsbl, $qtype) = @_;
561
562 my $key = "DNSBL:".$dnsbl.":".$lookupstr;
563 return if $scanner->{async}->get_lookup($key);
564 my $item = $lookupstr.".".$dnsbl;
565
566 # dig $ip txt
567 my $ent = $self->start_lookup($scanner, $item, 'DNSBL',
568 $self->res_bgsend($scanner, $item, $qtype, $key),
569 $key);
570 $ent->{obj} = $obj;
571 $ent->{rulename} = $rulename;
572 $ent->{zone} = $dnsbl;
573 }
574
575 sub complete_dnsbl_lookup {
576 my ($self, $scanner, $ent, $dnsblip) = @_;
577
578 my $conf = $scanner->{conf};
579 my @subtests;
580 my $rulename = $ent->{rulename};
581 my $rulecf = $conf->{uridnsbls}->{$rulename};
582
583 my $packet = $ent->{response_packet};
584 my @answer = !defined $packet ? () : $packet->answer;
585
586 my $uridnsbl_subs = $conf->{uridnsbl_subs}->{$ent->{zone}};
587 foreach my $rr (@answer)
588 {
589 next if ($rr->type ne 'A' && $rr->type ne 'TXT');
590
591 my $rdatastr = $rr->rdatastr;
592 my $dom = $ent->{obj}->{dom};
593
594 if (!$rulecf->{is_subrule}) {
595 # this zone is a simple rule, not a set of subrules
596 # skip any A record that isn't on 127/8
597 if ($rr->type eq 'A' && $rr->rdatastr !~ /^127\./) {
598 warn("uridnsbl: bogus rr for domain=$dom, rule=$rulename, id=" .
599 $packet->header->id." rr=".$rr->string);
600 next;
601 }
602 $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $rulename);
603 }
604 else {
605 foreach my $subtest (keys (%{$uridnsbl_subs}))
606 {
607 if ($subtest eq $rdatastr) {
608 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
609 $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
610 }
611 }
612 # bitmask
613 elsif ($subtest =~ /^\d+$/) {
614 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 {
617 foreach my $subrulename (@{$uridnsbl_subs->{$subtest}->{rulenames}}) {
618 $self->got_dnsbl_hit($scanner, $ent, $rdatastr, $dom, $subrulename);
619 }
620 }
621 }
622 }
623 }
624 }
625 }
626
627 sub got_dnsbl_hit {
628 my ($self, $scanner, $ent, $str, $dom, $rulename) = @_;
629
630 $str =~ s/\s+/ /gs; # long whitespace => short
631 dbg("uridnsbl: domain \"$dom\" listed ($rulename): $str");
632
633 if (!defined $scanner->{uridnsbl_hits}->{$rulename}) {
634 $scanner->{uridnsbl_hits}->{$rulename} = { };
635 };
636 $scanner->{uridnsbl_hits}->{$rulename}->{$dom} = 1;
637
638 if ($scanner->{uridnsbl_active_rules_revipbl}->{$rulename}
639 || $scanner->{uridnsbl_active_rules_rhsbl}->{$rulename})
640 {
641 # TODO: this needs to handle multiple domain hits per rule
642 $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
647 # note that this rule has completed (since it got at least 1 hit)
648 $scanner->register_async_rule_finish($rulename);
649 }
650 }
651
652 # ---------------------------------------------------------------------------
653
654 sub start_lookup {
655 my ($self, $scanner, $zone, $type, $id, $key) = @_;
656
657 my $ent = {
658 key => $key,
659 zone => $zone, # serves to fetch other per-zone settings
660 type => "URI-".$type,
661 id => $id,
662 completed_callback => sub {
663 my $ent = shift;
664 if (defined $ent->{response_packet}) { # not aborted or empty
665 $self->completed_lookup_callback ($scanner, $ent);
666 }
667 }
668 };
669 $scanner->{async}->start_lookup($ent);
670 return $ent;
671 }
672
673 sub completed_lookup_callback {
674 my ($self, $scanner, $ent) = @_;
675 my $type = $ent->{type};
676 my $key = $ent->{key};
677 $key =~ /:(\S+?)$/; my $val = $1;
678
679 if ($type eq 'URI-NS') {
680 $self->complete_ns_lookup ($scanner, $ent, $val);
681 }
682 elsif ($type eq 'URI-A') {
683 $self->complete_a_lookup ($scanner, $ent, $val);
684 }
685 elsif ($type eq 'URI-DNSBL') {
686 $self->complete_dnsbl_lookup ($scanner, $ent, $val);
687 }
688 }
689
690 # ---------------------------------------------------------------------------
691
692 sub res_bgsend {
693 my ($self, $scanner, $host, $type, $key) = @_;
694
695 return $self->{main}->{resolver}->bgsend($host, $type, undef, sub {
696 my ($pkt, $id, $timestamp) = @_;
697 $scanner->{async}->set_response_packet($id, $pkt, $key, $timestamp);
698 });
699 }
700
701 sub log_dns_result {
702 #my $self = shift;
703 #Mail::SpamAssassin::dbg("uridnsbl: ".join (' ', @_));
704 }
705
706 # ---------------------------------------------------------------------------
707
708 1;

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2