line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
AnyEvent::DNS - fully asynchronous DNS resolution |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use AnyEvent::DNS; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
my $cv = AnyEvent->condvar; |
10
|
|
|
|
|
|
|
AnyEvent::DNS::a "www.google.de", $cv; |
11
|
|
|
|
|
|
|
# ... later |
12
|
|
|
|
|
|
|
my @addrs = $cv->recv; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 DESCRIPTION |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
This module offers both a number of DNS convenience functions as well |
17
|
|
|
|
|
|
|
as a fully asynchronous and high-performance pure-perl stub resolver. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
The stub resolver supports DNS over IPv4 and IPv6, UDP and TCP, optional |
20
|
|
|
|
|
|
|
EDNS0 support for up to 4kiB datagrams and automatically falls back to |
21
|
|
|
|
|
|
|
virtual circuit mode for large responses. |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head2 CONVENIENCE FUNCTIONS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=over 4 |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=cut |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
package AnyEvent::DNS; |
30
|
|
|
|
|
|
|
|
31
|
8
|
|
|
8
|
|
2183
|
use Carp (); |
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
217
|
|
32
|
8
|
|
|
8
|
|
40
|
use Socket qw(AF_INET SOCK_DGRAM SOCK_STREAM); |
|
8
|
|
|
|
|
10
|
|
|
8
|
|
|
|
|
891
|
|
33
|
|
|
|
|
|
|
|
34
|
8
|
|
|
8
|
|
45
|
use AnyEvent (); BEGIN { AnyEvent::common_sense } |
|
8
|
|
|
8
|
|
13
|
|
|
8
|
|
|
|
|
137
|
|
|
8
|
|
|
|
|
34
|
|
35
|
8
|
|
|
8
|
|
42
|
use AnyEvent::Util qw(AF_INET6); |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
25582
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
our $VERSION = $AnyEvent::VERSION; |
38
|
|
|
|
|
|
|
our @DNS_FALLBACK; # some public dns servers as fallback |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
{ |
41
|
|
|
|
|
|
|
my $prep = sub { |
42
|
|
|
|
|
|
|
$_ = $_->[rand @$_] for @_; |
43
|
|
|
|
|
|
|
push @_, splice @_, rand $_, 1 for reverse 1..@_; # shuffle |
44
|
|
|
|
|
|
|
$_ = pack "H*", $_ for @_; |
45
|
|
|
|
|
|
|
\@_ |
46
|
|
|
|
|
|
|
}; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $ipv4 = $prep->( |
49
|
|
|
|
|
|
|
["08080808", "08080404"], # 8.8.8.8, 8.8.4.4 - google public dns |
50
|
|
|
|
|
|
|
["01010101", "01000001"], # 1.1.1.1, 1.0.0.1 - cloudflare public dns |
51
|
|
|
|
|
|
|
["50505050", "50505151"], # 80.80.80.80, 80.80.81.81 - freenom.world |
52
|
|
|
|
|
|
|
## ["d1f40003", "d1f30004"], # v209.244.0.3/4 - resolver1/2.level3.net - status unknown |
53
|
|
|
|
|
|
|
## ["04020201", "04020203", "04020204", "04020205", "04020206"], # v4.2.2.1/3/4/5/6 - vnsc-pri.sys.gtei.net - effectively public |
54
|
|
|
|
|
|
|
## ["cdd22ad2", "4044c8c8"], # 205.210.42.205, 64.68.200.200 - cache1/2.dnsresolvers.com - verified public |
55
|
|
|
|
|
|
|
# ["8d010101"], # 141.1.1.1 - cable&wireless, now vodafone - status unknown |
56
|
|
|
|
|
|
|
# 84.200.69.80 # dns.watch |
57
|
|
|
|
|
|
|
# 84.200.70.40 # dns.watch |
58
|
|
|
|
|
|
|
# 37.235.1.174 # freedns.zone |
59
|
|
|
|
|
|
|
# 37.235.1.177 # freedns.zone |
60
|
|
|
|
|
|
|
# 213.73.91.35 # dnscache.berlin.ccc.de |
61
|
|
|
|
|
|
|
# 194.150.168.168 # dns.as250.net; Berlin/Frankfurt |
62
|
|
|
|
|
|
|
# 85.214.20.141 # FoeBud (digitalcourage.de) |
63
|
|
|
|
|
|
|
# 77.109.148.136 # privacyfoundation.ch |
64
|
|
|
|
|
|
|
# 77.109.148.137 # privacyfoundation.ch |
65
|
|
|
|
|
|
|
# 91.239.100.100 # anycast.censurfridns.dk |
66
|
|
|
|
|
|
|
# 89.233.43.71 # ns1.censurfridns.dk |
67
|
|
|
|
|
|
|
# 204.152.184.76 # f.6to4-servers.net, ISC, USA |
68
|
|
|
|
|
|
|
); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
my $ipv6 = $prep->( |
71
|
|
|
|
|
|
|
["20014860486000000000000000008888", "20014860486000000000000000008844"], # 2001:4860:4860::8888/8844 - google ipv6 |
72
|
|
|
|
|
|
|
["26064700470000000000000000001111", "26064700470000000000000000001001"], # 2606:4700:4700::1111/1001 - cloudflare dns |
73
|
|
|
|
|
|
|
); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
undef $ipv4 unless $AnyEvent::PROTOCOL{ipv4}; |
76
|
|
|
|
|
|
|
undef $ipv6 unless $AnyEvent::PROTOCOL{ipv6}; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
($ipv6, $ipv4) = ($ipv4, $ipv6) |
79
|
|
|
|
|
|
|
if $AnyEvent::PROTOCOL{ipv6} > $AnyEvent::PROTOCOL{ipv4}; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
@DNS_FALLBACK = (@$ipv4, @$ipv6); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=item AnyEvent::DNS::a $domain, $cb->(@addrs) |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Tries to resolve the given domain to IPv4 address(es). |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item AnyEvent::DNS::aaaa $domain, $cb->(@addrs) |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Tries to resolve the given domain to IPv6 address(es). |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item AnyEvent::DNS::mx $domain, $cb->(@hostnames) |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Tries to resolve the given domain into a sorted (lower preference value |
95
|
|
|
|
|
|
|
first) list of domain names. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=item AnyEvent::DNS::ns $domain, $cb->(@hostnames) |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
Tries to resolve the given domain name into a list of name servers. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item AnyEvent::DNS::txt $domain, $cb->(@hostnames) |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Tries to resolve the given domain name into a list of text records. Only |
104
|
|
|
|
|
|
|
the first text string per record will be returned. If you want all |
105
|
|
|
|
|
|
|
strings, you need to call the resolver manually: |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
resolver->resolve ($domain => "txt", sub { |
108
|
|
|
|
|
|
|
for my $record (@_) { |
109
|
|
|
|
|
|
|
my (undef, undef, undef, @txt) = @$record; |
110
|
|
|
|
|
|
|
# strings now in @txt |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
}); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=item AnyEvent::DNS::srv $service, $proto, $domain, $cb->(@srv_rr) |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Tries to resolve the given service, protocol and domain name into a list |
117
|
|
|
|
|
|
|
of service records. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Each C<$srv_rr> is an array reference with the following contents: |
120
|
|
|
|
|
|
|
C<[$priority, $weight, $transport, $target]>. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
They will be sorted with lowest priority first, then randomly |
123
|
|
|
|
|
|
|
distributed by weight as per RFC 2782. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Example: |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
AnyEvent::DNS::srv "sip", "udp", "schmorp.de", sub { ... |
128
|
|
|
|
|
|
|
# @_ = ( [10, 10, 5060, "sip1.schmorp.de" ] ) |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=item AnyEvent::DNS::any $domain, $cb->(@rrs) |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Tries to resolve the given domain and passes all resource records found |
133
|
|
|
|
|
|
|
to the callback. Note that this uses a DNS C query, which, as of RFC |
134
|
|
|
|
|
|
|
8482, are officially deprecated. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item AnyEvent::DNS::ptr $domain, $cb->(@hostnames) |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Tries to make a PTR lookup on the given domain. See C |
139
|
|
|
|
|
|
|
and C if you want to resolve an IP address to a hostname |
140
|
|
|
|
|
|
|
instead. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item AnyEvent::DNS::reverse_lookup $ipv4_or_6, $cb->(@hostnames) |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Tries to reverse-resolve the given IPv4 or IPv6 address (in textual form) |
145
|
|
|
|
|
|
|
into its hostname(s). Handles V4MAPPED and V4COMPAT IPv6 addresses |
146
|
|
|
|
|
|
|
transparently. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=item AnyEvent::DNS::reverse_verify $ipv4_or_6, $cb->(@hostnames) |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
The same as C, but does forward-lookups to verify that |
151
|
|
|
|
|
|
|
the resolved hostnames indeed point to the address, which makes spoofing |
152
|
|
|
|
|
|
|
harder. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
If you want to resolve an address into a hostname, this is the preferred |
155
|
|
|
|
|
|
|
method: The DNS records could still change, but at least this function |
156
|
|
|
|
|
|
|
verified that the hostname, at one point in the past, pointed at the IP |
157
|
|
|
|
|
|
|
address you originally resolved. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
Example: |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
AnyEvent::DNS::reverse_verify "2001:500:2f::f", sub { print shift }; |
162
|
|
|
|
|
|
|
# => f.root-servers.net |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=cut |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub MAX_PKT() { 4096 } # max packet size we advertise and accept |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub DOMAIN_PORT() { 53 } # if this changes drop me a note |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub resolver (); |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub a($$) { |
173
|
3
|
|
|
3
|
1
|
15
|
my ($domain, $cb) = @_; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
resolver->resolve ($domain => "a", sub { |
176
|
3
|
|
|
3
|
|
36
|
$cb->(map $_->[4], @_); |
177
|
3
|
|
|
|
|
17
|
}); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub aaaa($$) { |
181
|
2
|
|
|
2
|
1
|
5
|
my ($domain, $cb) = @_; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
resolver->resolve ($domain => "aaaa", sub { |
184
|
2
|
|
|
2
|
|
8
|
$cb->(map $_->[4], @_); |
185
|
2
|
|
|
|
|
5
|
}); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub mx($$) { |
189
|
0
|
|
|
0
|
1
|
0
|
my ($domain, $cb) = @_; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
resolver->resolve ($domain => "mx", sub { |
192
|
0
|
|
|
0
|
|
0
|
$cb->(map $_->[5], sort { $a->[4] <=> $b->[4] } @_); |
|
0
|
|
|
|
|
0
|
|
193
|
0
|
|
|
|
|
0
|
}); |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub ns($$) { |
197
|
0
|
|
|
0
|
1
|
0
|
my ($domain, $cb) = @_; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
resolver->resolve ($domain => "ns", sub { |
200
|
0
|
|
|
0
|
|
0
|
$cb->(map $_->[4], @_); |
201
|
0
|
|
|
|
|
0
|
}); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub txt($$) { |
205
|
0
|
|
|
0
|
1
|
0
|
my ($domain, $cb) = @_; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
resolver->resolve ($domain => "txt", sub { |
208
|
0
|
|
|
0
|
|
0
|
$cb->(map $_->[4], @_); |
209
|
0
|
|
|
|
|
0
|
}); |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub srv($$$$) { |
213
|
0
|
|
|
0
|
1
|
0
|
my ($service, $proto, $domain, $cb) = @_; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# todo, ask for any and check glue records |
216
|
|
|
|
|
|
|
resolver->resolve ("_$service._$proto.$domain" => "srv", sub { |
217
|
0
|
|
|
0
|
|
0
|
my @res; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# classify by priority |
220
|
|
|
|
|
|
|
my %pri; |
221
|
0
|
|
|
|
|
0
|
push @{ $pri{$_->[4]} }, [ @$_[4,5,6,7] ] |
222
|
0
|
|
|
|
|
0
|
for @_; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# order by priority |
225
|
0
|
|
|
|
|
0
|
for my $pri (sort { $a <=> $b } keys %pri) { |
|
0
|
|
|
|
|
0
|
|
226
|
|
|
|
|
|
|
# order by weight |
227
|
0
|
|
|
|
|
0
|
my @rr = sort { $a->[1] <=> $b->[1] } @{ delete $pri{$pri} }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
228
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
0
|
my $sum; $sum += $_->[1] for @rr; |
|
0
|
|
|
|
|
0
|
|
230
|
|
|
|
|
|
|
|
231
|
0
|
|
|
|
|
0
|
while (@rr) { |
232
|
0
|
|
|
|
|
0
|
my $w = int rand $sum + 1; |
233
|
0
|
|
|
|
|
0
|
for (0 .. $#rr) { |
234
|
0
|
0
|
|
|
|
0
|
if (($w -= $rr[$_][1]) <= 0) { |
235
|
0
|
|
|
|
|
0
|
$sum -= $rr[$_][1]; |
236
|
0
|
|
|
|
|
0
|
push @res, splice @rr, $_, 1, (); |
237
|
0
|
|
|
|
|
0
|
last; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
0
|
$cb->(@res); |
244
|
0
|
|
|
|
|
0
|
}); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub ptr($$) { |
248
|
0
|
|
|
0
|
1
|
0
|
my ($domain, $cb) = @_; |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
resolver->resolve ($domain => "ptr", sub { |
251
|
0
|
|
|
0
|
|
0
|
$cb->(map $_->[4], @_); |
252
|
0
|
|
|
|
|
0
|
}); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub any($$) { |
256
|
0
|
|
|
0
|
1
|
0
|
my ($domain, $cb) = @_; |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
|
|
0
|
resolver->resolve ($domain => "*", $cb); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# convert textual ip address into reverse lookup form |
262
|
|
|
|
|
|
|
sub _munge_ptr($) { |
263
|
0
|
0
|
|
0
|
|
0
|
my $ipn = $_[0] |
264
|
|
|
|
|
|
|
or return; |
265
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
0
|
my $ptr; |
267
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
0
|
my $af = AnyEvent::Socket::address_family ($ipn); |
269
|
|
|
|
|
|
|
|
270
|
0
|
0
|
|
|
|
0
|
if ($af == AF_INET6) { |
271
|
0
|
|
|
|
|
0
|
$ipn = substr $ipn, 0, 16; # anticipate future expansion |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# handle v4mapped and v4compat |
274
|
0
|
0
|
|
|
|
0
|
if ($ipn =~ s/^\x00{10}(?:\xff\xff|\x00\x00)//) { |
275
|
0
|
|
|
|
|
0
|
$af = AF_INET; |
276
|
|
|
|
|
|
|
} else { |
277
|
0
|
|
|
|
|
0
|
$ptr = join ".", (reverse split //, unpack "H32", $ipn), "ip6.arpa."; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
0
|
0
|
|
|
|
0
|
if ($af == AF_INET) { |
282
|
0
|
|
|
|
|
0
|
$ptr = join ".", (reverse unpack "C4", $ipn), "in-addr.arpa."; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
$ptr |
286
|
0
|
|
|
|
|
0
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub reverse_lookup($$) { |
289
|
0
|
|
|
0
|
1
|
0
|
my ($ip, $cb) = @_; |
290
|
|
|
|
|
|
|
|
291
|
0
|
0
|
|
|
|
0
|
$ip = _munge_ptr AnyEvent::Socket::parse_address ($ip) |
292
|
|
|
|
|
|
|
or return $cb->(); |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
resolver->resolve ($ip => "ptr", sub { |
295
|
0
|
|
|
0
|
|
0
|
$cb->(map $_->[4], @_); |
296
|
0
|
|
|
|
|
0
|
}); |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub reverse_verify($$) { |
300
|
0
|
|
|
0
|
1
|
0
|
my ($ip, $cb) = @_; |
301
|
|
|
|
|
|
|
|
302
|
0
|
0
|
|
|
|
0
|
my $ipn = AnyEvent::Socket::parse_address ($ip) |
303
|
|
|
|
|
|
|
or return $cb->(); |
304
|
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
0
|
my $af = AnyEvent::Socket::address_family ($ipn); |
306
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
0
|
my @res; |
308
|
|
|
|
|
|
|
my $cnt; |
309
|
|
|
|
|
|
|
|
310
|
0
|
0
|
|
|
|
0
|
my $ptr = _munge_ptr $ipn |
311
|
|
|
|
|
|
|
or return $cb->(); |
312
|
|
|
|
|
|
|
|
313
|
0
|
|
|
|
|
0
|
$ip = AnyEvent::Socket::format_address ($ipn); # normalise into the same form |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
ptr $ptr, sub { |
316
|
0
|
|
|
0
|
|
0
|
for my $name (@_) { |
317
|
0
|
|
|
|
|
0
|
++$cnt; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# () around AF_INET to work around bug in 5.8 |
320
|
|
|
|
|
|
|
resolver->resolve ("$name." => ($af == (AF_INET) ? "a" : "aaaa"), sub { |
321
|
0
|
|
|
|
|
0
|
for (@_) { |
322
|
0
|
0
|
|
|
|
0
|
push @res, $name |
323
|
|
|
|
|
|
|
if $_->[4] eq $ip; |
324
|
|
|
|
|
|
|
} |
325
|
0
|
0
|
|
|
|
0
|
$cb->(@res) unless --$cnt; |
326
|
0
|
0
|
|
|
|
0
|
}); |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
0
|
0
|
|
|
|
0
|
$cb->() unless $cnt; |
330
|
0
|
|
|
|
|
0
|
}; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
################################################################################# |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=back |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=head2 LOW-LEVEL DNS EN-/DECODING FUNCTIONS |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=over 4 |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=item $AnyEvent::DNS::EDNS0 |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
This variable decides whether dns_pack automatically enables EDNS0 |
344
|
|
|
|
|
|
|
support. By default, this is disabled (C<0>), unless overridden by |
345
|
|
|
|
|
|
|
C<$ENV{PERL_ANYEVENT_EDNS0}>, but when set to C<1>, AnyEvent::DNS will use |
346
|
|
|
|
|
|
|
EDNS0 in all requests. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=cut |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
our $EDNS0 = $ENV{PERL_ANYEVENT_EDNS0}*1; # set to 1 to enable (partial) edns0 |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
our %opcode_id = ( |
353
|
|
|
|
|
|
|
query => 0, |
354
|
|
|
|
|
|
|
iquery => 1, |
355
|
|
|
|
|
|
|
status => 2, |
356
|
|
|
|
|
|
|
notify => 4, |
357
|
|
|
|
|
|
|
update => 5, |
358
|
|
|
|
|
|
|
map +($_ => $_), 3, 6..15 |
359
|
|
|
|
|
|
|
); |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
our %opcode_str = reverse %opcode_id; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
our %rcode_id = ( |
364
|
|
|
|
|
|
|
noerror => 0, |
365
|
|
|
|
|
|
|
formerr => 1, |
366
|
|
|
|
|
|
|
servfail => 2, |
367
|
|
|
|
|
|
|
nxdomain => 3, |
368
|
|
|
|
|
|
|
notimp => 4, |
369
|
|
|
|
|
|
|
refused => 5, |
370
|
|
|
|
|
|
|
yxdomain => 6, # Name Exists when it should not [RFC 2136] |
371
|
|
|
|
|
|
|
yxrrset => 7, # RR Set Exists when it should not [RFC 2136] |
372
|
|
|
|
|
|
|
nxrrset => 8, # RR Set that should exist does not [RFC 2136] |
373
|
|
|
|
|
|
|
notauth => 9, # Server Not Authoritative for zone [RFC 2136] |
374
|
|
|
|
|
|
|
notzone => 10, # Name not contained in zone [RFC 2136] |
375
|
|
|
|
|
|
|
# EDNS0 16 BADVERS Bad OPT Version [RFC 2671] |
376
|
|
|
|
|
|
|
# EDNS0 16 BADSIG TSIG Signature Failure [RFC 2845] |
377
|
|
|
|
|
|
|
# EDNS0 17 BADKEY Key not recognized [RFC 2845] |
378
|
|
|
|
|
|
|
# EDNS0 18 BADTIME Signature out of time window [RFC 2845] |
379
|
|
|
|
|
|
|
# EDNS0 19 BADMODE Bad TKEY Mode [RFC 2930] |
380
|
|
|
|
|
|
|
# EDNS0 20 BADNAME Duplicate key name [RFC 2930] |
381
|
|
|
|
|
|
|
# EDNS0 21 BADALG Algorithm not supported [RFC 2930] |
382
|
|
|
|
|
|
|
map +($_ => $_), 11..15 |
383
|
|
|
|
|
|
|
); |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
our %rcode_str = reverse %rcode_id; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
our %type_id = ( |
388
|
|
|
|
|
|
|
a => 1, |
389
|
|
|
|
|
|
|
ns => 2, |
390
|
|
|
|
|
|
|
md => 3, |
391
|
|
|
|
|
|
|
mf => 4, |
392
|
|
|
|
|
|
|
cname => 5, |
393
|
|
|
|
|
|
|
soa => 6, |
394
|
|
|
|
|
|
|
mb => 7, |
395
|
|
|
|
|
|
|
mg => 8, |
396
|
|
|
|
|
|
|
mr => 9, |
397
|
|
|
|
|
|
|
null => 10, |
398
|
|
|
|
|
|
|
wks => 11, |
399
|
|
|
|
|
|
|
ptr => 12, |
400
|
|
|
|
|
|
|
hinfo => 13, |
401
|
|
|
|
|
|
|
minfo => 14, |
402
|
|
|
|
|
|
|
mx => 15, |
403
|
|
|
|
|
|
|
txt => 16, |
404
|
|
|
|
|
|
|
sig => 24, |
405
|
|
|
|
|
|
|
key => 25, |
406
|
|
|
|
|
|
|
gpos => 27, # rfc1712 |
407
|
|
|
|
|
|
|
aaaa => 28, |
408
|
|
|
|
|
|
|
loc => 29, # rfc1876 |
409
|
|
|
|
|
|
|
srv => 33, |
410
|
|
|
|
|
|
|
naptr => 35, # rfc2915 |
411
|
|
|
|
|
|
|
dname => 39, # rfc2672 |
412
|
|
|
|
|
|
|
opt => 41, |
413
|
|
|
|
|
|
|
ds => 43, # rfc4034 |
414
|
|
|
|
|
|
|
sshfp => 44, # rfc4255 |
415
|
|
|
|
|
|
|
rrsig => 46, # rfc4034 |
416
|
|
|
|
|
|
|
nsec => 47, # rfc4034 |
417
|
|
|
|
|
|
|
dnskey=> 48, # rfc4034 |
418
|
|
|
|
|
|
|
smimea=> 53, # rfc8162 |
419
|
|
|
|
|
|
|
cds => 59, # rfc7344 |
420
|
|
|
|
|
|
|
cdnskey=> 60, # rfc7344 |
421
|
|
|
|
|
|
|
openpgpkey=> 61, # rfc7926 |
422
|
|
|
|
|
|
|
csync => 62, # rfc7929 |
423
|
|
|
|
|
|
|
spf => 99, |
424
|
|
|
|
|
|
|
tkey => 249, |
425
|
|
|
|
|
|
|
tsig => 250, |
426
|
|
|
|
|
|
|
ixfr => 251, |
427
|
|
|
|
|
|
|
axfr => 252, |
428
|
|
|
|
|
|
|
mailb => 253, |
429
|
|
|
|
|
|
|
"*" => 255, |
430
|
|
|
|
|
|
|
uri => 256, |
431
|
|
|
|
|
|
|
caa => 257, # rfc6844 |
432
|
|
|
|
|
|
|
); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
our %type_str = reverse %type_id; |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
our %class_id = ( |
437
|
|
|
|
|
|
|
in => 1, |
438
|
|
|
|
|
|
|
ch => 3, |
439
|
|
|
|
|
|
|
hs => 4, |
440
|
|
|
|
|
|
|
none => 254, |
441
|
|
|
|
|
|
|
"*" => 255, |
442
|
|
|
|
|
|
|
); |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
our %class_str = reverse %class_id; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub _enc_name($) { |
447
|
5
|
|
|
5
|
|
92
|
pack "(C/a*)*", (split /\./, shift), "" |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
if ($] < 5.008) { |
451
|
|
|
|
|
|
|
# special slower 5.6 version |
452
|
|
|
|
|
|
|
*_enc_name = sub ($) { |
453
|
|
|
|
|
|
|
join "", map +(pack "C/a*", $_), (split /\./, shift), "" |
454
|
|
|
|
|
|
|
}; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
sub _enc_qd() { |
458
|
|
|
|
|
|
|
(_enc_name $_->[0]) . pack "nn", |
459
|
|
|
|
|
|
|
($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}), |
460
|
5
|
50
|
50
|
5
|
|
13
|
($_->[3] > 0 ? $_->[2] : $class_id{$_->[2] || "in"}) |
|
|
50
|
|
|
|
|
|
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
sub _enc_rr() { |
464
|
0
|
|
|
0
|
|
0
|
die "encoding of resource records is not supported"; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=item $pkt = AnyEvent::DNS::dns_pack $dns |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Packs a perl data structure into a DNS packet. Reading RFC 1035 is strongly |
470
|
|
|
|
|
|
|
recommended, then everything will be totally clear. Or maybe not. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Resource records are not yet encodable. |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Examples: |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# very simple request, using lots of default values: |
477
|
|
|
|
|
|
|
{ rd => 1, qd => [ [ "host.domain", "a"] ] } |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# more complex example, showing how flags etc. are named: |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
{ |
482
|
|
|
|
|
|
|
id => 10000, |
483
|
|
|
|
|
|
|
op => "query", |
484
|
|
|
|
|
|
|
rc => "nxdomain", |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
# flags |
487
|
|
|
|
|
|
|
qr => 1, |
488
|
|
|
|
|
|
|
aa => 0, |
489
|
|
|
|
|
|
|
tc => 0, |
490
|
|
|
|
|
|
|
rd => 0, |
491
|
|
|
|
|
|
|
ra => 0, |
492
|
|
|
|
|
|
|
ad => 0, |
493
|
|
|
|
|
|
|
cd => 0, |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
qd => [@rr], # query section |
496
|
|
|
|
|
|
|
an => [@rr], # answer section |
497
|
|
|
|
|
|
|
ns => [@rr], # authority section |
498
|
|
|
|
|
|
|
ar => [@rr], # additional records section |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=cut |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub dns_pack($) { |
504
|
5
|
|
|
5
|
1
|
10
|
my ($req) = @_; |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
pack "nn nnnn a* a* a* a* a*", |
507
|
|
|
|
|
|
|
$req->{id}, |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
! !$req->{qr} * 0x8000 |
510
|
|
|
|
|
|
|
+ $opcode_id{$req->{op}} * 0x0800 |
511
|
|
|
|
|
|
|
+ ! !$req->{aa} * 0x0400 |
512
|
|
|
|
|
|
|
+ ! !$req->{tc} * 0x0200 |
513
|
|
|
|
|
|
|
+ ! !$req->{rd} * 0x0100 |
514
|
|
|
|
|
|
|
+ ! !$req->{ra} * 0x0080 |
515
|
|
|
|
|
|
|
+ ! !$req->{ad} * 0x0020 |
516
|
|
|
|
|
|
|
+ ! !$req->{cd} * 0x0010 |
517
|
|
|
|
|
|
|
+ $rcode_id{$req->{rc}} * 0x0001, |
518
|
|
|
|
|
|
|
|
519
|
5
|
50
|
|
|
|
15
|
scalar @{ $req->{qd} || [] }, |
520
|
5
|
50
|
|
|
|
26
|
scalar @{ $req->{an} || [] }, |
521
|
5
|
50
|
|
|
|
16
|
scalar @{ $req->{ns} || [] }, |
522
|
5
|
50
|
|
|
|
28
|
$EDNS0 + scalar @{ $req->{ar} || [] }, # EDNS0 option included here |
523
|
|
|
|
|
|
|
|
524
|
5
|
50
|
|
|
|
17
|
(join "", map _enc_qd, @{ $req->{qd} || [] }), |
525
|
5
|
50
|
|
|
|
31
|
(join "", map _enc_rr, @{ $req->{an} || [] }), |
526
|
5
|
50
|
|
|
|
18
|
(join "", map _enc_rr, @{ $req->{ns} || [] }), |
527
|
5
|
50
|
|
|
|
44
|
(join "", map _enc_rr, @{ $req->{ar} || [] }), |
|
5
|
50
|
|
|
|
47
|
|
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
($EDNS0 ? pack "C nnNn", 0, 41, MAX_PKT, 0, 0 : "") # EDNS0 option |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
our $ofs; |
533
|
|
|
|
|
|
|
our $pkt; |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
# bitches |
536
|
|
|
|
|
|
|
sub _dec_name { |
537
|
18
|
|
|
18
|
|
24
|
my @res; |
538
|
|
|
|
|
|
|
my $redir; |
539
|
18
|
|
|
|
|
24
|
my $ptr = $ofs; |
540
|
18
|
|
|
|
|
17
|
my $cnt; |
541
|
|
|
|
|
|
|
|
542
|
18
|
|
|
|
|
31
|
while () { |
543
|
57
|
50
|
|
|
|
103
|
return undef if ++$cnt >= 256; # to avoid DoS attacks |
544
|
|
|
|
|
|
|
|
545
|
57
|
|
|
|
|
84
|
my $len = ord substr $pkt, $ptr++, 1; |
546
|
|
|
|
|
|
|
|
547
|
57
|
100
|
|
|
|
87
|
if ($len >= 0xc0) { |
|
|
100
|
|
|
|
|
|
548
|
1
|
|
|
|
|
2
|
$ptr++; |
549
|
1
|
50
|
|
|
|
4
|
$ofs = $ptr if $ptr > $ofs; |
550
|
1
|
|
|
|
|
6
|
$ptr = (unpack "n", substr $pkt, $ptr - 2, 2) & 0x3fff; |
551
|
|
|
|
|
|
|
} elsif ($len) { |
552
|
38
|
|
|
|
|
71
|
push @res, substr $pkt, $ptr, $len; |
553
|
38
|
|
|
|
|
42
|
$ptr += $len; |
554
|
|
|
|
|
|
|
} else { |
555
|
18
|
100
|
|
|
|
33
|
$ofs = $ptr if $ptr > $ofs; |
556
|
18
|
|
|
|
|
63
|
return join ".", @res; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub _dec_qd { |
562
|
5
|
|
|
5
|
|
16
|
my $qname = _dec_name; |
563
|
5
|
|
|
|
|
18
|
my ($qt, $qc) = unpack "nn", substr $pkt, $ofs; $ofs += 4; |
|
5
|
|
|
|
|
12
|
|
564
|
5
|
|
33
|
|
|
78
|
[$qname, $type_str{$qt} || $qt, $class_str{$qc} || $qc] |
|
|
|
33
|
|
|
|
|
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
our %dec_rr = ( |
568
|
|
|
|
|
|
|
1 => sub { join ".", unpack "C4", $_ }, # a |
569
|
|
|
|
|
|
|
2 => sub { local $ofs = $ofs - length; _dec_name }, # ns |
570
|
|
|
|
|
|
|
5 => sub { local $ofs = $ofs - length; _dec_name }, # cname |
571
|
|
|
|
|
|
|
6 => sub { |
572
|
|
|
|
|
|
|
local $ofs = $ofs - length; |
573
|
|
|
|
|
|
|
my $mname = _dec_name; |
574
|
|
|
|
|
|
|
my $rname = _dec_name; |
575
|
|
|
|
|
|
|
($mname, $rname, unpack "NNNNN", substr $pkt, $ofs) |
576
|
|
|
|
|
|
|
}, # soa |
577
|
|
|
|
|
|
|
11 => sub { ((join ".", unpack "C4", $_), unpack "C a*", substr $_, 4) }, # wks |
578
|
|
|
|
|
|
|
12 => sub { local $ofs = $ofs - length; _dec_name }, # ptr |
579
|
|
|
|
|
|
|
13 => sub { unpack "C/a* C/a*", $_ }, # hinfo |
580
|
|
|
|
|
|
|
15 => sub { local $ofs = $ofs + 2 - length; ((unpack "n", $_), _dec_name) }, # mx |
581
|
|
|
|
|
|
|
16 => sub { unpack "(C/a*)*", $_ }, # txt |
582
|
|
|
|
|
|
|
28 => sub { AnyEvent::Socket::format_ipv6 ($_) }, # aaaa |
583
|
|
|
|
|
|
|
33 => sub { local $ofs = $ofs + 6 - length; ((unpack "nnn", $_), _dec_name) }, # srv |
584
|
|
|
|
|
|
|
35 => sub { # naptr |
585
|
|
|
|
|
|
|
# requires perl 5.10, sorry |
586
|
|
|
|
|
|
|
my ($order, $preference, $flags, $service, $regexp, $offset) = unpack "nn C/a* C/a* C/a* .", $_; |
587
|
|
|
|
|
|
|
local $ofs = $ofs + $offset - length; |
588
|
|
|
|
|
|
|
($order, $preference, $flags, $service, $regexp, _dec_name) |
589
|
|
|
|
|
|
|
}, |
590
|
|
|
|
|
|
|
39 => sub { local $ofs = $ofs - length; _dec_name }, # dname |
591
|
|
|
|
|
|
|
99 => sub { unpack "(C/a*)*", $_ }, # spf |
592
|
|
|
|
|
|
|
257 => sub { unpack "CC/a*a*", $_ }, # caa |
593
|
|
|
|
|
|
|
); |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
sub _dec_rr { |
596
|
5
|
|
|
5
|
|
13
|
my $name = _dec_name; |
597
|
|
|
|
|
|
|
|
598
|
5
|
|
|
|
|
24
|
my ($rt, $rc, $ttl, $rdlen) = unpack "nn N n", substr $pkt, $ofs; $ofs += 10; |
|
5
|
|
|
|
|
10
|
|
599
|
5
|
|
|
|
|
13
|
local $_ = substr $pkt, $ofs, $rdlen; $ofs += $rdlen; |
|
5
|
|
|
|
|
8
|
|
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
[ |
602
|
|
|
|
|
|
|
$name, |
603
|
|
|
|
|
|
|
$type_str{$rt} || $rt, |
604
|
|
|
|
|
|
|
$class_str{$rc} || $rc, |
605
|
|
|
|
|
|
|
$ttl, |
606
|
5
|
|
33
|
0
|
|
45
|
($dec_rr{$rt} || sub { $_ })->(), |
|
0
|
|
33
|
|
|
0
|
|
|
|
|
50
|
|
|
|
|
607
|
|
|
|
|
|
|
] |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=item $dns = AnyEvent::DNS::dns_unpack $pkt |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Unpacks a DNS packet into a perl data structure. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Examples: |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# an unsuccessful reply |
617
|
|
|
|
|
|
|
{ |
618
|
|
|
|
|
|
|
'qd' => [ |
619
|
|
|
|
|
|
|
[ 'ruth.plan9.de.mach.uni-karlsruhe.de', '*', 'in' ] |
620
|
|
|
|
|
|
|
], |
621
|
|
|
|
|
|
|
'rc' => 'nxdomain', |
622
|
|
|
|
|
|
|
'ar' => [], |
623
|
|
|
|
|
|
|
'ns' => [ |
624
|
|
|
|
|
|
|
[ |
625
|
|
|
|
|
|
|
'uni-karlsruhe.de', |
626
|
|
|
|
|
|
|
'soa', |
627
|
|
|
|
|
|
|
'in', |
628
|
|
|
|
|
|
|
600, |
629
|
|
|
|
|
|
|
'netserv.rz.uni-karlsruhe.de', |
630
|
|
|
|
|
|
|
'hostmaster.rz.uni-karlsruhe.de', |
631
|
|
|
|
|
|
|
2008052201, 10800, 1800, 2592000, 86400 |
632
|
|
|
|
|
|
|
] |
633
|
|
|
|
|
|
|
], |
634
|
|
|
|
|
|
|
'tc' => '', |
635
|
|
|
|
|
|
|
'ra' => 1, |
636
|
|
|
|
|
|
|
'qr' => 1, |
637
|
|
|
|
|
|
|
'id' => 45915, |
638
|
|
|
|
|
|
|
'aa' => '', |
639
|
|
|
|
|
|
|
'an' => [], |
640
|
|
|
|
|
|
|
'rd' => 1, |
641
|
|
|
|
|
|
|
'op' => 'query', |
642
|
|
|
|
|
|
|
'__' => '', |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
# a successful reply |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
{ |
648
|
|
|
|
|
|
|
'qd' => [ [ 'www.google.de', 'a', 'in' ] ], |
649
|
|
|
|
|
|
|
'rc' => 0, |
650
|
|
|
|
|
|
|
'ar' => [ |
651
|
|
|
|
|
|
|
[ 'a.l.google.com', 'a', 'in', 3600, '209.85.139.9' ], |
652
|
|
|
|
|
|
|
[ 'b.l.google.com', 'a', 'in', 3600, '64.233.179.9' ], |
653
|
|
|
|
|
|
|
[ 'c.l.google.com', 'a', 'in', 3600, '64.233.161.9' ], |
654
|
|
|
|
|
|
|
], |
655
|
|
|
|
|
|
|
'ns' => [ |
656
|
|
|
|
|
|
|
[ 'l.google.com', 'ns', 'in', 3600, 'a.l.google.com' ], |
657
|
|
|
|
|
|
|
[ 'l.google.com', 'ns', 'in', 3600, 'b.l.google.com' ], |
658
|
|
|
|
|
|
|
], |
659
|
|
|
|
|
|
|
'tc' => '', |
660
|
|
|
|
|
|
|
'ra' => 1, |
661
|
|
|
|
|
|
|
'qr' => 1, |
662
|
|
|
|
|
|
|
'id' => 64265, |
663
|
|
|
|
|
|
|
'aa' => '', |
664
|
|
|
|
|
|
|
'an' => [ |
665
|
|
|
|
|
|
|
[ 'www.google.de', 'cname', 'in', 3600, 'www.google.com' ], |
666
|
|
|
|
|
|
|
[ 'www.google.com', 'cname', 'in', 3600, 'www.l.google.com' ], |
667
|
|
|
|
|
|
|
[ 'www.l.google.com', 'a', 'in', 3600, '66.249.93.104' ], |
668
|
|
|
|
|
|
|
[ 'www.l.google.com', 'a', 'in', 3600, '66.249.93.147' ], |
669
|
|
|
|
|
|
|
], |
670
|
|
|
|
|
|
|
'rd' => 1, |
671
|
|
|
|
|
|
|
'op' => 0, |
672
|
|
|
|
|
|
|
'__' => '', |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=cut |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
sub dns_unpack($) { |
678
|
5
|
|
|
5
|
1
|
21
|
local $pkt = shift; |
679
|
5
|
|
|
|
|
39
|
my ($id, $flags, $qd, $an, $ns, $ar) |
680
|
|
|
|
|
|
|
= unpack "nn nnnn A*", $pkt; |
681
|
|
|
|
|
|
|
|
682
|
5
|
|
|
|
|
15
|
local $ofs = 6 * 2; |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
{ |
685
|
|
|
|
|
|
|
__ => $pkt, |
686
|
|
|
|
|
|
|
id => $id, |
687
|
|
|
|
|
|
|
qr => ! ! ($flags & 0x8000), |
688
|
|
|
|
|
|
|
aa => ! ! ($flags & 0x0400), |
689
|
|
|
|
|
|
|
tc => ! ! ($flags & 0x0200), |
690
|
|
|
|
|
|
|
rd => ! ! ($flags & 0x0100), |
691
|
|
|
|
|
|
|
ra => ! ! ($flags & 0x0080), |
692
|
|
|
|
|
|
|
ad => ! ! ($flags & 0x0020), |
693
|
|
|
|
|
|
|
cd => ! ! ($flags & 0x0010), |
694
|
|
|
|
|
|
|
op => $opcode_str{($flags & 0x001e) >> 11}, |
695
|
5
|
|
|
|
|
56
|
rc => $rcode_str{($flags & 0x000f)}, |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
qd => [map _dec_qd, 1 .. $qd], |
698
|
|
|
|
|
|
|
an => [map _dec_rr, 1 .. $an], |
699
|
|
|
|
|
|
|
ns => [map _dec_rr, 1 .. $ns], |
700
|
|
|
|
|
|
|
ar => [map _dec_rr, 1 .. $ar], |
701
|
|
|
|
|
|
|
} |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
############################################################################# |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=back |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=head3 Extending DNS Encoder and Decoder |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
This section describes an I method to extend the DNS encoder |
711
|
|
|
|
|
|
|
and decoder with new opcode, rcode, class and type strings, as well as |
712
|
|
|
|
|
|
|
resource record decoders. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
Since this is experimental, it can change, as anything can change, but |
715
|
|
|
|
|
|
|
this interface is expe ctedc to be relatively stable and was stable during |
716
|
|
|
|
|
|
|
the whole existance of C so far. |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
Note that, since changing the decoder or encoder might break existing |
719
|
|
|
|
|
|
|
code, you should either be sure to control for this, or only temporarily |
720
|
|
|
|
|
|
|
change these values, e.g. like so: |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
my $decoded = do { |
723
|
|
|
|
|
|
|
local $AnyEvent::DNS::opcode_str{7} = "yxrrset"; |
724
|
|
|
|
|
|
|
AnyEvent::DNS::dns_unpack $mypkt |
725
|
|
|
|
|
|
|
}; |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=over 4 |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=item %AnyEvent::DNS::opcode_id, %AnyEvent::DNS::opcode_str |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Two hashes that map lowercase opcode strings to numerical id's (For the |
732
|
|
|
|
|
|
|
encoder), or vice versa (for the decoder). Example: add a new opcode |
733
|
|
|
|
|
|
|
string C. |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
$AnyEvent::DNS::opcode_id{notzone} = 10; |
736
|
|
|
|
|
|
|
$AnyEvent::DNS::opcode_str{10} = 'notzone'; |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=item %AnyEvent::DNS::rcode_id, %AnyEvent::DNS::rcode_str |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Same as above, for for rcode values. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=item %AnyEvent::DNS::class_id, %AnyEvent::DNS::class_str |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
Same as above, but for resource record class names/values. |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=item %AnyEvent::DNS::type_id, %AnyEvent::DNS::type_str |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
Same as above, but for resource record type names/values. |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
=item %AnyEvent::DNS::dec_rr |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
This hash maps resource record type values to code references. When |
753
|
|
|
|
|
|
|
decoding, they are called with C<$_> set to the undecoded data portion and |
754
|
|
|
|
|
|
|
C<$ofs> being the current byte offset. of the record. You should have a |
755
|
|
|
|
|
|
|
look at the existing implementations to understand how it works in detail, |
756
|
|
|
|
|
|
|
but here are two examples: |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
Decode an A record. A records are simply four bytes with one byte per |
759
|
|
|
|
|
|
|
address component, so the decoder simply unpacks them and joins them with |
760
|
|
|
|
|
|
|
dots in between: |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
$AnyEvent::DNS::dec_rr{1} = sub { join ".", unpack "C4", $_ }; |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
Decode a CNAME record, which contains a potentially compressed domain |
765
|
|
|
|
|
|
|
name. |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
package AnyEvent::DNS; # for %dec_rr, $ofsd and &_dec_name |
768
|
|
|
|
|
|
|
$dec_rr{5} = sub { local $ofs = $ofs - length; _dec_name }; |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=back |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=head2 THE AnyEvent::DNS RESOLVER CLASS |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
This is the class which does the actual protocol work. |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
=over 4 |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=cut |
779
|
|
|
|
|
|
|
|
780
|
8
|
|
|
8
|
|
99
|
use Carp (); |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
164
|
|
781
|
8
|
|
|
8
|
|
40
|
use Scalar::Util (); |
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
119
|
|
782
|
8
|
|
|
8
|
|
51
|
use Socket (); |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
28528
|
|
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
our $NOW; |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
=item AnyEvent::DNS::resolver |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
This function creates and returns a resolver that is ready to use and |
789
|
|
|
|
|
|
|
should mimic the default resolver for your system as good as possible. It |
790
|
|
|
|
|
|
|
is used by AnyEvent itself as well. |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
It only ever creates one resolver and returns this one on subsequent calls |
793
|
|
|
|
|
|
|
- see C<$AnyEvent::DNS::RESOLVER>, below, for details. |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
Unless you have special needs, prefer this function over creating your own |
796
|
|
|
|
|
|
|
resolver object. |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
The resolver is created with the following parameters: |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
untaint enabled |
801
|
|
|
|
|
|
|
max_outstanding $ENV{PERL_ANYEVENT_MAX_OUTSTANDING_DNS} (default 10) |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
C will be used for OS-specific configuration, unless |
804
|
|
|
|
|
|
|
C<$ENV{PERL_ANYEVENT_RESOLV_CONF}> is specified, in which case that file |
805
|
|
|
|
|
|
|
gets parsed. |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
=item $AnyEvent::DNS::RESOLVER |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
This variable stores the default resolver returned by |
810
|
|
|
|
|
|
|
C, or C when the default resolver hasn't |
811
|
|
|
|
|
|
|
been instantiated yet. |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
One can provide a custom resolver (e.g. one with caching functionality) |
814
|
|
|
|
|
|
|
by storing it in this variable, causing all subsequent resolves done via |
815
|
|
|
|
|
|
|
C to be done via the custom one. |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
=cut |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
our $RESOLVER; |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
sub resolver() { |
822
|
8
|
100
|
|
8
|
1
|
158
|
$RESOLVER || do { |
823
|
|
|
|
|
|
|
$RESOLVER = new AnyEvent::DNS |
824
|
|
|
|
|
|
|
untaint => 1, |
825
|
2
|
|
50
|
|
|
27
|
max_outstanding => $ENV{PERL_ANYEVENT_MAX_OUTSTANDING_DNS}*1 || 10, |
826
|
|
|
|
|
|
|
; |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
$ENV{PERL_ANYEVENT_RESOLV_CONF} |
829
|
|
|
|
|
|
|
? $RESOLVER->_load_resolv_conf_file ($ENV{PERL_ANYEVENT_RESOLV_CONF}) |
830
|
2
|
50
|
|
|
|
10
|
: $RESOLVER->os_config; |
831
|
|
|
|
|
|
|
|
832
|
2
|
|
|
|
|
12
|
$RESOLVER |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
} |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=item $resolver = new AnyEvent::DNS key => value... |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
Creates and returns a new resolver. |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
The following options are supported: |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=over 4 |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=item server => [...] |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
A list of server addresses (default: C or C<::1>) in network |
847
|
|
|
|
|
|
|
format (i.e. as returned by C - both IPv4 |
848
|
|
|
|
|
|
|
and IPv6 are supported). |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=item timeout => [...] |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
A list of timeouts to use (also determines the number of retries). To make |
853
|
|
|
|
|
|
|
three retries with individual time-outs of 2, 5 and 5 seconds, use C<[2, |
854
|
|
|
|
|
|
|
5, 5]>, which is also the default. |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
=item search => [...] |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
The default search list of suffixes to append to a domain name (default: none). |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
=item ndots => $integer |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
The number of dots (default: C<1>) that a name must have so that the resolver |
863
|
|
|
|
|
|
|
tries to resolve the name without any suffixes first. |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=item max_outstanding => $integer |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
Most name servers do not handle many parallel requests very well. This |
868
|
|
|
|
|
|
|
option limits the number of outstanding requests to C<$integer> |
869
|
|
|
|
|
|
|
(default: C<10>), that means if you request more than this many requests, |
870
|
|
|
|
|
|
|
then the additional requests will be queued until some other requests have |
871
|
|
|
|
|
|
|
been resolved. |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
=item reuse => $seconds |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
The number of seconds (default: C<300>) that a query id cannot be re-used |
876
|
|
|
|
|
|
|
after a timeout. If there was no time-out then query ids can be reused |
877
|
|
|
|
|
|
|
immediately. |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
=item untaint => $boolean |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
When true, then the resolver will automatically untaint results, and might |
882
|
|
|
|
|
|
|
also ignore certain environment variables. |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
=back |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=cut |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
sub new { |
889
|
2
|
|
|
2
|
1
|
11
|
my ($class, %arg) = @_; |
890
|
|
|
|
|
|
|
|
891
|
2
|
|
|
|
|
18
|
my $self = bless { |
892
|
|
|
|
|
|
|
server => [], |
893
|
|
|
|
|
|
|
timeout => [2, 5, 5], |
894
|
|
|
|
|
|
|
search => [], |
895
|
|
|
|
|
|
|
ndots => 1, |
896
|
|
|
|
|
|
|
max_outstanding => 10, |
897
|
|
|
|
|
|
|
reuse => 300, |
898
|
|
|
|
|
|
|
%arg, |
899
|
|
|
|
|
|
|
inhibit => 0, |
900
|
|
|
|
|
|
|
reuse_q => [], |
901
|
|
|
|
|
|
|
}, $class; |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
# search should default to gethostname's domain |
904
|
|
|
|
|
|
|
# but perl lacks a good posix module |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
# try to create an ipv4 and an ipv6 socket |
907
|
|
|
|
|
|
|
# only fail when we cannot create either |
908
|
2
|
|
|
|
|
6
|
my $got_socket; |
909
|
|
|
|
|
|
|
|
910
|
2
|
|
|
|
|
10
|
Scalar::Util::weaken (my $wself = $self); |
911
|
|
|
|
|
|
|
|
912
|
2
|
50
|
|
|
|
95
|
if (socket my $fh4, AF_INET , Socket::SOCK_DGRAM(), 0) { |
913
|
2
|
|
|
|
|
8
|
++$got_socket; |
914
|
|
|
|
|
|
|
|
915
|
2
|
|
|
|
|
10
|
AnyEvent::fh_unblock $fh4; |
916
|
2
|
|
|
|
|
15
|
$self->{fh4} = $fh4; |
917
|
|
|
|
|
|
|
$self->{rw4} = AE::io $fh4, 0, sub { |
918
|
5
|
50
|
|
5
|
|
188
|
if (my $peer = recv $fh4, my $pkt, MAX_PKT, 0) { |
919
|
5
|
|
|
|
|
38
|
$wself->_recv ($pkt, $peer); |
920
|
|
|
|
|
|
|
} |
921
|
2
|
|
|
|
|
18
|
}; |
922
|
|
|
|
|
|
|
} |
923
|
|
|
|
|
|
|
|
924
|
2
|
50
|
|
|
|
60
|
if (AF_INET6 && socket my $fh6, AF_INET6, Socket::SOCK_DGRAM(), 0) { |
925
|
2
|
|
|
|
|
5
|
++$got_socket; |
926
|
|
|
|
|
|
|
|
927
|
2
|
|
|
|
|
5
|
$self->{fh6} = $fh6; |
928
|
2
|
|
|
|
|
13
|
AnyEvent::fh_unblock $fh6; |
929
|
|
|
|
|
|
|
$self->{rw6} = AE::io $fh6, 0, sub { |
930
|
0
|
0
|
|
0
|
|
0
|
if (my $peer = recv $fh6, my $pkt, MAX_PKT, 0) { |
931
|
0
|
|
|
|
|
0
|
$wself->_recv ($pkt, $peer); |
932
|
|
|
|
|
|
|
} |
933
|
2
|
|
|
|
|
17
|
}; |
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
$got_socket |
937
|
2
|
50
|
|
|
|
8
|
or Carp::croak "unable to create either an IPv4 or an IPv6 socket"; |
938
|
|
|
|
|
|
|
|
939
|
2
|
|
|
|
|
8
|
$self->_compile; |
940
|
|
|
|
|
|
|
|
941
|
2
|
|
|
|
|
8
|
$self |
942
|
|
|
|
|
|
|
} |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
# called to start asynchronous configuration |
945
|
|
|
|
|
|
|
sub _config_begin { |
946
|
4
|
|
|
4
|
|
8
|
++$_[0]{inhibit}; |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
# called when done with async config |
950
|
|
|
|
|
|
|
sub _config_done { |
951
|
4
|
|
|
4
|
|
10
|
--$_[0]{inhibit}; |
952
|
4
|
|
|
|
|
9
|
$_[0]->_compile; |
953
|
4
|
|
|
|
|
9
|
$_[0]->_scheduler; |
954
|
|
|
|
|
|
|
} |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=item $resolver->parse_resolv_conf ($string) |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
Parses the given string as if it were a F file. The following |
959
|
|
|
|
|
|
|
directives are supported (but not necessarily implemented). |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
C<#>- and C<;>-style comments, C, C, C, C, |
962
|
|
|
|
|
|
|
C (C, C, C). |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
Everything else is silently ignored. |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
=cut |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
sub parse_resolv_conf { |
969
|
2
|
|
|
2
|
1
|
23
|
my ($self, $resolvconf) = @_; |
970
|
|
|
|
|
|
|
|
971
|
2
|
|
|
|
|
9
|
$self->{server} = []; |
972
|
2
|
|
|
|
|
5
|
$self->{search} = []; |
973
|
|
|
|
|
|
|
|
974
|
2
|
|
|
|
|
3
|
my $attempts; |
975
|
|
|
|
|
|
|
|
976
|
2
|
|
|
|
|
10
|
for (split /\n/, $resolvconf) { |
977
|
12
|
|
|
|
|
42
|
s/\s*[;#].*$//; # not quite legal, but many people insist |
978
|
|
|
|
|
|
|
|
979
|
12
|
100
|
|
|
|
54
|
if (/^\s*nameserver\s+(\S+)\s*$/i) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
980
|
4
|
|
|
|
|
11
|
my $ip = $1; |
981
|
4
|
50
|
|
|
|
12
|
if (my $ipn = AnyEvent::Socket::parse_address ($ip)) { |
982
|
4
|
|
|
|
|
5
|
push @{ $self->{server} }, $ipn; |
|
4
|
|
|
|
|
12
|
|
983
|
|
|
|
|
|
|
} else { |
984
|
0
|
|
|
|
|
0
|
AE::log 5 => "nameserver $ip invalid and ignored, while parsing resolver config."; |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
} elsif (/^\s*domain\s+(\S*)\s*$/i) { |
987
|
2
|
|
|
|
|
10
|
$self->{search} = [$1]; |
988
|
|
|
|
|
|
|
} elsif (/^\s*search\s+(.*?)\s*$/i) { |
989
|
0
|
|
|
|
|
0
|
$self->{search} = [split /\s+/, $1]; |
990
|
|
|
|
|
|
|
} elsif (/^\s*sortlist\s+(.*?)\s*$/i) { |
991
|
|
|
|
|
|
|
# ignored, NYI |
992
|
|
|
|
|
|
|
} elsif (/^\s*options\s+(.*?)\s*$/i) { |
993
|
0
|
|
|
|
|
0
|
for (split /\s+/, $1) { |
994
|
0
|
0
|
|
|
|
0
|
if (/^timeout:(\d+)$/) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
995
|
0
|
|
|
|
|
0
|
$self->{timeout} = [$1]; |
996
|
|
|
|
|
|
|
} elsif (/^attempts:(\d+)$/) { |
997
|
0
|
|
|
|
|
0
|
$attempts = $1; |
998
|
|
|
|
|
|
|
} elsif (/^ndots:(\d+)$/) { |
999
|
0
|
|
|
|
|
0
|
$self->{ndots} = $1; |
1000
|
|
|
|
|
|
|
} else { |
1001
|
|
|
|
|
|
|
# debug, rotate, no-check-names, inet6 |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
} |
1004
|
|
|
|
|
|
|
} else { |
1005
|
|
|
|
|
|
|
# silently skip stuff we don't understand |
1006
|
|
|
|
|
|
|
} |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
|
1009
|
2
|
50
|
|
|
|
7
|
$self->{timeout} = [($self->{timeout}[0]) x $attempts] |
1010
|
|
|
|
|
|
|
if $attempts; |
1011
|
|
|
|
|
|
|
|
1012
|
2
|
|
|
|
|
25
|
$self->_compile; |
1013
|
|
|
|
|
|
|
} |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
sub _load_resolv_conf_file { |
1016
|
2
|
|
|
2
|
|
7
|
my ($self, $resolv_conf) = @_; |
1017
|
|
|
|
|
|
|
|
1018
|
2
|
|
|
|
|
5
|
$self->_config_begin; |
1019
|
|
|
|
|
|
|
|
1020
|
2
|
|
|
|
|
12
|
require AnyEvent::IO; |
1021
|
|
|
|
|
|
|
AnyEvent::IO::aio_load ($resolv_conf, sub { |
1022
|
2
|
50
|
|
2
|
|
10
|
if (my ($contents) = @_) { |
1023
|
2
|
|
|
|
|
7
|
$self->parse_resolv_conf ($contents); |
1024
|
|
|
|
|
|
|
} else { |
1025
|
0
|
|
|
|
|
0
|
AE::log 4 => "$resolv_conf: $!"; |
1026
|
|
|
|
|
|
|
} |
1027
|
|
|
|
|
|
|
|
1028
|
2
|
|
|
|
|
33
|
$self->_config_done; |
1029
|
2
|
|
|
|
|
12
|
}); |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
=item $resolver->os_config |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
Tries so load and parse F on portable operating |
1035
|
|
|
|
|
|
|
systems. Tries various egregious hacks on windows to force the DNS servers |
1036
|
|
|
|
|
|
|
and searchlist out of the system. |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
This method must be called at most once before trying to resolve anything. |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
=cut |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
sub os_config { |
1043
|
2
|
|
|
2
|
1
|
5
|
my ($self) = @_; |
1044
|
|
|
|
|
|
|
|
1045
|
2
|
|
|
|
|
5
|
$self->_config_begin; |
1046
|
|
|
|
|
|
|
|
1047
|
2
|
|
|
|
|
5
|
$self->{server} = []; |
1048
|
2
|
|
|
|
|
6
|
$self->{search} = []; |
1049
|
|
|
|
|
|
|
|
1050
|
2
|
50
|
|
|
|
8
|
if ((AnyEvent::WIN32 || $^O =~ /cygwin/i)) { |
1051
|
|
|
|
|
|
|
# TODO: this blocks the program, but should not, but I |
1052
|
|
|
|
|
|
|
# am too lazy to implement and test it. need to boot windows. ugh. |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
#no strict 'refs'; |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
# there are many options to find the current nameservers etc. on windows |
1057
|
|
|
|
|
|
|
# all of them don't work consistently: |
1058
|
|
|
|
|
|
|
# - the registry thing needs separate code on win32 native vs. cygwin |
1059
|
|
|
|
|
|
|
# - the registry layout differs between windows versions |
1060
|
|
|
|
|
|
|
# - calling windows api functions doesn't work on cygwin |
1061
|
|
|
|
|
|
|
# - ipconfig uses locale-specific messages |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
# we use Net::DNS::Resolver first, and if it fails, will fall back to |
1064
|
|
|
|
|
|
|
# ipconfig parsing. |
1065
|
0
|
0
|
|
|
|
0
|
unless (eval { |
1066
|
|
|
|
|
|
|
# Net::DNS::Resolver uses a LOT of ram (~10mb), but what can we do :/ |
1067
|
|
|
|
|
|
|
# (this seems mostly to be due to Win32::API). |
1068
|
0
|
|
|
|
|
0
|
require Net::DNS::Resolver; |
1069
|
0
|
|
|
|
|
0
|
my $r = Net::DNS::Resolver->new; |
1070
|
|
|
|
|
|
|
|
1071
|
0
|
0
|
|
|
|
0
|
$r->nameservers |
1072
|
|
|
|
|
|
|
or die; |
1073
|
|
|
|
|
|
|
|
1074
|
0
|
|
|
|
|
0
|
for my $s ($r->nameservers) { |
1075
|
0
|
0
|
|
|
|
0
|
if (my $ipn = AnyEvent::Socket::parse_address ($s)) { |
1076
|
0
|
|
|
|
|
0
|
push @{ $self->{server} }, $ipn; |
|
0
|
|
|
|
|
0
|
|
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
} |
1079
|
0
|
|
|
|
|
0
|
$self->{search} = [$r->searchlist]; |
1080
|
|
|
|
|
|
|
|
1081
|
0
|
|
|
|
|
0
|
1 |
1082
|
|
|
|
|
|
|
}) { |
1083
|
|
|
|
|
|
|
# we use ipconfig parsing because, despite all its brokenness, |
1084
|
|
|
|
|
|
|
# it seems quite stable in practise. |
1085
|
|
|
|
|
|
|
# unfortunately it wants a console window. |
1086
|
|
|
|
|
|
|
# for good measure, we append a fallback nameserver to our list. |
1087
|
|
|
|
|
|
|
|
1088
|
0
|
0
|
|
|
|
0
|
if (open my $fh, "ipconfig /all |") { |
1089
|
|
|
|
|
|
|
# parsing strategy: we go through the output and look for |
1090
|
|
|
|
|
|
|
# :-lines with DNS in them. everything in those is regarded as |
1091
|
|
|
|
|
|
|
# either a nameserver (if it parses as an ip address), or a suffix |
1092
|
|
|
|
|
|
|
# (all else). |
1093
|
|
|
|
|
|
|
|
1094
|
0
|
|
|
|
|
0
|
my $dns; |
1095
|
0
|
|
|
|
|
0
|
local $_; |
1096
|
0
|
|
|
|
|
0
|
while (<$fh>) { |
1097
|
0
|
0
|
0
|
|
|
0
|
if (s/^\s.*\bdns\b.*://i) { |
|
|
0
|
|
|
|
|
|
1098
|
0
|
|
|
|
|
0
|
$dns = 1; |
1099
|
|
|
|
|
|
|
} elsif (/^\S/ || /^\s[^:]{16,}: /) { |
1100
|
0
|
|
|
|
|
0
|
$dns = 0; |
1101
|
|
|
|
|
|
|
} |
1102
|
0
|
0
|
0
|
|
|
0
|
if ($dns && /^\s*(\S+)\s*$/) { |
1103
|
0
|
|
|
|
|
0
|
my $s = $1; |
1104
|
0
|
|
|
|
|
0
|
$s =~ s/%\d+(?!\S)//; # get rid of ipv6 scope id |
1105
|
0
|
0
|
|
|
|
0
|
if (my $ipn = AnyEvent::Socket::parse_address ($s)) { |
1106
|
0
|
|
|
|
|
0
|
push @{ $self->{server} }, $ipn; |
|
0
|
|
|
|
|
0
|
|
1107
|
|
|
|
|
|
|
} else { |
1108
|
0
|
|
|
|
|
0
|
push @{ $self->{search} }, $s; |
|
0
|
|
|
|
|
0
|
|
1109
|
|
|
|
|
|
|
} |
1110
|
|
|
|
|
|
|
} |
1111
|
|
|
|
|
|
|
} |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
} |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
# always add the fallback servers on windows |
1116
|
0
|
|
|
|
|
0
|
push @{ $self->{server} }, @DNS_FALLBACK; |
|
0
|
|
|
|
|
0
|
|
1117
|
|
|
|
|
|
|
|
1118
|
0
|
|
|
|
|
0
|
$self->_config_done; |
1119
|
|
|
|
|
|
|
} else { |
1120
|
|
|
|
|
|
|
# try /etc/resolv.conf everywhere else |
1121
|
|
|
|
|
|
|
|
1122
|
2
|
|
|
|
|
975
|
require AnyEvent::IO; |
1123
|
|
|
|
|
|
|
AnyEvent::IO::aio_stat ("/etc/resolv.conf", sub { |
1124
|
2
|
50
|
|
2
|
|
15
|
$self->_load_resolv_conf_file ("/etc/resolv.conf") |
1125
|
|
|
|
|
|
|
if @_; |
1126
|
2
|
|
|
|
|
15
|
$self->_config_done; |
1127
|
2
|
|
|
|
|
15
|
}); |
1128
|
|
|
|
|
|
|
} |
1129
|
|
|
|
|
|
|
} |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
=item $resolver->timeout ($timeout, ...) |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
Sets the timeout values. See the C constructor argument (and |
1134
|
|
|
|
|
|
|
note that this method expects the timeout values themselves, not an |
1135
|
|
|
|
|
|
|
array-reference). |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
=cut |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
sub timeout { |
1140
|
0
|
|
|
0
|
1
|
0
|
my ($self, @timeout) = @_; |
1141
|
|
|
|
|
|
|
|
1142
|
0
|
|
|
|
|
0
|
$self->{timeout} = \@timeout; |
1143
|
0
|
|
|
|
|
0
|
$self->_compile; |
1144
|
|
|
|
|
|
|
} |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
=item $resolver->max_outstanding ($nrequests) |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
Sets the maximum number of outstanding requests to C<$nrequests>. See the |
1149
|
|
|
|
|
|
|
C constructor argument. |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=cut |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
sub max_outstanding { |
1154
|
0
|
|
|
0
|
1
|
0
|
my ($self, $max) = @_; |
1155
|
|
|
|
|
|
|
|
1156
|
0
|
|
|
|
|
0
|
$self->{max_outstanding} = $max; |
1157
|
0
|
|
|
|
|
0
|
$self->_compile; |
1158
|
|
|
|
|
|
|
} |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
sub _compile { |
1161
|
9
|
|
|
9
|
|
13
|
my $self = shift; |
1162
|
|
|
|
|
|
|
|
1163
|
9
|
|
|
|
|
12
|
my %search; $self->{search} = [grep 0 < length, grep !$search{$_}++, @{ $self->{search} }]; |
|
9
|
|
|
|
|
11
|
|
|
9
|
|
|
|
|
40
|
|
1164
|
9
|
|
|
|
|
15
|
my %server; $self->{server} = [grep 0 < length, grep !$server{$_}++, @{ $self->{server} }]; |
|
9
|
|
|
|
|
16
|
|
|
9
|
|
|
|
|
31
|
|
1165
|
|
|
|
|
|
|
|
1166
|
9
|
100
|
|
|
|
13
|
unless (@{ $self->{server} }) { |
|
9
|
|
|
|
|
22
|
|
1167
|
|
|
|
|
|
|
# use 127.0.0.1/::1 by default, add public nameservers as fallback |
1168
|
|
|
|
|
|
|
my $default = $AnyEvent::PROTOCOL{ipv6} > $AnyEvent::PROTOCOL{ipv4} |
1169
|
2
|
50
|
|
|
|
8
|
? "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" : "\x7f\x00\x00\x01"; |
1170
|
2
|
|
|
|
|
9
|
$self->{server} = [$default, @DNS_FALLBACK]; |
1171
|
|
|
|
|
|
|
} |
1172
|
|
|
|
|
|
|
|
1173
|
9
|
|
|
|
|
12
|
my @retry; |
1174
|
|
|
|
|
|
|
|
1175
|
9
|
|
|
|
|
12
|
for my $timeout (@{ $self->{timeout} }) { |
|
9
|
|
|
|
|
19
|
|
1176
|
25
|
|
|
|
|
29
|
for my $server (@{ $self->{server} }) { |
|
25
|
|
|
|
|
41
|
|
1177
|
74
|
|
|
|
|
133
|
push @retry, [$server, $timeout]; |
1178
|
|
|
|
|
|
|
} |
1179
|
|
|
|
|
|
|
} |
1180
|
|
|
|
|
|
|
|
1181
|
9
|
|
|
|
|
42
|
$self->{retry} = \@retry; |
1182
|
|
|
|
|
|
|
} |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
sub _feed { |
1185
|
5
|
|
|
5
|
|
14
|
my ($self, $res) = @_; |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
($res) = $res =~ /^(.*)$/s |
1188
|
5
|
|
|
|
|
8
|
if AnyEvent::TAINT && $self->{untaint}; |
1189
|
|
|
|
|
|
|
|
1190
|
5
|
50
|
|
|
|
20
|
$res = dns_unpack $res |
1191
|
|
|
|
|
|
|
or return; |
1192
|
|
|
|
|
|
|
|
1193
|
5
|
|
|
|
|
20
|
my $id = $self->{id}{$res->{id}}; |
1194
|
|
|
|
|
|
|
|
1195
|
5
|
50
|
|
|
|
24
|
return unless ref $id; |
1196
|
|
|
|
|
|
|
|
1197
|
5
|
|
|
|
|
13
|
$NOW = time; |
1198
|
5
|
|
|
|
|
23
|
$id->[1]->($res); |
1199
|
|
|
|
|
|
|
} |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
sub _recv { |
1202
|
5
|
|
|
5
|
|
22
|
my ($self, $pkt, $peer) = @_; |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# we ignore errors (often one gets port unreachable, but there is |
1205
|
|
|
|
|
|
|
# no good way to take advantage of that. |
1206
|
|
|
|
|
|
|
|
1207
|
5
|
|
|
|
|
28
|
my ($port, $host) = AnyEvent::Socket::unpack_sockaddr ($peer); |
1208
|
|
|
|
|
|
|
|
1209
|
5
|
50
|
33
|
|
|
28
|
return unless $port == DOMAIN_PORT && grep $_ eq $host, @{ $self->{server} }; |
|
5
|
|
|
|
|
56
|
|
1210
|
|
|
|
|
|
|
|
1211
|
5
|
|
|
|
|
30
|
$self->_feed ($pkt); |
1212
|
|
|
|
|
|
|
} |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
sub _free_id { |
1215
|
5
|
|
|
5
|
|
13
|
my ($self, $id, $timeout) = @_; |
1216
|
|
|
|
|
|
|
|
1217
|
5
|
50
|
|
|
|
15
|
if ($timeout) { |
1218
|
|
|
|
|
|
|
# we need to block the id for a while |
1219
|
0
|
|
|
|
|
0
|
$self->{id}{$id} = 1; |
1220
|
0
|
|
|
|
|
0
|
push @{ $self->{reuse_q} }, [$NOW + $self->{reuse}, $id]; |
|
0
|
|
|
|
|
0
|
|
1221
|
|
|
|
|
|
|
} else { |
1222
|
|
|
|
|
|
|
# we can quickly recycle the id |
1223
|
5
|
|
|
|
|
16
|
delete $self->{id}{$id}; |
1224
|
|
|
|
|
|
|
} |
1225
|
|
|
|
|
|
|
|
1226
|
5
|
|
|
|
|
11
|
--$self->{outstanding}; |
1227
|
5
|
|
|
|
|
18
|
$self->_scheduler; |
1228
|
|
|
|
|
|
|
} |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
# execute a single request, involves sending it with timeouts to multiple servers |
1231
|
|
|
|
|
|
|
sub _exec { |
1232
|
5
|
|
|
5
|
|
17
|
my ($self, $req) = @_; |
1233
|
|
|
|
|
|
|
|
1234
|
5
|
|
|
|
|
9
|
my $retry; # of retries |
1235
|
|
|
|
|
|
|
my $do_retry; |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
$do_retry = sub { |
1238
|
|
|
|
|
|
|
my $retry_cfg = $self->{retry}[$retry++] |
1239
|
5
|
50
|
|
5
|
|
20
|
or do { |
1240
|
|
|
|
|
|
|
# failure |
1241
|
0
|
|
|
|
|
0
|
$self->_free_id ($req->[2], $retry > 1); |
1242
|
0
|
|
|
|
|
0
|
undef $do_retry; return $req->[1]->(); |
|
0
|
|
|
|
|
0
|
|
1243
|
|
|
|
|
|
|
}; |
1244
|
|
|
|
|
|
|
|
1245
|
5
|
|
|
|
|
12
|
my ($server, $timeout) = @$retry_cfg; |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
$self->{id}{$req->[2]} = [(AE::timer $timeout, 0, sub { |
1248
|
0
|
|
|
|
|
0
|
$NOW = time; |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
# timeout, try next |
1251
|
0
|
0
|
|
|
|
0
|
&$do_retry if $do_retry; |
1252
|
|
|
|
|
|
|
}), sub { |
1253
|
5
|
|
|
|
|
12
|
my ($res) = @_; |
1254
|
|
|
|
|
|
|
|
1255
|
5
|
50
|
|
|
|
23
|
if ($res->{tc}) { |
1256
|
|
|
|
|
|
|
# success, but truncated, so use tcp |
1257
|
|
|
|
|
|
|
AnyEvent::Socket::tcp_connect (AnyEvent::Socket::format_address ($server), DOMAIN_PORT, sub { |
1258
|
0
|
0
|
|
|
|
0
|
return unless $do_retry; # some other request could have invalidated us already |
1259
|
|
|
|
|
|
|
|
1260
|
0
|
0
|
|
|
|
0
|
my ($fh) = @_ |
1261
|
|
|
|
|
|
|
or return &$do_retry; |
1262
|
|
|
|
|
|
|
|
1263
|
0
|
|
|
|
|
0
|
require AnyEvent::Handle; |
1264
|
|
|
|
|
|
|
|
1265
|
0
|
|
|
|
|
0
|
my $handle; $handle = new AnyEvent::Handle |
1266
|
|
|
|
|
|
|
fh => $fh, |
1267
|
|
|
|
|
|
|
timeout => $timeout, |
1268
|
|
|
|
|
|
|
on_error => sub { |
1269
|
0
|
|
|
|
|
0
|
undef $handle; |
1270
|
0
|
0
|
|
|
|
0
|
return unless $do_retry; # some other request could have invalidated us already |
1271
|
|
|
|
|
|
|
# failure, try next |
1272
|
0
|
|
|
|
|
0
|
&$do_retry; |
1273
|
0
|
|
|
|
|
0
|
}; |
1274
|
|
|
|
|
|
|
|
1275
|
0
|
|
|
|
|
0
|
$handle->push_write (pack "n/a*", $req->[0]); |
1276
|
|
|
|
|
|
|
$handle->push_read (chunk => 2, sub { |
1277
|
|
|
|
|
|
|
$handle->unshift_read (chunk => (unpack "n", $_[1]), sub { |
1278
|
0
|
|
|
|
|
0
|
undef $handle; |
1279
|
0
|
|
|
|
|
0
|
$self->_feed ($_[1]); |
1280
|
0
|
|
|
|
|
0
|
}); |
1281
|
0
|
|
|
|
|
0
|
}); |
1282
|
|
|
|
|
|
|
|
1283
|
0
|
|
|
|
|
0
|
}, sub { $timeout }); |
|
0
|
|
|
|
|
0
|
|
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
} else { |
1286
|
|
|
|
|
|
|
# success |
1287
|
5
|
|
|
|
|
25
|
$self->_free_id ($req->[2], $retry > 1); |
1288
|
5
|
|
|
|
|
11
|
undef $do_retry; return $req->[1]->($res); |
|
5
|
|
|
|
|
17
|
|
1289
|
|
|
|
|
|
|
} |
1290
|
5
|
|
|
|
|
36
|
}]; |
1291
|
|
|
|
|
|
|
|
1292
|
5
|
|
|
|
|
19
|
my $sa = AnyEvent::Socket::pack_sockaddr (DOMAIN_PORT, $server); |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
my $fh = AF_INET == AnyEvent::Socket::sockaddr_family ($sa) |
1295
|
|
|
|
|
|
|
? $self->{fh4} : $self->{fh6} |
1296
|
5
|
50
|
|
|
|
22
|
or return &$do_retry; |
|
|
50
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
|
1298
|
5
|
|
|
|
|
576
|
send $fh, $req->[0], 0, $sa; |
1299
|
5
|
|
|
|
|
45
|
}; |
1300
|
|
|
|
|
|
|
|
1301
|
5
|
|
|
|
|
11
|
&$do_retry; |
1302
|
|
|
|
|
|
|
} |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
sub _scheduler { |
1305
|
19
|
|
|
19
|
|
36
|
my ($self) = @_; |
1306
|
|
|
|
|
|
|
|
1307
|
19
|
100
|
|
|
|
69
|
return if $self->{inhibit}; |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
#no strict 'refs'; |
1310
|
|
|
|
|
|
|
|
1311
|
17
|
|
|
|
|
26
|
$NOW = time; |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
# first clear id reuse queue |
1314
|
0
|
|
|
|
|
0
|
delete $self->{id}{ (shift @{ $self->{reuse_q} })->[1] } |
1315
|
17
|
|
33
|
|
|
22
|
while @{ $self->{reuse_q} } && $self->{reuse_q}[0][0] <= $NOW; |
|
17
|
|
|
|
|
58
|
|
1316
|
|
|
|
|
|
|
|
1317
|
17
|
|
|
|
|
48
|
while ($self->{outstanding} < $self->{max_outstanding}) { |
1318
|
|
|
|
|
|
|
|
1319
|
27
|
50
|
|
|
|
41
|
if (@{ $self->{reuse_q} } >= 30000) { |
|
27
|
|
|
|
|
53
|
|
1320
|
|
|
|
|
|
|
# we ran out of ID's, wait a bit |
1321
|
|
|
|
|
|
|
$self->{reuse_to} ||= AE::timer $self->{reuse_q}[0][0] - $NOW, 0, sub { |
1322
|
0
|
|
|
0
|
|
0
|
delete $self->{reuse_to}; |
1323
|
0
|
|
|
|
|
0
|
$self->_scheduler; |
1324
|
0
|
|
0
|
|
|
0
|
}; |
1325
|
0
|
|
|
|
|
0
|
last; |
1326
|
|
|
|
|
|
|
} |
1327
|
|
|
|
|
|
|
|
1328
|
27
|
100
|
|
|
|
34
|
if (my $req = shift @{ $self->{queue} }) { |
|
27
|
100
|
|
|
|
57
|
|
1329
|
|
|
|
|
|
|
# found a request in the queue, execute it |
1330
|
5
|
|
|
|
|
7
|
while () { |
1331
|
5
|
|
|
|
|
16
|
$req->[2] = int rand 65536; |
1332
|
5
|
50
|
|
|
|
25
|
last unless exists $self->{id}{$req->[2]}; |
1333
|
|
|
|
|
|
|
} |
1334
|
|
|
|
|
|
|
|
1335
|
5
|
|
|
|
|
8
|
++$self->{outstanding}; |
1336
|
5
|
|
|
|
|
12
|
$self->{id}{$req->[2]} = 1; |
1337
|
5
|
|
|
|
|
16
|
substr $req->[0], 0, 2, pack "n", $req->[2]; |
1338
|
|
|
|
|
|
|
|
1339
|
5
|
|
|
|
|
13
|
$self->_exec ($req); |
1340
|
|
|
|
|
|
|
|
1341
|
22
|
|
|
|
|
54
|
} elsif (my $cb = shift @{ $self->{wait} }) { |
1342
|
|
|
|
|
|
|
# found a wait_for_slot callback |
1343
|
5
|
|
|
|
|
11
|
$cb->($self); |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
} else { |
1346
|
|
|
|
|
|
|
# nothing to do, just exit |
1347
|
17
|
|
|
|
|
66
|
last; |
1348
|
|
|
|
|
|
|
} |
1349
|
|
|
|
|
|
|
} |
1350
|
|
|
|
|
|
|
} |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
=item $resolver->request ($req, $cb->($res)) |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
This is the main low-level workhorse for sending DNS requests. |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
This function sends a single request (a hash-ref formated as specified |
1357
|
|
|
|
|
|
|
for C) to the configured nameservers in turn until it gets a |
1358
|
|
|
|
|
|
|
response. It handles timeouts, retries and automatically falls back to |
1359
|
|
|
|
|
|
|
virtual circuit mode (TCP) when it receives a truncated reply. It does not |
1360
|
|
|
|
|
|
|
handle anything else, such as the domain searchlist or relative names - |
1361
|
|
|
|
|
|
|
use C<< ->resolve >> for that. |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
Calls the callback with the decoded response packet if a reply was |
1364
|
|
|
|
|
|
|
received, or no arguments in case none of the servers answered. |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
=cut |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
sub request($$) { |
1369
|
5
|
|
|
5
|
1
|
14
|
my ($self, $req, $cb) = @_; |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
# _enc_name barfs on names that are too long, which is often outside |
1372
|
|
|
|
|
|
|
# program control, so check for too long names here. |
1373
|
5
|
|
|
|
|
7
|
for (@{ $req->{qd} }) { |
|
5
|
|
|
|
|
12
|
|
1374
|
0
|
|
|
0
|
|
0
|
return AE::postpone sub { $cb->(undef) } |
1375
|
5
|
50
|
|
|
|
23
|
if 255 < length $_->[0]; |
1376
|
|
|
|
|
|
|
} |
1377
|
|
|
|
|
|
|
|
1378
|
5
|
|
|
|
|
14
|
push @{ $self->{queue} }, [dns_pack $req, $cb]; |
|
5
|
|
|
|
|
15
|
|
1379
|
5
|
|
|
|
|
25
|
$self->_scheduler; |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
=item $resolver->resolve ($qname, $qtype, %options, $cb->(@rr)) |
1383
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
Queries the DNS for the given domain name C<$qname> of type C<$qtype>. |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
A C<$qtype> is either a numerical query type (e.g. C<1> for A records) or |
1387
|
|
|
|
|
|
|
a lowercase name (you have to look at the source to see which aliases are |
1388
|
|
|
|
|
|
|
supported, but all types from RFC 1035, C, C, C and a few |
1389
|
|
|
|
|
|
|
more are known to this module). A C<$qtype> of "*" is supported and means |
1390
|
|
|
|
|
|
|
"any" record type. |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
The callback will be invoked with a list of matching result records or |
1393
|
|
|
|
|
|
|
none on any error or if the name could not be found. |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
CNAME chains (although illegal) are followed up to a length of 10. |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
The callback will be invoked with arraryefs of the form C<[$name, |
1398
|
|
|
|
|
|
|
$type, $class, $ttl, @data>], where C<$name> is the domain name, |
1399
|
|
|
|
|
|
|
C<$type> a type string or number, C<$class> a class name, C<$ttl> is the |
1400
|
|
|
|
|
|
|
remaining time-to-live and C<@data> is resource-record-dependent data, in |
1401
|
|
|
|
|
|
|
seconds. For C records, this will be the textual IPv4 addresses, for |
1402
|
|
|
|
|
|
|
C or C records this will be a domain name, for C records |
1403
|
|
|
|
|
|
|
these are all the strings and so on. |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
All types mentioned in RFC 1035, C, C, C and C are |
1406
|
|
|
|
|
|
|
decoded. All resource records not known to this module will have the raw |
1407
|
|
|
|
|
|
|
C field as fifth array element. |
1408
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
Note that this resolver is just a stub resolver: it requires a name server |
1410
|
|
|
|
|
|
|
supporting recursive queries, will not do any recursive queries itself and |
1411
|
|
|
|
|
|
|
is not secure when used against an untrusted name server. |
1412
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
The following options are supported: |
1414
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
=over 4 |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
=item search => [$suffix...] |
1418
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
Use the given search list (which might be empty), by appending each one |
1420
|
|
|
|
|
|
|
in turn to the C<$qname>. If this option is missing then the configured |
1421
|
|
|
|
|
|
|
C and C values define its value (depending on C, the |
1422
|
|
|
|
|
|
|
empty suffix will be prepended or appended to that C value). If |
1423
|
|
|
|
|
|
|
the C<$qname> ends in a dot, then the searchlist will be ignored. |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
=item accept => [$type...] |
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
Lists the acceptable result types: only result types in this set will be |
1428
|
|
|
|
|
|
|
accepted and returned. The default includes the C<$qtype> and nothing |
1429
|
|
|
|
|
|
|
else. If this list includes C, then CNAME-chains will not be |
1430
|
|
|
|
|
|
|
followed (because you asked for the CNAME record). |
1431
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
=item class => "class" |
1433
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
Specify the query class ("in" for internet, "ch" for chaosnet and "hs" for |
1435
|
|
|
|
|
|
|
hesiod are the only ones making sense). The default is "in", of course. |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
=back |
1438
|
|
|
|
|
|
|
|
1439
|
|
|
|
|
|
|
Examples: |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
# full example, you can paste this into perl: |
1442
|
|
|
|
|
|
|
use Data::Dumper; |
1443
|
|
|
|
|
|
|
use AnyEvent::DNS; |
1444
|
|
|
|
|
|
|
AnyEvent::DNS::resolver->resolve ( |
1445
|
|
|
|
|
|
|
"google.com", "*", my $cv = AnyEvent->condvar); |
1446
|
|
|
|
|
|
|
warn Dumper [$cv->recv]; |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
# shortened result: |
1449
|
|
|
|
|
|
|
# [ |
1450
|
|
|
|
|
|
|
# [ 'google.com', 'soa', 'in', 3600, 'ns1.google.com', 'dns-admin.google.com', |
1451
|
|
|
|
|
|
|
# 2008052701, 7200, 1800, 1209600, 300 ], |
1452
|
|
|
|
|
|
|
# [ |
1453
|
|
|
|
|
|
|
# 'google.com', 'txt', 'in', 3600, |
1454
|
|
|
|
|
|
|
# 'v=spf1 include:_netblocks.google.com ~all' |
1455
|
|
|
|
|
|
|
# ], |
1456
|
|
|
|
|
|
|
# [ 'google.com', 'a', 'in', 3600, '64.233.187.99' ], |
1457
|
|
|
|
|
|
|
# [ 'google.com', 'mx', 'in', 3600, 10, 'smtp2.google.com' ], |
1458
|
|
|
|
|
|
|
# [ 'google.com', 'ns', 'in', 3600, 'ns2.google.com' ], |
1459
|
|
|
|
|
|
|
# ] |
1460
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
# resolve a records: |
1462
|
|
|
|
|
|
|
$res->resolve ("ruth.plan9.de", "a", sub { warn Dumper [@_] }); |
1463
|
|
|
|
|
|
|
|
1464
|
|
|
|
|
|
|
# result: |
1465
|
|
|
|
|
|
|
# [ |
1466
|
|
|
|
|
|
|
# [ 'ruth.schmorp.de', 'a', 'in', 86400, '129.13.162.95' ] |
1467
|
|
|
|
|
|
|
# ] |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
# resolve any records, but return only a and aaaa records: |
1470
|
|
|
|
|
|
|
$res->resolve ("test1.laendle", "*", |
1471
|
|
|
|
|
|
|
accept => ["a", "aaaa"], |
1472
|
|
|
|
|
|
|
sub { |
1473
|
|
|
|
|
|
|
warn Dumper [@_]; |
1474
|
|
|
|
|
|
|
} |
1475
|
|
|
|
|
|
|
); |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
# result: |
1478
|
|
|
|
|
|
|
# [ |
1479
|
|
|
|
|
|
|
# [ 'test1.laendle', 'a', 'in', 86400, '10.0.0.255' ], |
1480
|
|
|
|
|
|
|
# [ 'test1.laendle', 'aaaa', 'in', 60, '3ffe:1900:4545:0002:0240:0000:0000:f7e1' ] |
1481
|
|
|
|
|
|
|
# ] |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
=cut |
1484
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
sub resolve($%) { |
1486
|
5
|
|
|
5
|
1
|
12
|
my $cb = pop; |
1487
|
5
|
|
|
|
|
11
|
my ($self, $qname, $qtype, %opt) = @_; |
1488
|
|
|
|
|
|
|
|
1489
|
|
|
|
|
|
|
$self->wait_for_slot (sub { |
1490
|
5
|
|
|
5
|
|
7
|
my $self = shift; |
1491
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
my @search = $qname =~ s/\.$// |
1493
|
|
|
|
|
|
|
? "" |
1494
|
|
|
|
|
|
|
: $opt{search} |
1495
|
0
|
|
|
|
|
0
|
? @{ $opt{search} } |
1496
|
|
|
|
|
|
|
: ($qname =~ y/.//) >= $self->{ndots} |
1497
|
1
|
|
|
|
|
5
|
? ("", @{ $self->{search} }) |
1498
|
5
|
50
|
|
|
|
32
|
: (@{ $self->{search} }, ""); |
|
0
|
50
|
|
|
|
0
|
|
|
|
100
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
|
1500
|
5
|
|
50
|
|
|
21
|
my $class = $opt{class} || "in"; |
1501
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
my %atype = $opt{accept} |
1503
|
5
|
50
|
|
|
|
16
|
? map +($_ => 1), @{ $opt{accept} } |
|
0
|
|
|
|
|
0
|
|
1504
|
|
|
|
|
|
|
: ($qtype => 1); |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
# advance in searchlist |
1507
|
5
|
|
|
|
|
8
|
my ($do_search, $do_req); |
1508
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
$do_search = sub { |
1510
|
|
|
|
|
|
|
@search |
1511
|
9
|
100
|
|
|
|
37
|
or (undef $do_search), (undef $do_req), return $cb->(); |
1512
|
|
|
|
|
|
|
|
1513
|
5
|
|
|
|
|
28
|
(my $name = lc "$qname." . shift @search) =~ s/\.$//; |
1514
|
5
|
|
|
|
|
8
|
my $depth = 10; |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
# advance in cname-chain |
1517
|
|
|
|
|
|
|
$do_req = sub { |
1518
|
|
|
|
|
|
|
$self->request ({ |
1519
|
|
|
|
|
|
|
rd => 1, |
1520
|
|
|
|
|
|
|
qd => [[$name, $qtype, $class]], |
1521
|
|
|
|
|
|
|
}, sub { |
1522
|
5
|
50
|
|
|
|
15
|
my ($res) = @_ |
1523
|
|
|
|
|
|
|
or return $do_search->(); |
1524
|
|
|
|
|
|
|
|
1525
|
5
|
|
|
|
|
8
|
my $cname; |
1526
|
|
|
|
|
|
|
|
1527
|
5
|
|
|
|
|
7
|
while () { |
1528
|
|
|
|
|
|
|
# results found? |
1529
|
5
|
|
33
|
|
|
16
|
my @rr = grep $name eq lc $_->[0] && ($atype{"*"} || $atype{$_->[1]}), @{ $res->{an} }; |
|
5
|
|
|
|
|
23
|
|
1530
|
|
|
|
|
|
|
|
1531
|
5
|
100
|
|
|
|
17
|
(undef $do_search), (undef $do_req), return $cb->(@rr) |
1532
|
|
|
|
|
|
|
if @rr; |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
# see if there is a cname we can follow |
1535
|
4
|
|
0
|
|
|
6
|
my @rr = grep $name eq lc $_->[0] && $_->[1] eq "cname", @{ $res->{an} }; |
|
4
|
|
|
|
|
9
|
|
1536
|
|
|
|
|
|
|
|
1537
|
4
|
50
|
|
|
|
10
|
if (@rr) { |
|
|
50
|
|
|
|
|
|
1538
|
0
|
0
|
|
|
|
0
|
$depth-- |
1539
|
|
|
|
|
|
|
or return $do_search->(); # cname chain too long |
1540
|
|
|
|
|
|
|
|
1541
|
0
|
|
|
|
|
0
|
$cname = 1; |
1542
|
0
|
|
|
|
|
0
|
$name = lc $rr[0][4]; |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
} elsif ($cname) { |
1545
|
|
|
|
|
|
|
# follow the cname |
1546
|
0
|
|
|
|
|
0
|
return $do_req->(); |
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
} else { |
1549
|
|
|
|
|
|
|
# no, not found anything |
1550
|
4
|
|
|
|
|
31
|
return $do_search->(); |
1551
|
|
|
|
|
|
|
} |
1552
|
|
|
|
|
|
|
} |
1553
|
5
|
|
|
|
|
52
|
}); |
1554
|
5
|
|
|
|
|
19
|
}; |
1555
|
|
|
|
|
|
|
|
1556
|
5
|
|
|
|
|
8
|
$do_req->(); |
1557
|
5
|
|
|
|
|
26
|
}; |
1558
|
|
|
|
|
|
|
|
1559
|
5
|
|
|
|
|
12
|
$do_search->(); |
1560
|
5
|
|
|
|
|
30
|
}); |
1561
|
|
|
|
|
|
|
} |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
=item $resolver->wait_for_slot ($cb->($resolver)) |
1564
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
Wait until a free request slot is available and call the callback with the |
1566
|
|
|
|
|
|
|
resolver object. |
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
A request slot is used each time a request is actually sent to the |
1569
|
|
|
|
|
|
|
nameservers: There are never more than C of them. |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
Although you can submit more requests (they will simply be queued until |
1572
|
|
|
|
|
|
|
a request slot becomes available), sometimes, usually for rate-limiting |
1573
|
|
|
|
|
|
|
purposes, it is useful to instead wait for a slot before generating the |
1574
|
|
|
|
|
|
|
request (or simply to know when the request load is low enough so one can |
1575
|
|
|
|
|
|
|
submit requests again). |
1576
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
This is what this method does: The callback will be called when submitting |
1578
|
|
|
|
|
|
|
a DNS request will not result in that request being queued. The callback |
1579
|
|
|
|
|
|
|
may or may not generate any requests in response. |
1580
|
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
Note that the callback will only be invoked when the request queue is |
1582
|
|
|
|
|
|
|
empty, so this does not play well if somebody else keeps the request queue |
1583
|
|
|
|
|
|
|
full at all times. |
1584
|
|
|
|
|
|
|
|
1585
|
|
|
|
|
|
|
=cut |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
sub wait_for_slot { |
1588
|
5
|
|
|
5
|
1
|
13
|
my ($self, $cb) = @_; |
1589
|
|
|
|
|
|
|
|
1590
|
5
|
|
|
|
|
6
|
push @{ $self->{wait} }, $cb; |
|
5
|
|
|
|
|
10
|
|
1591
|
5
|
|
|
|
|
12
|
$self->_scheduler; |
1592
|
|
|
|
|
|
|
} |
1593
|
|
|
|
|
|
|
|
1594
|
8
|
|
|
8
|
|
1986
|
use AnyEvent::Socket (); # circular dependency, so do not import anything and do it at the end |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
527
|
|
1595
|
|
|
|
|
|
|
|
1596
|
|
|
|
|
|
|
=back |
1597
|
|
|
|
|
|
|
|
1598
|
|
|
|
|
|
|
=head1 AUTHOR |
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
Marc Lehmann |
1601
|
|
|
|
|
|
|
http://anyevent.schmorp.de |
1602
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
=cut |
1604
|
|
|
|
|
|
|
|
1605
|
|
|
|
|
|
|
1 |