| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
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
|
|
|
|
|
|
|
Mail::SpamAssassin::AsyncLoop - scanner asynchronous event loop |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
An asynchronous event loop used for long-running operations, performed "in the |
|
25
|
|
|
|
|
|
|
background" during the Mail::SpamAssassin::check() scan operation, such as DNS |
|
26
|
|
|
|
|
|
|
blocklist lookups. |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 METHODS |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=over 4 |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=cut |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
use strict; |
|
36
|
41
|
|
|
41
|
|
249
|
use warnings; |
|
|
41
|
|
|
|
|
69
|
|
|
|
41
|
|
|
|
|
1134
|
|
|
37
|
41
|
|
|
41
|
|
193
|
# use bytes; |
|
|
41
|
|
|
|
|
74
|
|
|
|
41
|
|
|
|
|
1274
|
|
|
38
|
|
|
|
|
|
|
use re 'taint'; |
|
39
|
41
|
|
|
41
|
|
211
|
|
|
|
41
|
|
|
|
|
95
|
|
|
|
41
|
|
|
|
|
1368
|
|
|
40
|
|
|
|
|
|
|
use Time::HiRes qw(time); |
|
41
|
41
|
|
|
41
|
|
256
|
|
|
|
41
|
|
|
|
|
69
|
|
|
|
41
|
|
|
|
|
244
|
|
|
42
|
|
|
|
|
|
|
use Mail::SpamAssassin; |
|
43
|
41
|
|
|
41
|
|
3488
|
use Mail::SpamAssassin::Logger; |
|
|
41
|
|
|
|
|
70
|
|
|
|
41
|
|
|
|
|
4232
|
|
|
44
|
41
|
|
|
41
|
|
209
|
|
|
|
41
|
|
|
|
|
95
|
|
|
|
41
|
|
|
|
|
8800
|
|
|
45
|
|
|
|
|
|
|
our @ISA = qw(); |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# obtain timer resolution if possible |
|
48
|
|
|
|
|
|
|
our $timer_resolution; |
|
49
|
|
|
|
|
|
|
BEGIN { |
|
50
|
|
|
|
|
|
|
eval { |
|
51
|
|
|
|
|
|
|
$timer_resolution = Time::HiRes->can('clock_getres') |
|
52
|
41
|
50
|
|
|
|
871
|
? Time::HiRes::clock_getres(Time::HiRes::CLOCK_REALTIME()) |
|
53
|
|
|
|
|
|
|
: 0.001; # wild guess, assume resolution is better than 1s |
|
54
|
|
|
|
|
|
|
1; |
|
55
|
41
|
|
|
|
|
119807
|
} or do { |
|
56
|
41
|
50
|
|
41
|
|
133
|
$timer_resolution = 1; # Perl's builtin timer ticks at one second |
|
57
|
0
|
|
|
|
|
0
|
}; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
############################################################################# |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# called from PerMsgStatus, a new AsyncLoop object is created |
|
63
|
|
|
|
|
|
|
# for each new message processing |
|
64
|
|
|
|
|
|
|
my $class = shift; |
|
65
|
|
|
|
|
|
|
$class = ref($class) || $class; |
|
66
|
155
|
|
|
155
|
0
|
351
|
|
|
67
|
155
|
|
33
|
|
|
740
|
my ($main) = @_; |
|
68
|
|
|
|
|
|
|
my $self = { |
|
69
|
155
|
|
|
|
|
389
|
main => $main, |
|
70
|
155
|
|
|
|
|
1772
|
queries_started => 0, |
|
71
|
|
|
|
|
|
|
queries_completed => 0, |
|
72
|
|
|
|
|
|
|
total_queries_started => 0, |
|
73
|
|
|
|
|
|
|
total_queries_completed => 0, |
|
74
|
|
|
|
|
|
|
pending_lookups => { }, |
|
75
|
|
|
|
|
|
|
timing_by_query => { }, |
|
76
|
|
|
|
|
|
|
all_lookups => { }, # keyed by "rr_type/domain" |
|
77
|
|
|
|
|
|
|
}; |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
bless ($self, $class); |
|
80
|
|
|
|
|
|
|
$self; |
|
81
|
155
|
|
|
|
|
496
|
} |
|
82
|
155
|
|
|
|
|
3471
|
|
|
83
|
|
|
|
|
|
|
# Given a domain name, produces a listref of successively stripped down |
|
84
|
|
|
|
|
|
|
# parent domains, e.g. a domain '2.10.Example.COM' would produce a list: |
|
85
|
|
|
|
|
|
|
# '2.10.example.com', '10.example.com', 'example.com', 'com', '' |
|
86
|
|
|
|
|
|
|
# |
|
87
|
|
|
|
|
|
|
my ($domain) = @_; |
|
88
|
|
|
|
|
|
|
$domain =~ s/^\.+//; $domain =~ s/\.+\z//; # strip leading and trailing dots |
|
89
|
|
|
|
|
|
|
my @search_keys; |
|
90
|
0
|
|
|
0
|
0
|
0
|
if ($domain =~ /\[/) { # don't split address literals |
|
91
|
0
|
|
|
|
|
0
|
@search_keys = ( $domain, '' ); # presumably an address literal |
|
|
0
|
|
|
|
|
0
|
|
|
92
|
0
|
|
|
|
|
0
|
} else { |
|
93
|
0
|
0
|
|
|
|
0
|
local $1; |
|
94
|
0
|
|
|
|
|
0
|
$domain = lc $domain; |
|
95
|
|
|
|
|
|
|
for (;;) { |
|
96
|
0
|
|
|
|
|
0
|
push(@search_keys, $domain); |
|
97
|
0
|
|
|
|
|
0
|
last if $domain eq ''; |
|
98
|
0
|
|
|
|
|
0
|
# strip one level |
|
99
|
0
|
|
|
|
|
0
|
$domain = ($domain =~ /^ (?: [^.]* ) \. (.*) \z/xs) ? $1 : ''; |
|
100
|
0
|
0
|
|
|
|
0
|
} |
|
101
|
|
|
|
|
|
|
if (@search_keys > 20) { # enforce some sanity limit |
|
102
|
0
|
0
|
|
|
|
0
|
@search_keys = @search_keys[$#search_keys-19 .. $#search_keys]; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
0
|
0
|
|
|
|
0
|
} |
|
105
|
0
|
|
|
|
|
0
|
return \@search_keys; |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
0
|
# --------------------------------------------------------------------------- |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=item $ent = $async->start_lookup($ent, $master_deadline) |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Register the start of a long-running asynchronous lookup operation. |
|
113
|
|
|
|
|
|
|
C<$ent> is a hash reference containing the following items: |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=over 4 |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item key (required) |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
A key string, unique to this lookup. This is what is reported in |
|
120
|
|
|
|
|
|
|
debug messages, used as the key for C<get_lookup()>, etc. |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=item id (required) |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
An ID string, also unique to this lookup. Typically, this is the DNS packet ID |
|
125
|
|
|
|
|
|
|
as returned by DnsResolver's C<bgsend> method. Sadly, the Net::DNS |
|
126
|
|
|
|
|
|
|
architecture forces us to keep a separate ID string for this task instead of |
|
127
|
|
|
|
|
|
|
reusing C<key> -- if you are not using DNS lookups through DnsResolver, it |
|
128
|
|
|
|
|
|
|
should be OK to just reuse C<key>. |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=item type (required) |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
A string, typically one word, used to describe the type of lookup in log |
|
133
|
|
|
|
|
|
|
messages, such as C<DNSBL>, C<MX>, C<TXT>. |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item zone (optional) |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
A zone specification (typically a DNS zone name - e.g. host, domain, or RBL) |
|
138
|
|
|
|
|
|
|
which may be used as a key to look up per-zone settings. No semantics on this |
|
139
|
|
|
|
|
|
|
parameter is imposed by this module. Currently used to fetch by-zone timeouts. |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item timeout_initial (optional) |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
An initial value of elapsed time for which we are willing to wait for a |
|
144
|
|
|
|
|
|
|
response (time in seconds, floating point value is allowed). When elapsed |
|
145
|
|
|
|
|
|
|
time since a query started exceeds the timeout value and there are no other |
|
146
|
|
|
|
|
|
|
queries to wait for, the query is aborted. The actual timeout value ranges |
|
147
|
|
|
|
|
|
|
from timeout_initial and gradually approaches timeout_min (see next parameter) |
|
148
|
|
|
|
|
|
|
as the number of already completed queries approaches the number of all |
|
149
|
|
|
|
|
|
|
queries started. |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
If a caller does not explicitly provide this parameter or its value is |
|
152
|
|
|
|
|
|
|
undefined, a default initial timeout value is settable by a configuration |
|
153
|
|
|
|
|
|
|
variable rbl_timeout. |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
If a value of the timeout_initial parameter is below timeout_min, the initial |
|
156
|
|
|
|
|
|
|
timeout is set to timeout_min. |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=item timeout_min (optional) |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
A lower bound (in seconds) to which the actual timeout approaches as the |
|
161
|
|
|
|
|
|
|
number of queries completed approaches the number of all queries started. |
|
162
|
|
|
|
|
|
|
Defaults to 0.2 * timeout_initial. |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=back |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
C<$ent> is returned by this method, with its contents augmented by additional |
|
167
|
|
|
|
|
|
|
information. |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=cut |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
my ($self, $ent, $master_deadline) = @_; |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
my $id = $ent->{id}; |
|
174
|
|
|
|
|
|
|
my $key = $ent->{key}; |
|
175
|
13
|
|
|
13
|
1
|
24
|
defined $id && $id ne '' or die "oops, no id"; |
|
176
|
|
|
|
|
|
|
$key or die "oops, no key"; |
|
177
|
13
|
|
|
|
|
20
|
$ent->{type} or die "oops, no type"; |
|
178
|
13
|
|
|
|
|
22
|
|
|
179
|
13
|
50
|
33
|
|
|
61
|
my $now = time; |
|
180
|
13
|
50
|
|
|
|
29
|
$ent->{start_time} = $now if !defined $ent->{start_time}; |
|
181
|
13
|
50
|
|
|
|
25
|
|
|
182
|
|
|
|
|
|
|
# are there any applicable per-zone settings? |
|
183
|
13
|
|
|
|
|
708
|
my $zone = $ent->{zone}; |
|
184
|
13
|
50
|
|
|
|
33
|
my $settings; # a ref to a by-zone or to global settings |
|
185
|
|
|
|
|
|
|
my $conf_by_zone = $self->{main}->{conf}->{by_zone}; |
|
186
|
|
|
|
|
|
|
if (defined $zone && $conf_by_zone) { |
|
187
|
13
|
|
|
|
|
23
|
# dbg("async: searching for by_zone settings for $zone"); |
|
188
|
13
|
|
|
|
|
19
|
$zone =~ s/^\.//; $zone =~ s/\.\z//; # strip leading and trailing dot |
|
189
|
13
|
|
|
|
|
22
|
for (;;) { # 2.10.example.com, 10.example.com, example.com, com, '' |
|
190
|
13
|
50
|
33
|
|
|
62
|
if (exists $conf_by_zone->{$zone}) { |
|
191
|
|
|
|
|
|
|
$settings = $conf_by_zone->{$zone}; |
|
192
|
0
|
|
|
|
|
0
|
last; |
|
|
0
|
|
|
|
|
0
|
|
|
193
|
0
|
|
|
|
|
0
|
} elsif ($zone eq '') { |
|
194
|
0
|
0
|
|
|
|
0
|
last; |
|
|
|
0
|
|
|
|
|
|
|
195
|
0
|
|
|
|
|
0
|
} else { # strip one level, careful with address literals |
|
196
|
0
|
|
|
|
|
0
|
$zone = ($zone =~ /^( (?: [^.] | \[ (?: \\. | [^\]\\] )* \] )* ) |
|
197
|
|
|
|
|
|
|
\. (.*) \z/xs) ? $2 : ''; |
|
198
|
0
|
|
|
|
|
0
|
} |
|
199
|
|
|
|
|
|
|
} |
|
200
|
0
|
0
|
|
|
|
0
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
dbg("async: applying by_zone settings for %s", $zone) if $settings; |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
my $t_init = $ent->{timeout_initial}; # application-specified has precedence |
|
205
|
|
|
|
|
|
|
$t_init = $settings->{rbl_timeout} if $settings && !defined $t_init; |
|
206
|
13
|
50
|
|
|
|
23
|
$t_init = $self->{main}->{conf}->{rbl_timeout} if !defined $t_init; |
|
207
|
|
|
|
|
|
|
$t_init = 0 if !defined $t_init; # last-resort default, just in case |
|
208
|
13
|
|
|
|
|
23
|
|
|
209
|
13
|
50
|
33
|
|
|
25
|
my $t_end = $ent->{timeout_min}; # application-specified has precedence |
|
210
|
13
|
50
|
|
|
|
32
|
$t_end = $settings->{rbl_timeout_min} if $settings && !defined $t_end; |
|
211
|
13
|
50
|
|
|
|
28
|
$t_end = $self->{main}->{conf}->{rbl_timeout_min} if !defined $t_end; # added for bug 7070 |
|
212
|
|
|
|
|
|
|
$t_end = 0.2 * $t_init if !defined $t_end; |
|
213
|
13
|
|
|
|
|
20
|
$t_end = 0 if $t_end < 0; # just in case |
|
214
|
13
|
50
|
33
|
|
|
24
|
$t_init = $t_end if $t_init < $t_end; |
|
215
|
13
|
50
|
|
|
|
24
|
|
|
216
|
13
|
50
|
|
|
|
546
|
my $clipped_by_master_deadline = 0; |
|
217
|
13
|
50
|
|
|
|
48
|
if (defined $master_deadline) { |
|
218
|
13
|
50
|
|
|
|
29
|
my $time_avail = $master_deadline - time; |
|
219
|
|
|
|
|
|
|
$time_avail = 0.5 if $time_avail < 0.5; # give some slack |
|
220
|
13
|
|
|
|
|
16
|
if ($t_init > $time_avail) { |
|
221
|
13
|
50
|
|
|
|
22
|
$t_init = $time_avail; $clipped_by_master_deadline = 1; |
|
222
|
13
|
|
|
|
|
23
|
$t_end = $time_avail if $t_end > $time_avail; |
|
223
|
13
|
50
|
|
|
|
26
|
} |
|
224
|
13
|
50
|
|
|
|
31
|
} |
|
225
|
0
|
|
|
|
|
0
|
$ent->{timeout_initial} = $t_init; |
|
|
0
|
|
|
|
|
0
|
|
|
226
|
0
|
0
|
|
|
|
0
|
$ent->{timeout_min} = $t_end; |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
$ent->{display_id} = # identifies entry in debug logging and similar |
|
229
|
13
|
|
|
|
|
25
|
join(", ", grep { defined } |
|
230
|
13
|
|
|
|
|
23
|
map { ref $ent->{$_} ? @{$ent->{$_}} : $ent->{$_} } |
|
231
|
|
|
|
|
|
|
qw(sets rules rulename type key) ); |
|
232
|
|
|
|
|
|
|
|
|
233
|
65
|
|
|
|
|
134
|
$self->{pending_lookups}->{$key} = $ent; |
|
234
|
13
|
50
|
|
|
|
22
|
|
|
|
65
|
|
|
|
|
139
|
|
|
|
0
|
|
|
|
|
0
|
|
|
235
|
|
|
|
|
|
|
$self->{queries_started}++; |
|
236
|
|
|
|
|
|
|
$self->{total_queries_started}++; |
|
237
|
13
|
|
|
|
|
35
|
dbg("async: starting: %s (timeout %.1fs, min %.1fs)%s", |
|
238
|
|
|
|
|
|
|
$ent->{display_id}, $ent->{timeout_initial}, $ent->{timeout_min}, |
|
239
|
13
|
|
|
|
|
17
|
!$clipped_by_master_deadline ? '' : ', capped by time limit'); |
|
240
|
13
|
|
|
|
|
14
|
|
|
241
|
|
|
|
|
|
|
$ent; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
13
|
50
|
|
|
|
44
|
|
|
244
|
|
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
245
|
13
|
|
|
|
|
56
|
|
|
246
|
|
|
|
|
|
|
=item $ent = $async->bgsend_and_start_lookup($domain, $type, $class, $ent, $cb, %options) |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
A common idiom: calls C<bgsend>, followed by a call to C<start_lookup>, |
|
249
|
|
|
|
|
|
|
returning the argument $ent object as modified by C<start_lookup> and |
|
250
|
|
|
|
|
|
|
filled-in with a query ID. |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=cut |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
my($self, $domain, $type, $class, $ent, $cb, %options) = @_; |
|
255
|
|
|
|
|
|
|
$ent = {} if !$ent; |
|
256
|
|
|
|
|
|
|
$domain =~ s/\.+\z//s; # strip trailing dots, these sometimes still sneak in |
|
257
|
|
|
|
|
|
|
$ent->{id} = undef; |
|
258
|
|
|
|
|
|
|
$ent->{query_type} = $type; |
|
259
|
135
|
|
|
135
|
1
|
468
|
$ent->{query_domain} = $domain; |
|
260
|
135
|
50
|
|
|
|
262
|
$ent->{type} = $type if !exists $ent->{type}; |
|
261
|
135
|
|
|
|
|
376
|
$cb = $ent->{completed_callback} if !$cb; # compatibility with SA < 3.4 |
|
262
|
135
|
|
|
|
|
180
|
|
|
263
|
135
|
|
|
|
|
222
|
my $key = $ent->{key} || ''; |
|
264
|
135
|
|
|
|
|
310
|
|
|
265
|
135
|
50
|
|
|
|
233
|
my $dnskey = uc($type) . '/' . lc($domain); |
|
266
|
135
|
50
|
|
|
|
176
|
my $dns_query_info = $self->{all_lookups}{$dnskey}; |
|
267
|
|
|
|
|
|
|
|
|
268
|
135
|
|
50
|
|
|
290
|
if ($dns_query_info) { # DNS query already underway or completed |
|
269
|
|
|
|
|
|
|
my $id = $ent->{id} = $dns_query_info->{id}; # re-use existing query |
|
270
|
135
|
|
|
|
|
347
|
return if !defined $id; # presumably blocked, or other fatal failure |
|
271
|
135
|
|
|
|
|
205
|
my $id_tail = $id; $id_tail =~ s{^\d+/IN/}{}; |
|
272
|
|
|
|
|
|
|
lc($id_tail) eq lc($dnskey) |
|
273
|
135
|
100
|
|
|
|
203
|
or info("async: unmatched id %s, key=%s", $id, $dnskey); |
|
274
|
122
|
|
|
|
|
207
|
|
|
275
|
122
|
50
|
|
|
|
210
|
my $pkt = $dns_query_info->{pkt}; |
|
276
|
122
|
|
|
|
|
159
|
if (!$pkt) { # DNS query underway, still waiting for results |
|
|
122
|
|
|
|
|
475
|
|
|
277
|
122
|
50
|
|
|
|
339
|
# just add our query to the existing one |
|
278
|
|
|
|
|
|
|
push(@{$dns_query_info->{applicants}}, [$ent,$cb]); |
|
279
|
|
|
|
|
|
|
dbg("async: query %s already underway, adding no.%d %s", |
|
280
|
122
|
|
|
|
|
189
|
$id, scalar @{$dns_query_info->{applicants}}, |
|
281
|
122
|
50
|
|
|
|
168
|
$ent->{rulename} || $key); |
|
282
|
|
|
|
|
|
|
|
|
283
|
122
|
|
|
|
|
130
|
} else { # DNS query already completed, re-use results |
|
|
122
|
|
|
|
|
311
|
|
|
284
|
|
|
|
|
|
|
# answer already known, just do the callback and be done with it |
|
285
|
122
|
|
|
|
|
305
|
if (!$cb) { |
|
286
|
122
|
|
33
|
|
|
148
|
dbg("async: query %s already done, re-using for %s", $id, $key); |
|
287
|
|
|
|
|
|
|
} else { |
|
288
|
|
|
|
|
|
|
dbg("async: query %s already done, re-using for %s, callback", |
|
289
|
|
|
|
|
|
|
$id, $key); |
|
290
|
0
|
0
|
|
|
|
0
|
eval { |
|
291
|
0
|
|
|
|
|
0
|
$cb->($ent, $pkt); 1; |
|
292
|
|
|
|
|
|
|
} or do { |
|
293
|
0
|
|
|
|
|
0
|
chomp $@; |
|
294
|
|
|
|
|
|
|
# resignal if alarm went off |
|
295
|
|
|
|
|
|
|
die "async: (1) $@\n" if $@ =~ /__alarm__ignore__\(.*\)/s; |
|
296
|
0
|
|
|
|
|
0
|
warn sprintf("query %s completed, callback %s failed: %s\n", |
|
|
0
|
|
|
|
|
0
|
|
|
297
|
0
|
0
|
|
|
|
0
|
$id, $key, $@); |
|
298
|
0
|
|
|
|
|
0
|
}; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
0
|
0
|
|
|
|
0
|
} |
|
301
|
0
|
|
|
|
|
0
|
} |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
else { # no existing query, open a new DNS query |
|
304
|
|
|
|
|
|
|
$dns_query_info = $self->{all_lookups}{$dnskey} = {}; # new query needed |
|
305
|
|
|
|
|
|
|
my($id, $blocked); |
|
306
|
|
|
|
|
|
|
my $dns_query_blockages = $self->{main}->{conf}->{dns_query_blocked}; |
|
307
|
|
|
|
|
|
|
if ($dns_query_blockages) { |
|
308
|
|
|
|
|
|
|
my $search_list = domain_to_search_list($domain); |
|
309
|
13
|
|
|
|
|
26
|
foreach my $parent_domain (@$search_list) { |
|
310
|
13
|
|
|
|
|
18
|
$blocked = $dns_query_blockages->{$parent_domain}; |
|
311
|
13
|
|
|
|
|
24
|
last if defined $blocked; # stop at first defined, can be true or false |
|
312
|
13
|
50
|
|
|
|
26
|
} |
|
313
|
0
|
|
|
|
|
0
|
} |
|
314
|
0
|
|
|
|
|
0
|
if ($blocked) { |
|
315
|
0
|
|
|
|
|
0
|
dbg("async: blocked by dns_query_restriction: %s", $dnskey); |
|
316
|
0
|
0
|
|
|
|
0
|
} else { |
|
317
|
|
|
|
|
|
|
dbg("async: launching %s for %s", $dnskey, $key); |
|
318
|
|
|
|
|
|
|
$id = $self->{main}->{resolver}->bgsend($domain, $type, $class, sub { |
|
319
|
13
|
50
|
|
|
|
19
|
my($pkt, $pkt_id, $timestamp) = @_; |
|
320
|
0
|
|
|
|
|
0
|
# this callback sub is called from DnsResolver::poll_responses() |
|
321
|
|
|
|
|
|
|
# dbg("async: in a bgsend_and_start_lookup callback, id %s", $pkt_id); |
|
322
|
13
|
|
|
|
|
31
|
if ($pkt_id ne $id) { |
|
323
|
|
|
|
|
|
|
warn "async: mismatched dns id: got $pkt_id, expected $id\n"; |
|
324
|
13
|
|
|
13
|
|
33
|
return; |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
$self->set_response_packet($pkt_id, $pkt, $ent->{key}, $timestamp); |
|
327
|
13
|
50
|
|
|
|
34
|
$dns_query_info->{pkt} = $pkt; |
|
328
|
0
|
|
|
|
|
0
|
my $cb_count = 0; |
|
329
|
0
|
|
|
|
|
0
|
foreach my $tuple (@{$dns_query_info->{applicants}}) { |
|
330
|
|
|
|
|
|
|
my($appl_ent, $appl_cb) = @$tuple; |
|
331
|
13
|
|
|
|
|
41
|
if ($appl_cb) { |
|
332
|
13
|
|
|
|
|
19
|
dbg("async: calling callback on key %s%s", $key, |
|
333
|
13
|
|
|
|
|
14
|
!defined $appl_ent->{rulename} ? '' |
|
334
|
13
|
|
|
|
|
13
|
: ", rule ".$appl_ent->{rulename}); |
|
|
13
|
|
|
|
|
25
|
|
|
335
|
135
|
|
|
|
|
254
|
$cb_count++; |
|
336
|
135
|
50
|
|
|
|
229
|
eval { |
|
337
|
|
|
|
|
|
|
$appl_cb->($appl_ent, $pkt); 1; |
|
338
|
|
|
|
|
|
|
} or do { |
|
339
|
135
|
50
|
|
|
|
459
|
chomp $@; |
|
340
|
135
|
|
|
|
|
163
|
# resignal if alarm went off |
|
341
|
|
|
|
|
|
|
die "async: (2) $@\n" if $@ =~ /__alarm__ignore__\(.*\)/s; |
|
342
|
135
|
|
|
|
|
266
|
warn sprintf("query %s completed, callback %s failed: %s\n", |
|
|
135
|
|
|
|
|
335
|
|
|
343
|
135
|
50
|
|
|
|
165
|
$id, $appl_ent->{key}, $@); |
|
344
|
0
|
|
|
|
|
0
|
}; |
|
345
|
|
|
|
|
|
|
} |
|
346
|
0
|
0
|
|
|
|
0
|
} |
|
347
|
|
|
|
|
|
|
delete $dns_query_info->{applicants}; |
|
348
|
0
|
|
|
|
|
0
|
dbg("async: query $id completed, no callbacks run") if !$cb_count; |
|
349
|
|
|
|
|
|
|
}); |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
return if !defined $id; |
|
352
|
13
|
|
|
|
|
230
|
$dns_query_info->{id} = $ent->{id} = $id; |
|
353
|
13
|
50
|
|
|
|
43
|
push(@{$dns_query_info->{applicants}}, [$ent,$cb]); |
|
354
|
13
|
|
|
|
|
97
|
$self->start_lookup($ent, $options{master_deadline}); |
|
355
|
|
|
|
|
|
|
} |
|
356
|
13
|
50
|
|
|
|
35
|
return $ent; |
|
357
|
13
|
|
|
|
|
34
|
} |
|
358
|
13
|
|
|
|
|
17
|
|
|
|
13
|
|
|
|
|
37
|
|
|
359
|
13
|
|
|
|
|
31
|
# --------------------------------------------------------------------------- |
|
360
|
|
|
|
|
|
|
|
|
361
|
135
|
|
|
|
|
412
|
=item $ent = $async->get_lookup($key) |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Retrieve the pending-lookup object for the given key C<$key>. |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
If the lookup is complete, this will return C<undef>. |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Note that a lookup is still considered "pending" until C<complete_lookups()> is |
|
368
|
|
|
|
|
|
|
called, even if it has been reported as complete via C<set_response_packet()>. |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
my ($self, $key) = @_; |
|
373
|
|
|
|
|
|
|
return $self->{pending_lookups}->{$key}; |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
377
|
|
|
|
|
|
|
|
|
378
|
0
|
|
|
0
|
1
|
0
|
=item $async->log_lookups_timing() |
|
379
|
0
|
|
|
|
|
0
|
|
|
380
|
|
|
|
|
|
|
Log sorted timing for all completed lookups. |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=cut |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
my ($self) = @_; |
|
385
|
|
|
|
|
|
|
my $timings = $self->{timing_by_query}; |
|
386
|
|
|
|
|
|
|
for my $key (sort { $timings->{$a} <=> $timings->{$b} } keys %$timings) { |
|
387
|
|
|
|
|
|
|
dbg("async: timing: %.3f %s", $timings->{$key}, $key); |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
|
|
391
|
96
|
|
|
96
|
1
|
210
|
# --------------------------------------------------------------------------- |
|
392
|
96
|
|
|
|
|
182
|
|
|
393
|
96
|
|
|
|
|
411
|
=item $alldone = $async->complete_lookups() |
|
|
19
|
|
|
|
|
30
|
|
|
394
|
13
|
|
|
|
|
19
|
|
|
395
|
|
|
|
|
|
|
Perform a poll of the pending lookups, to see if any are completed. |
|
396
|
|
|
|
|
|
|
Callbacks on completed queries will be called from poll_responses(). |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
If there are no lookups remaining, or if too much time has elapsed since |
|
399
|
|
|
|
|
|
|
any results were returned, C<1> is returned, otherwise C<0>. |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=cut |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
my ($self, $timeout, $allow_aborting_of_expired) = @_; |
|
404
|
|
|
|
|
|
|
my $alldone = 0; |
|
405
|
|
|
|
|
|
|
my $anydone = 0; |
|
406
|
|
|
|
|
|
|
my $allexpired = 1; |
|
407
|
|
|
|
|
|
|
my %typecount; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
my $pending = $self->{pending_lookups}; |
|
410
|
|
|
|
|
|
|
$self->{queries_started} = 0; |
|
411
|
3159
|
|
|
3159
|
1
|
4509
|
$self->{queries_completed} = 0; |
|
412
|
3159
|
|
|
|
|
3628
|
|
|
413
|
3159
|
|
|
|
|
3236
|
my $now = time; |
|
414
|
3159
|
|
|
|
|
3264
|
|
|
415
|
3159
|
|
|
|
|
3420
|
if (defined $timeout && $timeout > 0 && |
|
416
|
|
|
|
|
|
|
%$pending && $self->{total_queries_started} > 0) |
|
417
|
3159
|
|
|
|
|
3729
|
{ |
|
418
|
3159
|
|
|
|
|
3586
|
# shrink a 'select' timeout if a caller specified unnecessarily long |
|
419
|
3159
|
|
|
|
|
3395
|
# value beyond the latest deadline of any outstanding request; |
|
420
|
|
|
|
|
|
|
# can save needless wait time (up to 1 second in harvest_dnsbl_queries) |
|
421
|
3159
|
|
|
|
|
5652
|
my $r = $self->{total_queries_completed} / $self->{total_queries_started}; |
|
422
|
|
|
|
|
|
|
my $r2 = $r * $r; # 0..1 |
|
423
|
3159
|
0
|
33
|
|
|
9804
|
my $max_deadline; |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
424
|
|
|
|
|
|
|
while (my($key,$ent) = each %$pending) { |
|
425
|
|
|
|
|
|
|
my $t_init = $ent->{timeout_initial}; |
|
426
|
|
|
|
|
|
|
my $dt = $t_init - ($t_init - $ent->{timeout_min}) * $r2; |
|
427
|
|
|
|
|
|
|
my $deadline = $ent->{start_time} + $dt; |
|
428
|
|
|
|
|
|
|
$max_deadline = $deadline if !defined $max_deadline || |
|
429
|
0
|
|
|
|
|
0
|
$deadline > $max_deadline; |
|
430
|
0
|
|
|
|
|
0
|
} |
|
431
|
0
|
|
|
|
|
0
|
if (defined $max_deadline) { |
|
432
|
0
|
|
|
|
|
0
|
# adjust to timer resolution, only deals with 1s and with fine resolution |
|
433
|
0
|
|
|
|
|
0
|
$max_deadline = 1 + int $max_deadline |
|
434
|
0
|
|
|
|
|
0
|
if $timer_resolution == 1 && $max_deadline > int $max_deadline; |
|
435
|
0
|
|
|
|
|
0
|
my $sufficient_timeout = $max_deadline - $now; |
|
436
|
0
|
0
|
0
|
|
|
0
|
$sufficient_timeout = 0 if $sufficient_timeout < 0; |
|
437
|
|
|
|
|
|
|
if ($timeout > $sufficient_timeout) { |
|
438
|
|
|
|
|
|
|
dbg("async: reducing select timeout from %.1f to %.1f s", |
|
439
|
0
|
0
|
|
|
|
0
|
$timeout, $sufficient_timeout); |
|
440
|
|
|
|
|
|
|
$timeout = $sufficient_timeout; |
|
441
|
0
|
0
|
0
|
|
|
0
|
} |
|
442
|
|
|
|
|
|
|
} |
|
443
|
0
|
|
|
|
|
0
|
} |
|
444
|
0
|
0
|
|
|
|
0
|
|
|
445
|
0
|
0
|
|
|
|
0
|
# trap this loop in an eval { } block, as Net::DNS could throw |
|
446
|
0
|
|
|
|
|
0
|
# die()s our way; in particular, process_dnsbl_results() has |
|
447
|
|
|
|
|
|
|
# thrown die()s before (bug 3794). |
|
448
|
0
|
|
|
|
|
0
|
eval { |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
if (%$pending) { # any outstanding requests still? |
|
451
|
|
|
|
|
|
|
$self->{last_poll_responses_time} = $now; |
|
452
|
|
|
|
|
|
|
my $nfound = $self->{main}->{resolver}->poll_responses($timeout); |
|
453
|
|
|
|
|
|
|
dbg("async: select found %s responses ready (t.o.=%.1f)", |
|
454
|
|
|
|
|
|
|
!$nfound ? 'no' : $nfound, $timeout); |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
$now = time; # capture new timestamp, after possible sleep in 'select' |
|
457
|
|
|
|
|
|
|
|
|
458
|
3159
|
100
|
|
|
|
5238
|
# A callback routine may generate another DNS query, which may insert |
|
459
|
3
|
|
|
|
|
6
|
# an entry into the %$pending hash thus invalidating the each() context. |
|
460
|
3
|
|
|
|
|
12
|
# So, make sure that callbacks are not called while the each() context |
|
461
|
3
|
50
|
|
|
|
14
|
# is open. [Bug 6937] |
|
462
|
|
|
|
|
|
|
# |
|
463
|
|
|
|
|
|
|
while (my($key,$ent) = each %$pending) { |
|
464
|
3159
|
|
|
|
|
4454
|
my $id = $ent->{id}; |
|
465
|
|
|
|
|
|
|
if (exists $self->{finished}->{$id}) { |
|
466
|
|
|
|
|
|
|
delete $self->{finished}->{$id}; |
|
467
|
|
|
|
|
|
|
$anydone = 1; |
|
468
|
|
|
|
|
|
|
$ent->{finish_time} = $now if !defined $ent->{finish_time}; |
|
469
|
|
|
|
|
|
|
my $elapsed = $ent->{finish_time} - $ent->{start_time}; |
|
470
|
|
|
|
|
|
|
dbg("async: completed in %.3f s: %s", $elapsed, $ent->{display_id}); |
|
471
|
3159
|
|
|
|
|
7801
|
$self->{timing_by_query}->{". $key"} += $elapsed; |
|
472
|
13
|
|
|
|
|
24
|
$self->{queries_completed}++; |
|
473
|
13
|
50
|
|
|
|
30
|
$self->{total_queries_completed}++; |
|
474
|
13
|
|
|
|
|
24
|
delete $pending->{$key}; |
|
475
|
13
|
|
|
|
|
15
|
} |
|
476
|
13
|
50
|
|
|
|
21
|
} |
|
477
|
13
|
|
|
|
|
16
|
|
|
478
|
13
|
|
|
|
|
26
|
if (%$pending) { # still any requests outstanding? are they expired? |
|
479
|
13
|
|
|
|
|
30
|
my $r = |
|
480
|
13
|
|
|
|
|
15
|
!$allow_aborting_of_expired || !$self->{total_queries_started} ? 1.0 |
|
481
|
13
|
|
|
|
|
14
|
: $self->{total_queries_completed} / $self->{total_queries_started}; |
|
482
|
13
|
|
|
|
|
66
|
my $r2 = $r * $r; # 0..1 |
|
483
|
|
|
|
|
|
|
while (my($key,$ent) = each %$pending) { |
|
484
|
|
|
|
|
|
|
$typecount{$ent->{type}}++; |
|
485
|
|
|
|
|
|
|
my $t_init = $ent->{timeout_initial}; |
|
486
|
3159
|
50
|
|
|
|
5089
|
my $dt = $t_init - ($t_init - $ent->{timeout_min}) * $r2; |
|
487
|
|
|
|
|
|
|
# adjust to timer resolution, only deals with 1s and fine resolution |
|
488
|
|
|
|
|
|
|
$dt = 1 + int $dt if $timer_resolution == 1 && $dt > int $dt; |
|
489
|
0
|
0
|
0
|
|
|
0
|
$allexpired = 0 if $now <= $ent->{start_time} + $dt; |
|
490
|
0
|
|
|
|
|
0
|
} |
|
491
|
0
|
|
|
|
|
0
|
dbg("async: queries completed: %d, started: %d", |
|
492
|
0
|
|
|
|
|
0
|
$self->{queries_completed}, $self->{queries_started}); |
|
493
|
0
|
|
|
|
|
0
|
} |
|
494
|
0
|
|
|
|
|
0
|
|
|
495
|
|
|
|
|
|
|
# ensure we don't get stuck if a request gets lost in the ether. |
|
496
|
0
|
0
|
0
|
|
|
0
|
if (! %$pending) { |
|
497
|
0
|
0
|
|
|
|
0
|
$alldone = 1; |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
elsif ($allexpired && $allow_aborting_of_expired) { |
|
500
|
0
|
|
|
|
|
0
|
# avoid looping forever if we haven't got all results. |
|
501
|
|
|
|
|
|
|
dbg("async: escaping: lost or timed out requests or responses"); |
|
502
|
|
|
|
|
|
|
$self->abort_remaining_lookups(); |
|
503
|
|
|
|
|
|
|
$alldone = 1; |
|
504
|
3159
|
50
|
0
|
|
|
4799
|
} |
|
|
|
0
|
|
|
|
|
|
|
505
|
3159
|
|
|
|
|
3743
|
else { |
|
506
|
|
|
|
|
|
|
dbg("async: queries active: %s%s at %s", |
|
507
|
|
|
|
|
|
|
join (' ', map { "$_=$typecount{$_}" } sort keys %typecount), |
|
508
|
|
|
|
|
|
|
$allexpired ? ', all expired' : '', scalar(localtime(time))); |
|
509
|
0
|
|
|
|
|
0
|
$alldone = 0; |
|
510
|
0
|
|
|
|
|
0
|
} |
|
511
|
0
|
|
|
|
|
0
|
1; |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
} or do { |
|
514
|
|
|
|
|
|
|
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; |
|
515
|
0
|
0
|
|
|
|
0
|
# resignal if alarm went off |
|
|
0
|
|
|
|
|
0
|
|
|
516
|
|
|
|
|
|
|
die "async: (3) $eval_stat\n" if $eval_stat =~ /__alarm__ignore__\(.*\)/s; |
|
517
|
0
|
|
|
|
|
0
|
dbg("async: caught complete_lookups death, aborting: %s", $eval_stat); |
|
518
|
|
|
|
|
|
|
$alldone = 1; # abort remaining |
|
519
|
3159
|
|
|
|
|
5676
|
}; |
|
520
|
|
|
|
|
|
|
|
|
521
|
3159
|
50
|
|
|
|
3908
|
return wantarray ? ($alldone,$anydone) : $alldone; |
|
522
|
0
|
0
|
|
|
|
0
|
} |
|
|
0
|
|
|
|
|
0
|
|
|
523
|
|
|
|
|
|
|
|
|
524
|
0
|
0
|
|
|
|
0
|
# --------------------------------------------------------------------------- |
|
525
|
0
|
|
|
|
|
0
|
|
|
526
|
0
|
|
|
|
|
0
|
=item $async->abort_remaining_lookups() |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Abort any remaining lookups. |
|
529
|
3159
|
50
|
|
|
|
10011
|
|
|
530
|
|
|
|
|
|
|
=cut |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
my ($self) = @_; |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
my $pending = $self->{pending_lookups}; |
|
535
|
|
|
|
|
|
|
my $foundcnt = 0; |
|
536
|
|
|
|
|
|
|
my $now = time; |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
while (my($key,$ent) = each %$pending) { |
|
539
|
|
|
|
|
|
|
dbg("async: aborting after %.3f s, %s: %s", |
|
540
|
|
|
|
|
|
|
$now - $ent->{start_time}, |
|
541
|
96
|
|
|
96
|
1
|
249
|
(defined $ent->{timeout_initial} && |
|
542
|
|
|
|
|
|
|
$now > $ent->{start_time} + $ent->{timeout_initial} |
|
543
|
96
|
|
|
|
|
204
|
? 'past original deadline' : 'deadline shrunk'), |
|
544
|
96
|
|
|
|
|
201
|
$ent->{display_id} ); |
|
545
|
96
|
|
|
|
|
230
|
$foundcnt++; |
|
546
|
|
|
|
|
|
|
$self->{timing_by_query}->{"X $key"} = $now - $ent->{start_time}; |
|
547
|
96
|
|
|
|
|
336
|
$ent->{finish_time} = $now if !defined $ent->{finish_time}; |
|
548
|
|
|
|
|
|
|
delete $pending->{$key}; |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# call any remaining callbacks, indicating the query has been aborted |
|
552
|
|
|
|
|
|
|
# |
|
553
|
0
|
0
|
0
|
|
|
0
|
my $all_lookups_ref = $self->{all_lookups}; |
|
554
|
0
|
|
|
|
|
0
|
foreach my $dnskey (keys %$all_lookups_ref) { |
|
555
|
0
|
|
|
|
|
0
|
my $dns_query_info = $all_lookups_ref->{$dnskey}; |
|
556
|
0
|
0
|
|
|
|
0
|
my $cb_count = 0; |
|
557
|
0
|
|
|
|
|
0
|
foreach my $tuple (@{$dns_query_info->{applicants}}) { |
|
558
|
|
|
|
|
|
|
my($ent, $cb) = @$tuple; |
|
559
|
|
|
|
|
|
|
if ($cb) { |
|
560
|
|
|
|
|
|
|
dbg("async: calling callback/abort on key %s%s", $dnskey, |
|
561
|
|
|
|
|
|
|
!defined $ent->{rulename} ? '' : ", rule ".$ent->{rulename}); |
|
562
|
96
|
|
|
|
|
199
|
$cb_count++; |
|
563
|
96
|
|
|
|
|
301
|
eval { |
|
564
|
13
|
|
|
|
|
17
|
$cb->($ent, undef); 1; |
|
565
|
13
|
|
|
|
|
13
|
} or do { |
|
566
|
13
|
|
|
|
|
12
|
chomp $@; |
|
|
13
|
|
|
|
|
21
|
|
|
567
|
0
|
|
|
|
|
0
|
# resignal if alarm went off |
|
568
|
0
|
0
|
|
|
|
0
|
die "async: (2) $@\n" if $@ =~ /__alarm__ignore__\(.*\)/s; |
|
569
|
|
|
|
|
|
|
warn sprintf("query %s aborted, callback %s failed: %s\n", |
|
570
|
0
|
0
|
|
|
|
0
|
$dnskey, $ent->{key}, $@); |
|
571
|
0
|
|
|
|
|
0
|
}; |
|
572
|
|
|
|
|
|
|
} |
|
573
|
0
|
|
|
|
|
0
|
dbg("async: query $dnskey aborted, no callbacks run") if !$cb_count; |
|
|
0
|
|
|
|
|
0
|
|
|
574
|
0
|
0
|
|
|
|
0
|
} |
|
575
|
0
|
|
|
|
|
0
|
delete $dns_query_info->{applicants}; |
|
576
|
|
|
|
|
|
|
} |
|
577
|
0
|
0
|
|
|
|
0
|
|
|
578
|
|
|
|
|
|
|
dbg("async: aborted %d remaining lookups", $foundcnt) if $foundcnt > 0; |
|
579
|
0
|
|
|
|
|
0
|
delete $self->{last_poll_responses_time}; |
|
580
|
|
|
|
|
|
|
$self->{main}->{resolver}->bgabort(); |
|
581
|
|
|
|
|
|
|
1; |
|
582
|
0
|
0
|
|
|
|
0
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
13
|
|
|
|
|
23
|
# --------------------------------------------------------------------------- |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=item $async->set_response_packet($id, $pkt, $key, $timestamp) |
|
587
|
96
|
50
|
|
|
|
302
|
|
|
588
|
96
|
|
|
|
|
172
|
Register a "response packet" for a given query. C<$id> is the ID for the |
|
589
|
96
|
|
|
|
|
665
|
query, and must match the C<id> supplied in C<start_lookup()>. C<$pkt> is the |
|
590
|
96
|
|
|
|
|
210
|
packet object for the response. A parameter C<$key> identifies an entry in a |
|
591
|
|
|
|
|
|
|
hash %{$self->{pending_lookups}} where the object which spawned this query can |
|
592
|
|
|
|
|
|
|
be found, and through which further information about the query is accessible. |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
C<$pkt> may be undef, indicating that no response packet is available, but a |
|
595
|
|
|
|
|
|
|
query has completed (e.g. was aborted or dismissed) and is no longer "pending". |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
The DNS resolver's response packet C<$pkt> will be made available to a callback |
|
598
|
|
|
|
|
|
|
subroutine through its argument as well as in C<$ent-<gt>{response_packet}>. |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=cut |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my ($self, $id, $pkt, $key, $timestamp) = @_; |
|
603
|
|
|
|
|
|
|
$self->{finished}->{$id} = 1; # only key existence matters, any value |
|
604
|
|
|
|
|
|
|
$timestamp = time if !defined $timestamp; |
|
605
|
|
|
|
|
|
|
my $pending = $self->{pending_lookups}; |
|
606
|
|
|
|
|
|
|
if (!defined $key) { # backward compatibility with 3.2.3 and older plugins |
|
607
|
|
|
|
|
|
|
# a third-party plugin did not provide $key in a call, search for it: |
|
608
|
|
|
|
|
|
|
if ($id eq $pending->{$id}->{id}) { # I feel lucky, key==id ? |
|
609
|
|
|
|
|
|
|
$key = $id; |
|
610
|
|
|
|
|
|
|
} else { # then again, maybe not, be more systematic |
|
611
|
|
|
|
|
|
|
for my $tkey (keys %$pending) { |
|
612
|
13
|
|
|
13
|
1
|
31
|
if ($id eq $pending->{$tkey}->{id}) { $key = $tkey; last } |
|
613
|
13
|
|
|
|
|
33
|
} |
|
614
|
13
|
50
|
|
|
|
24
|
} |
|
615
|
13
|
|
|
|
|
19
|
dbg("async: got response on id $id, search found key $key"); |
|
616
|
13
|
50
|
|
|
|
48
|
} |
|
617
|
|
|
|
|
|
|
if (!defined $key) { |
|
618
|
0
|
0
|
|
|
|
0
|
info("async: no key, response packet not remembered, id $id"); |
|
619
|
0
|
|
|
|
|
0
|
} else { |
|
620
|
|
|
|
|
|
|
my $ent = $pending->{$key}; |
|
621
|
0
|
|
|
|
|
0
|
my $ent_id = $ent->{id}; |
|
622
|
0
|
0
|
|
|
|
0
|
if (!defined $ent_id) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
623
|
|
|
|
|
|
|
# should not happen, troubleshooting |
|
624
|
|
|
|
|
|
|
info("async: ignoring response, id %s, ent_id is undef: %s", |
|
625
|
0
|
|
|
|
|
0
|
$id, join(', ', %$ent)); |
|
626
|
|
|
|
|
|
|
} elsif ($id ne $ent_id) { |
|
627
|
13
|
50
|
|
|
|
26
|
info("async: ignoring response, mismatched id $id, expected $ent_id"); |
|
628
|
0
|
|
|
|
|
0
|
} else { |
|
629
|
|
|
|
|
|
|
$ent->{finish_time} = $timestamp; |
|
630
|
13
|
|
|
|
|
19
|
$ent->{response_packet} = $pkt; |
|
631
|
13
|
|
|
|
|
22
|
} |
|
632
|
13
|
50
|
|
|
|
34
|
} |
|
|
|
50
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
1; |
|
634
|
0
|
|
|
|
|
0
|
} |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=item $async->report_id_complete($id,$key,$key,$timestamp) |
|
637
|
0
|
|
|
|
|
0
|
|
|
638
|
|
|
|
|
|
|
Legacy. Equivalent to $self->set_response_packet($id,undef,$key,$timestamp), |
|
639
|
13
|
|
|
|
|
23
|
i.e. providing undef as a response packet. Register that a query has |
|
640
|
13
|
|
|
|
|
24
|
completed and is no longer "pending". C<$id> is the ID for the query, |
|
641
|
|
|
|
|
|
|
and must match the C<id> supplied in C<start_lookup()>. |
|
642
|
|
|
|
|
|
|
|
|
643
|
13
|
|
|
|
|
22
|
One or the other of C<set_response_packet()> or C<report_id_complete()> |
|
644
|
|
|
|
|
|
|
should be called, but not both. |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=cut |
|
647
|
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
my ($self, $id, $key, $timestamp) = @_; |
|
649
|
|
|
|
|
|
|
$self->set_response_packet($id, undef, $key, $timestamp); |
|
650
|
|
|
|
|
|
|
} |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# --------------------------------------------------------------------------- |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item $time = $async->last_poll_responses_time() |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Get the time of the last call to C<poll_responses()> (which is called |
|
657
|
|
|
|
|
|
|
from C<complete_lookups()>. If C<poll_responses()> was never called or |
|
658
|
|
|
|
|
|
|
C<abort_remaining_lookups()> has been called C<last_poll_responses_time()> |
|
659
|
0
|
|
|
0
|
1
|
0
|
will return undef. |
|
660
|
0
|
|
|
|
|
0
|
|
|
661
|
|
|
|
|
|
|
=cut |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
my ($self) = @_; |
|
664
|
|
|
|
|
|
|
return $self->{last_poll_responses_time}; |
|
665
|
|
|
|
|
|
|
} |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
1; |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=back |
|
670
|
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=cut |