File Coverage

blib/lib/Net/DNS/Resolver/Recurse.pm
Criterion Covered Total %
statement 78 78 100.0
branch 24 24 100.0
condition 4 4 100.0
subroutine 12 12 100.0
pod 3 5 100.0
total 121 123 100.0


line stmt bran cond sub pod time code
1             package Net::DNS::Resolver::Recurse;
2              
3 2     2   1517 use strict;
  2         5  
  2         81  
4 2     2   13 use warnings;
  2         17  
  2         165  
5             our $VERSION = (qw$Id: Recurse.pm 1930 2023-08-21 14:10:10Z willem $)[2];
6              
7              
8             =head1 NAME
9              
10             Net::DNS::Resolver::Recurse - DNS recursive resolver
11              
12              
13             =head1 SYNOPSIS
14              
15             use Net::DNS::Resolver::Recurse;
16              
17             my $resolver = new Net::DNS::Resolver::Recurse();
18             $resolver->debug(1);
19              
20             $resolver->hints('198.41.0.4'); # A.ROOT-SERVER.NET.
21              
22             my $packet = $resolver->send( 'www.rob.com.au.', 'A' );
23              
24              
25             =head1 DESCRIPTION
26              
27             This module is a subclass of Net::DNS::Resolver.
28              
29             =cut
30              
31              
32 2     2   20 use base qw(Net::DNS::Resolver);
  2         8  
  2         2607  
33              
34              
35             =head1 METHODS
36              
37             This module inherits almost all the methods from Net::DNS::Resolver.
38             Additional module-specific methods are described below.
39              
40              
41             =head2 hints
42              
43             This method specifies a list of the IP addresses of nameservers to
44             be used to discover the addresses of the root nameservers.
45              
46             $resolver->hints(@ip);
47              
48             If no hints are passed, the priming query is directed to nameservers
49             drawn from a built-in list of IP addresses.
50              
51             =cut
52              
53             my @hints;
54             my $root = [];
55              
56             sub hints {
57 4     4 1 463 my ( undef, @argument ) = @_;
58 4 100       23 return @hints unless scalar @argument;
59 2         8 $root = [];
60 2         8 @hints = @argument;
61 2         7 return;
62             }
63              
64              
65             =head2 query, search, send
66              
67             The query(), search() and send() methods produce the same result
68             as their counterparts in Net::DNS::Resolver.
69              
70             $packet = $resolver->send( 'www.example.com.', 'A' );
71              
72             Server-side recursion is suppressed by clearing the recurse flag in
73             query packets and recursive name resolution is performed explicitly.
74              
75             The query() and search() methods are inherited from Net::DNS::Resolver
76             and invoke send() indirectly.
77              
78             =cut
79              
80             sub send {
81 27     27 1 1048 my ( $self, @q ) = @_;
82 27         105 my @conf = ( recurse => 0, udppacketsize => 1024 ); # RFC8109
83 27         755 return bless( {persistent => {'.' => $root}, %$self, @conf}, ref($self) )->_send(@q);
84             }
85              
86              
87             sub query_dorecursion { ## historical
88 1     1 0 23 my ($self) = @_; # uncoverable pod
89 1         9 $self->_deprecate('prefer $resolver->send(...)');
90 1         13 return &send;
91             }
92              
93              
94             sub _send {
95 27     27   110 my ( $self, @q ) = @_;
96 27         173 my $query = $self->_make_query_packet(@q);
97              
98 27 100       158 unless ( scalar(@$root) ) {
99 5         41 $self->_diag("resolver priming query");
100 5 100       37 $self->nameservers( scalar(@hints) ? @hints : $self->_hints );
101 5         40 my $packet = $self->SUPER::send(qw(. NS));
102 5         37 $self->_callback($packet);
103 5         17 $self->_referral($packet);
104 5         101 $root = $self->{persistent}->{'.'};
105             }
106              
107 27         137 return $self->_recurse( $query, '.' );
108             }
109              
110              
111             sub _recurse {
112 74     74   220 my ( $self, $query, $apex ) = @_;
113 74         367 $self->_diag("using cached nameservers for $apex");
114 74         197 my $nslist = $self->{persistent}->{$apex};
115 74         389 $self->nameservers(@$nslist);
116 74         309 $query->header->id(undef);
117 74         348 my $reply = $self->SUPER::send($query);
118 74         415 $self->_callback($reply);
119 74 100       241 return unless $reply;
120 71         250 my $qname = lc( ( $query->question )[0]->qname );
121 71   100     266 my $zone = $self->_referral($reply) || return $reply;
122 63 100       249 return $reply if grep { lc( $_->owner ) eq $qname } $reply->answer;
  29         111  
123 47         195 return $self->_recurse( $query, $zone );
124             }
125              
126              
127             sub _referral {
128 76     76   227 my ( $self, $packet ) = @_;
129 76 100       310 return unless $packet;
130 73         265 my @ans = $packet->answer;
131 73         235 my @auth = grep { $_->type eq 'NS' } $packet->authority, @ans;
  466         1180  
132 73 100       1356 return unless scalar(@auth);
133 65         256 my $owner = lc( $auth[0]->owner );
134 65         206 my $cache = $self->{persistent}->{$owner};
135 65 100 100     484 return $owner if $cache && scalar(@$cache);
136 21         121 my @addr = grep { $_->can('address') } $packet->additional, @ans;
  269         718  
137 21         56 my @ip;
138 21         57 my @ns = map { lc( $_->nsdname ) } @auth;
  137         364  
139              
140 21         96 foreach my $ns (@ns) {
141 137 100       264 push @ip, map { $ns eq lc( $_->owner ) ? $_->address : () } @addr;
  2172         4615  
142             }
143 21 100       132 $self->_diag("resolving missing glue for $owner") unless scalar(@ip);
144 21 100       93 @ip = $self->nameservers( $ns[0], $ns[-1] ) unless scalar(@ip);
145 21         130 $self->_diag("caching nameserver addresses for $owner");
146 21         76 $self->{persistent}->{$owner} = \@ip;
147 21         139 return $owner;
148             }
149              
150              
151             =head2 callback
152              
153             This method specifies a code reference to a subroutine,
154             which is then invoked at each stage of the recursive lookup.
155              
156             For example to emulate dig's C<+trace> function:
157              
158             my $coderef = sub {
159             my $packet = shift;
160              
161             printf ";; Received %d bytes from %s\n\n",
162             $packet->answersize, $packet->answerfrom;
163             };
164              
165             $resolver->callback($coderef);
166              
167             The callback subroutine is not called
168             for queries for missing glue records.
169              
170             =cut
171              
172             sub callback {
173 3     3 1 31 my ( $self, @argument ) = @_;
174 3         11 for ( grep { ref($_) eq 'CODE' } @argument ) {
  3         14  
175 2         8 $self->{callback} = $_;
176             }
177 3         10 return;
178             }
179              
180             sub _callback {
181 79     79   222 my ( $self, @argument ) = @_;
182 79         188 my $callback = $self->{callback};
183 79 100       233 $callback->(@argument) if $callback;
184 79         171 return;
185             }
186              
187             sub recursion_callback { ## historical
188 1     1 0 23 my ($self) = @_; # uncoverable pod
189 1         5 $self->_deprecate('prefer $resolver->callback(...)');
190 1         5 &callback;
191 1         3 return;
192             }
193              
194              
195             1;
196              
197             __END__