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 |