line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package ParaDNS; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This is the query class - it is really just an encapsulation of the |
4
|
|
|
|
|
|
|
# hosts you want to query, plus the callback. All the hard work is done |
5
|
|
|
|
|
|
|
# in ParaDNS::Resolver. |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '2.0'; |
8
|
|
|
|
|
|
|
our $TIMEOUT = $ENV{PARADNS_TIMEOUT} || 10; |
9
|
|
|
|
|
|
|
our $REQUERY = $ENV{PARADNS_REQUERY} || 2; |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
|
|
6
|
use fields qw( |
12
|
|
|
|
|
|
|
client |
13
|
|
|
|
|
|
|
hosts |
14
|
|
|
|
|
|
|
num_hosts |
15
|
|
|
|
|
|
|
callback |
16
|
|
|
|
|
|
|
finished |
17
|
|
|
|
|
|
|
results |
18
|
|
|
|
|
|
|
start |
19
|
|
|
|
|
|
|
type |
20
|
|
|
|
|
|
|
nameservers |
21
|
1
|
|
|
1
|
|
1856
|
); |
|
1
|
|
|
|
|
1715
|
|
22
|
1
|
|
|
1
|
|
106
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
52
|
|
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
17
|
no warnings 'deprecated'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
43
|
|
25
|
|
|
|
|
|
|
|
26
|
1
|
|
|
1
|
|
729
|
use ParaDNS::Resolver; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
47
|
|
27
|
|
|
|
|
|
|
# note currently disabled as not everything works right |
28
|
1
|
|
|
1
|
|
5
|
use constant XS_AVAILABLE => eval { require ParaDNS::XS; }; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
483
|
|
29
|
1
|
|
50
|
1
|
|
5
|
use constant NO_DNS => ($ENV{NODNS} || 0); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
62
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my %RESOLVER; |
32
|
|
|
|
|
|
|
|
33
|
1
|
|
50
|
1
|
|
6
|
use constant TRACE_LEVEL => ($ENV{PARADNS_DEBUG} || 0); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
93
|
|
34
|
1
|
|
50
|
1
|
|
5
|
use constant INTERNAL_CACHE => !($ENV{PARADNS_NO_CACHE} || 0); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
57
|
|
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
1
|
|
6
|
use constant CACHE_CLEAN_INTERVAL => 60; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2164
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my %cache; |
39
|
|
|
|
|
|
|
my $cache_cleanup; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub trace { |
42
|
0
|
|
|
0
|
0
|
0
|
return unless TRACE_LEVEL; |
43
|
0
|
|
|
|
|
0
|
my $level = shift; |
44
|
0
|
|
|
|
|
0
|
print STDERR ("$ENV{PARADNS_DEBUG}/$level [$$] dns lookup: @_"); |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub get_resolver { |
48
|
4
|
|
|
4
|
0
|
5
|
if (INTERNAL_CACHE) { |
49
|
4
|
|
66
|
|
|
20
|
$cache_cleanup ||= Danga::Socket->AddTimer(CACHE_CLEAN_INTERVAL, \&_cache_cleanup); |
50
|
|
|
|
|
|
|
} |
51
|
4
|
50
|
|
|
|
40
|
if (XS_AVAILABLE) { |
52
|
0
|
0
|
|
|
|
0
|
return 1 if $RESOLVER{$$}; |
53
|
0
|
|
|
|
|
0
|
ParaDNS::XS::setup(); |
54
|
0
|
|
|
|
|
0
|
$RESOLVER{$$} = 1; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
else { |
57
|
4
|
|
|
|
|
7
|
my $servers = shift; |
58
|
4
|
|
66
|
|
|
31
|
$RESOLVER{$$} ||= ParaDNS::Resolver->new($servers); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub _cache_cleanup { |
63
|
0
|
|
|
0
|
|
0
|
my $now = time; |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
0
|
foreach my $type (keys(%cache)) { |
66
|
0
|
|
|
|
|
0
|
my @to_delete; |
67
|
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
0
|
keys %{$cache{$type}}; # reset internal iterator |
|
0
|
|
|
|
|
0
|
|
69
|
0
|
|
|
|
|
0
|
for my $query (keys(%{$cache{$type}})) { |
|
0
|
|
|
|
|
0
|
|
70
|
0
|
|
|
|
|
0
|
REC: |
71
|
0
|
|
|
|
|
0
|
for my $rec (@{ $cache{$type}{$query} }) { |
72
|
0
|
|
|
|
|
0
|
my $t = $rec->{timeout}; |
73
|
0
|
0
|
|
|
|
0
|
if ($t < $now) { |
74
|
0
|
|
|
|
|
0
|
push @to_delete, $query; |
75
|
0
|
|
|
|
|
0
|
last REC; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
0
|
|
|
|
|
0
|
foreach my $q (@to_delete) { |
81
|
0
|
|
|
|
|
0
|
delete $cache{$type}{$q}; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
0
|
$cache_cleanup = Danga::Socket->AddTimer(CACHE_CLEAN_INTERVAL, \&_cache_cleanup); |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub new { |
89
|
4
|
|
|
4
|
1
|
1875
|
my ParaDNS $self = shift; |
90
|
4
|
|
|
|
|
18
|
my %options = ( type => "A", @_ ); |
91
|
|
|
|
|
|
|
|
92
|
4
|
|
|
|
|
15
|
my $now = time; |
93
|
|
|
|
|
|
|
|
94
|
4
|
|
|
|
|
7
|
my $client = $options{client}; |
95
|
4
|
50
|
|
|
|
12
|
$client->pause_read() if $client; |
96
|
|
|
|
|
|
|
|
97
|
4
|
50
|
|
|
|
23
|
$self = fields::new($self) unless ref $self; |
98
|
|
|
|
|
|
|
|
99
|
4
|
50
|
|
|
|
4344
|
$self->{hosts} = $options{hosts} ? $options{hosts} : [ $options{host} ]; |
100
|
4
|
50
|
|
|
|
16
|
$self->{nameservers} = $options{nameservers} ? $options{nameservers} : ''; |
101
|
4
|
|
50
|
|
|
6
|
$self->{num_hosts} = scalar(@{$self->{hosts}}) || "No hosts supplied"; |
102
|
4
|
|
|
|
|
8
|
$self->{client} = $client; |
103
|
4
|
|
50
|
|
|
13
|
$self->{callback} = $options{callback} || die "No callback given"; |
104
|
4
|
|
|
|
|
8
|
$self->{finished} = $options{finished}; |
105
|
4
|
|
|
|
|
6
|
$self->{results} = {}; |
106
|
4
|
|
|
|
|
7
|
$self->{start} = $now; |
107
|
4
|
|
|
|
|
12
|
my $type = $self->{type} = $options{type}; |
108
|
|
|
|
|
|
|
|
109
|
4
|
50
|
|
|
|
11
|
trace(2, "Nameservers set to: @{$self->{nameservers}}\n") |
|
0
|
|
|
|
|
0
|
|
110
|
|
|
|
|
|
|
if $self->{nameservers}; |
111
|
|
|
|
|
|
|
|
112
|
4
|
|
|
|
|
5
|
if (NO_DNS) { |
113
|
|
|
|
|
|
|
$self->run_callback("NXDNS", $_) for @{ $self->{hosts} }; |
114
|
|
|
|
|
|
|
return $self; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
4
|
|
|
|
|
12
|
my $resolver = get_resolver($self->{nameservers}); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# check for cache hits |
120
|
4
|
|
|
|
|
7
|
for my $host (@{ $self->{hosts} }) { |
|
4
|
|
|
|
|
10
|
|
121
|
4
|
50
|
66
|
|
|
23
|
if (INTERNAL_CACHE && |
|
1
|
|
33
|
|
|
8
|
|
122
|
|
|
|
|
|
|
exists($cache{$type}{$host}) && |
123
|
|
|
|
|
|
|
@{$cache{$type}{$host}} > 0 && |
124
|
|
|
|
|
|
|
$cache{$type}{$host}[0]{timeout} >= $now) |
125
|
|
|
|
|
|
|
{ |
126
|
|
|
|
|
|
|
Danga::Socket->AddTimer(0, sub { |
127
|
0
|
|
|
0
|
|
0
|
$self->run_cache_callback($type, $host); |
128
|
0
|
|
|
|
|
0
|
}); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
else { |
131
|
|
|
|
|
|
|
# not cached - do lookup |
132
|
4
|
50
|
|
|
|
13
|
if (XS_AVAILABLE) { |
133
|
|
|
|
|
|
|
my $callback = sub { |
134
|
0
|
|
|
0
|
|
0
|
$self->run_xs_callback(@_); |
135
|
0
|
|
|
|
|
0
|
}; |
136
|
0
|
|
|
|
|
0
|
my $id; |
137
|
0
|
0
|
0
|
|
|
0
|
if ($type eq "A" && $host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { |
138
|
0
|
|
|
|
|
0
|
$id = ParaDNS::XS::dnsquery("PTR", "$4.$3.$2.$1.in-addr.arpa", $callback); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
else { |
141
|
0
|
|
|
|
|
0
|
$id = ParaDNS::XS::dnsquery($type, $host, $callback); |
142
|
|
|
|
|
|
|
} |
143
|
0
|
0
|
|
|
|
0
|
if (!defined($id)) { |
144
|
|
|
|
|
|
|
# lookup failed for some bizarre reason - should never happen |
145
|
0
|
0
|
|
|
|
0
|
$client->continue_read() if $client; |
146
|
0
|
|
|
|
|
0
|
return; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
else { |
150
|
4
|
|
|
|
|
11
|
$cache{$type}{$host} = []; |
151
|
4
|
50
|
|
|
|
17
|
if (!$resolver->query_type($self, $type, $host)) { |
152
|
|
|
|
|
|
|
# lookup failed for some bizarre reason - should never happen |
153
|
4
|
50
|
|
|
|
12
|
$client->continue_read() if $client; |
154
|
4
|
|
|
|
|
25
|
return; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
return $self; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub run_cache_callback { |
164
|
0
|
|
|
0
|
0
|
0
|
my ParaDNS $self = shift; |
165
|
0
|
|
|
|
|
0
|
my ($type, $host) = @_; |
166
|
0
|
|
|
|
|
0
|
for my $rec (@{ $cache{$type}{$host} }) { |
|
0
|
|
|
|
|
0
|
|
167
|
0
|
|
|
|
|
0
|
my $result = $rec->{value}; |
168
|
0
|
|
|
|
|
0
|
my $ttl = $rec->{ttl}; |
169
|
0
|
|
|
|
|
0
|
$self->{results}{$host} = $result; |
170
|
0
|
|
0
|
|
|
0
|
$ttl ||= 0; |
171
|
0
|
|
|
|
|
0
|
trace(2, "got cached $type $host => $result\n") if TRACE_LEVEL >= 2; |
172
|
0
|
|
|
|
|
0
|
$self->{callback}->($result, $host, $ttl); |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub run_callback { |
177
|
0
|
|
|
0
|
0
|
0
|
my ParaDNS $self = shift; |
178
|
0
|
|
|
|
|
0
|
my ($result, $query, $ttl) = @_; |
179
|
0
|
|
|
|
|
0
|
$self->{results}{$query} = $result; |
180
|
0
|
0
|
|
|
|
0
|
if (INTERNAL_CACHE && defined($ttl)) { |
181
|
|
|
|
|
|
|
# store in cache |
182
|
0
|
|
|
|
|
0
|
push @{$cache{$self->{type}}{$query}}, |
|
0
|
|
|
|
|
0
|
|
183
|
|
|
|
|
|
|
{ |
184
|
|
|
|
|
|
|
timeout => time + $ttl, |
185
|
|
|
|
|
|
|
ttl => $ttl, |
186
|
|
|
|
|
|
|
value => $result, |
187
|
|
|
|
|
|
|
}; |
188
|
|
|
|
|
|
|
} |
189
|
0
|
|
0
|
|
|
0
|
$ttl ||= 0; |
190
|
0
|
|
|
|
|
0
|
trace(2, "got $query => $result\n") if TRACE_LEVEL >= 2; |
191
|
0
|
|
|
|
|
0
|
$self->{callback}->($result, $query, $ttl); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
my %type_to_host = ( |
195
|
|
|
|
|
|
|
PTR => 'dname', |
196
|
|
|
|
|
|
|
A => 'address', |
197
|
|
|
|
|
|
|
AAAA => 'address', |
198
|
|
|
|
|
|
|
TXT => 'txtdata', |
199
|
|
|
|
|
|
|
NS => 'dname', |
200
|
|
|
|
|
|
|
CNAME => 'dname', |
201
|
|
|
|
|
|
|
); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub run_xs_callback { |
204
|
0
|
|
|
0
|
0
|
0
|
my ParaDNS $self = shift; |
205
|
0
|
|
|
|
|
0
|
my $data = shift; |
206
|
|
|
|
|
|
|
#warn("$$ run_xs_callback status: $data->{status} => $data->{error}\n"); |
207
|
0
|
0
|
|
|
|
0
|
if ($data->{status} > 1) { |
208
|
0
|
0
|
|
|
|
0
|
if ($data->{questions}) { |
209
|
|
|
|
|
|
|
#warn("$$ run_xs_callback $data->{error} with questions\n"); |
210
|
0
|
|
|
|
|
0
|
for my $q (@{$data->{questions}}) { |
|
0
|
|
|
|
|
0
|
|
211
|
0
|
|
|
|
|
0
|
trace(2, "got $q->{question} => $data->{error}\n") if TRACE_LEVEL >= 2; |
212
|
0
|
|
|
|
|
0
|
$self->{results}{$q->{question}} = $data->{error}; |
213
|
0
|
|
|
|
|
0
|
$self->{callback}->($data->{error}, $q->{question}); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
else { |
217
|
|
|
|
|
|
|
#warn("$$ run_xs_callback $data->{error} with no questions\n"); |
218
|
0
|
|
|
|
|
0
|
for my $host (@{$self->{hosts}}) { |
|
0
|
|
|
|
|
0
|
|
219
|
0
|
0
|
|
|
|
0
|
next if exists $self->{results}{$host}; |
220
|
0
|
|
|
|
|
0
|
trace(2, "got $host => $data->{error}\n") if TRACE_LEVEL >= 2; |
221
|
0
|
|
|
|
|
0
|
$self->{results}{$host} = $data->{error}; |
222
|
0
|
|
|
|
|
0
|
$self->{callback}->($data->{error}, $host); |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
} |
225
|
0
|
|
|
|
|
0
|
return; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
0
|
my $query = $data->{questions}[0]{question}; |
229
|
0
|
0
|
|
|
|
0
|
if ($data->{questions}[0]{type} eq 'PTR') { |
230
|
0
|
|
|
|
|
0
|
$query =~ s/^(\d+)\.(\d+)\.(\d+)\.(\d+)\.in-addr\.arpa/$4.$3.$2.$1/; |
231
|
|
|
|
|
|
|
} |
232
|
0
|
|
|
|
|
0
|
for my $answer (@{$data->{answers}}) { |
|
0
|
|
|
|
|
0
|
|
233
|
0
|
|
|
|
|
0
|
my $result; |
234
|
0
|
0
|
|
|
|
0
|
if (my $param = $type_to_host{$answer->{type}}) { |
|
|
0
|
|
|
|
|
|
235
|
0
|
|
|
|
|
0
|
$result = $answer->{$param}; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
elsif ($answer->{type} eq "MX") { |
238
|
0
|
|
|
|
|
0
|
$result = [$answer->{exchange}, $answer->{preference}]; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
else { |
241
|
0
|
|
|
|
|
0
|
die "Unimplemented query type: $answer->{type}"; |
242
|
|
|
|
|
|
|
} |
243
|
0
|
|
|
|
|
0
|
$self->run_callback($result, $query, $answer->{ttl}); |
244
|
|
|
|
|
|
|
} |
245
|
0
|
0
|
|
|
|
0
|
if (!$self->{results}{$query}) { |
246
|
0
|
|
|
|
|
0
|
$self->{results}{$query} = 'NXDOMAIN'; |
247
|
0
|
|
|
|
|
0
|
trace(2, "got $query => NXDOMAIN\n") if TRACE_LEVEL >= 2; |
248
|
0
|
|
|
|
|
0
|
$self->{callback}->("NXDOMAIN", $query); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub DESTROY { |
253
|
4
|
|
|
4
|
|
5
|
my ParaDNS $self = shift; |
254
|
4
|
|
|
|
|
6
|
my $now = time; |
255
|
4
|
|
|
|
|
6
|
my $num_hosts = @{$self->{hosts}}; |
|
4
|
|
|
|
|
9
|
|
256
|
4
|
50
|
|
|
|
5
|
if ($num_hosts > keys(%{$self->{results}})) { |
|
4
|
|
|
|
|
15
|
|
257
|
|
|
|
|
|
|
# not enough results came back |
258
|
4
|
|
|
|
|
5
|
foreach my $host (@{$self->{hosts}}) { |
|
4
|
|
|
|
|
10
|
|
259
|
4
|
50
|
|
|
|
12
|
next if exists($self->{results}{$host}); |
260
|
4
|
50
|
|
|
|
14
|
if ($host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { |
261
|
0
|
0
|
|
|
|
0
|
next if exists($self->{results}{"$4.$3.$2.$1.in-addr.arpa"}); |
262
|
|
|
|
|
|
|
} |
263
|
4
|
|
|
|
|
21
|
print STDERR "DNS failure looking for $host after " . ($now - $self->{start}) . " secs (looked for $num_hosts, got " . keys(%{$self->{results}}) . ")\n"; |
|
4
|
|
|
|
|
350
|
|
264
|
4
|
|
|
|
|
28
|
$self->{callback}->("NXDOMAIN", $host); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
} |
267
|
4
|
50
|
|
|
|
2396
|
$self->{client}->continue_read() if $self->{client}; |
268
|
4
|
50
|
|
|
|
32
|
if ($self->{finished}) { |
269
|
0
|
|
|
|
|
|
$self->{finished}->(); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
1; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head1 NAME |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
ParaDNS - a DNS lookup class for the Danga::Socket framework |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=head1 SYNOPSIS |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
ParaDNS->new( |
282
|
|
|
|
|
|
|
callback => sub { print "Got result $_[0] for query $_[1]\n" }, |
283
|
|
|
|
|
|
|
host => 'google.com', |
284
|
|
|
|
|
|
|
); |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=head1 DESCRIPTION |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
This module performs asynchronous DNS lookups, making use of a single UDP |
289
|
|
|
|
|
|
|
socket (unlike Net::DNS's bgsend/bgread combination). It uses the Danga::Socket |
290
|
|
|
|
|
|
|
framework for high performance. |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Currently this module will only perform A or PTR lookups. A rDNS (PTR) lookup |
293
|
|
|
|
|
|
|
will be performed if the host matches the regexp: C^\d+\.\d+\.\d+.\d+$/>. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
The lookups time out after 15 seconds. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head1 API |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 C<< ParaDNS->new( %options ) >> |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Create a new DNS query. You do not need to store the resulting object as this |
302
|
|
|
|
|
|
|
class is all done with callbacks. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Example: |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
ParaDNS->new( |
307
|
|
|
|
|
|
|
callback => sub { print "Got result: $_[0]\n" }, |
308
|
|
|
|
|
|
|
host => 'google.com', |
309
|
|
|
|
|
|
|
); |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=over 4 |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=item B<[required]> C |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
The callback to call when results come in. This should be a reference to a |
316
|
|
|
|
|
|
|
subroutine. The callback receives three parameters - the result of the DNS lookup, |
317
|
|
|
|
|
|
|
the host that was looked up, and the TTL (in seconds). |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item C |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
A host name to lookup. Note that if the hostname is a dotted quad of numbers then |
322
|
|
|
|
|
|
|
a reverse DNS (PTR) lookup is performend. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=item C |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
An array-ref list of hosts to lookup. |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
B One of either C or C is B. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=item C |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
It is possible to specify a client object which you wish to "pause" for reading |
333
|
|
|
|
|
|
|
until your DNS result returns. The client will be issued the C<< ->pause_read >> |
334
|
|
|
|
|
|
|
method when the query is issued, and the C<< ->continue_read >> method when the |
335
|
|
|
|
|
|
|
query returns. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
This is used in Qpsmtpd where we want to wait until the DNS query returns before |
338
|
|
|
|
|
|
|
accepting more data from the client. |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=item C |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
You can specify one of: I<"A">, I<"AAAA">, I<"PTR">, I<"CNAME">, I<"NS"> or |
343
|
|
|
|
|
|
|
I<"TXT"> here. Other types may be supported in the future. See C<%type_to_host> |
344
|
|
|
|
|
|
|
in C for details, though more complex queries (e.g. SRV) may |
345
|
|
|
|
|
|
|
require a slightly more complex solution. |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
A PTR query is automatically issued if the host looks like an IP address. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=item C |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
Normally, this module uses the name servers that are default for your system. |
352
|
|
|
|
|
|
|
You can specify an array-ref list of name servers to query. |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=back |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=head1 Environment Variables |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=head2 PARADNS_TIMEOUT |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Default: 10 |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Number of seconds to wait for a query to come back. |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=head2 PARADNS_REQUERY |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Default: 2 |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
Number of times to re-send a query when it times out. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=head2 PARADNS_NO_CACHE |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Provides the ability to turn off the in-memory cache. Set to 1 to disable. |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=head2 PARADNS_DEBUG |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
Provides internal debugging sent to STDERR. Set to 1 or higher to see more |
377
|
|
|
|
|
|
|
debugging output. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head1 Stand-alone Use |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Normal usage of ParaDNS is within another application that already uses the |
382
|
|
|
|
|
|
|
Danga::Socket framework. However if you wish to use this as a script to just |
383
|
|
|
|
|
|
|
issue thousands of DNS queries then you need to do a little more work. |
384
|
|
|
|
|
|
|
First, you need to set the SetPostLoopCallback, then issue the appropriate |
385
|
|
|
|
|
|
|
ParaDNS->new() call with your queries, and then launch the Danga event |
386
|
|
|
|
|
|
|
loop. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
Eg: |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
Danga::Socket->SetPostLoopCallback( |
391
|
|
|
|
|
|
|
sub { |
392
|
|
|
|
|
|
|
my $dmap = shift; |
393
|
|
|
|
|
|
|
for my $fd (keys %$dmap) { |
394
|
|
|
|
|
|
|
my $pob = $dmap->{$fd}; |
395
|
|
|
|
|
|
|
if ($pob->isa('ParaDNS::Resolver')) { |
396
|
|
|
|
|
|
|
return 1 if $pob->pending; |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
return 0; # causes EventLoop to exit |
400
|
|
|
|
|
|
|
}); |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# Call ParaDNS->new() with your parameters |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Danga::Socket->EventLoop(); |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head1 LICENSE |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
This module is licensed under the same terms as perl itself. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=head1 AUTHOR |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Matt Sergeant, . |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=cut |