/[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 585292 - (show annotations)
Wed Oct 17 00:04:48 2007 UTC (2 years, 1 month ago) by mmartinec
File size: 20272 byte(s)
AsyncLoop: replaces sharp clipping on a lower timeout value with a
smooth quadratic transition curve from an initial to a minimal timeout
value; updated documentation accordingly. Adjust actual timeout value
to timer resolution (ceiling) if Time::HiRes module is not available.

Conf+AsyncLoop: to a configuration variable rbl_timeout add two optional
parameters: a minimal timeout value, and a zone name if a timeout
specification should only apply to the specified zone or its subdomains;
update documentation accordingly.

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

apache@apache.org
ViewVC Help
Powered by ViewVC 1.1.2