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   1658 use strict;
  2         7  
  2         74  
4 2     2   17 use warnings;
  2         20  
  2         162  
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   15 use base qw(Net::DNS::Resolver);
  2         5  
  2         2788  
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 429 my ( undef, @argument ) = @_;
58 4 100       21 return @hints unless scalar @argument;
59 2         11 $root = [];
60 2         8 @hints = @argument;
61 2         6 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 23     23 1 558 my ( $self, @q ) = @_;
82 23         93 my @conf = ( recurse => 0, udppacketsize => 1024 ); # RFC8109
83 23         596 return bless( {persistent => {'.' => $root}, %$self, @conf}, ref($self) )->_send(@q);
84             }
85              
86              
87             sub query_dorecursion { ## historical
88 1     1 0 21 my ($self) = @_; # uncoverable pod
89 1         9 $self->_deprecate('prefer $resolver->send(...)');
90 1         4 return &send;
91             }
92              
93              
94             sub _send {
95 23     23   102 my ( $self, @q ) = @_;
96 23         137 my $query = $self->_make_query_packet(@q);
97              
98 23 100       101 unless ( scalar(@$root) ) {
99 5         29 $self->_diag("resolver priming query");
100 5 100       31 $self->nameservers( scalar(@hints) ? @hints : $self->_hints );
101 5         47 my $packet = $self->SUPER::send(qw(. NS));
102 5         26 $self->_callback($packet);
103 5         21 $self->_referral($packet);
104 5         103 $root = $self->{persistent}->{'.'};
105             }
106              
107 23         118 return $self->_recurse( $query, '.' );
108             }
109              
110              
111             sub _recurse {
112 62     62   221 my ( $self, $query, $apex ) = @_;
113 62         424 $self->_diag("using cached nameservers for $apex");
114 62         195 my $nslist = $self->{persistent}->{$apex};
115 62         424 $self->nameservers(@$nslist);
116 62         279 $query->header->id(undef);
117 62         374 my $reply = $self->SUPER::send($query);
118 62         459 $self->_callback($reply);
119 62 100       281 return unless $reply;
120 59         329 my $qname = lc( ( $query->question )[0]->qname );
121 59   100     721 my $zone = $self->_referral($reply) || return $reply;
122 46 100       190 return $reply if grep { lc( $_->owner ) eq $qname } $reply->answer;
  19         53  
123 39         217 return $self->_recurse( $query, $zone );
124             }
125              
126              
127             sub _referral {
128 64     64   215 my ( $self, $packet ) = @_;
129 64 100       244 return unless $packet;
130 61         286 my @ans = $packet->answer;
131 61         339 my @auth = grep { $_->type eq 'NS' } $packet->authority, @ans;
  397         1160  
132 61 100       2465 return unless scalar(@auth);
133 48         271 my $owner = lc( $auth[0]->owner );
134 48         221 my $cache = $self->{persistent}->{$owner};
135 48 100 100     366 return $owner if $cache && scalar(@$cache);
136 19         89 my @addr = grep { $_->can('address') } $packet->additional, @ans;
  259         694  
137 19         60 my @ip;
138 19         53 my @ns = map { lc( $_->nsdname ) } @auth;
  129         395  
139              
140 19         88 foreach my $ns (@ns) {
141 129 100       270 push @ip, map { $ns eq lc( $_->owner ) ? $_->address : () } @addr;
  2126         4748  
142             }
143 19 100       111 $self->_diag("resolving missing glue for $owner") unless scalar(@ip);
144 19 100       91 @ip = $self->nameservers( $ns[0], $ns[-1] ) unless scalar(@ip);
145 19         134 $self->_diag("caching nameserver addresses for $owner");
146 19         80 $self->{persistent}->{$owner} = \@ip;
147 19         130 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         26 for ( grep { ref($_) eq 'CODE' } @argument ) {
  3         19  
175 2         7 $self->{callback} = $_;
176             }
177 3         86 return;
178             }
179              
180             sub _callback {
181 67     67   280 my ( $self, @argument ) = @_;
182 67         294 my $callback = $self->{callback};
183 67 100       292 $callback->(@argument) if $callback;
184 67         181 return;
185             }
186              
187             sub recursion_callback { ## historical
188 1     1 0 28 my ($self) = @_; # uncoverable pod
189 1         5 $self->_deprecate('prefer $resolver->callback(...)');
190 1         4 &callback;
191 1         3 return;
192             }
193              
194              
195             1;
196              
197             __END__