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   1823 use strict;
  2         12  
  2         96  
4 2     2   23 use warnings;
  2         8  
  2         157  
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   14 use base qw(Net::DNS::Resolver);
  2         7  
  2         2937  
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 434 my ( undef, @argument ) = @_;
58 4 100       22 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 534 my ( $self, @q ) = @_;
82 27         108 my @conf = ( recurse => 0, udppacketsize => 1024 ); # RFC8109
83 27         717 return bless( {persistent => {'.' => $root}, %$self, @conf}, ref($self) )->_send(@q);
84             }
85              
86              
87             sub query_dorecursion { ## historical
88 1     1 0 25 my ($self) = @_; # uncoverable pod
89 1         11 $self->_deprecate('prefer $resolver->send(...)');
90 1         13 return &send;
91             }
92              
93              
94             sub _send {
95 27     27   113 my ( $self, @q ) = @_;
96 27         141 my $query = $self->_make_query_packet(@q);
97              
98 27 100       135 unless ( scalar(@$root) ) {
99 5         30 $self->_diag("resolver priming query");
100 5 100       30 $self->nameservers( scalar(@hints) ? @hints : $self->_hints );
101 5         50 my $packet = $self->SUPER::send(qw(. NS));
102 5         36 $self->_callback($packet);
103 5         36 $self->_referral($packet);
104 5         92 $root = $self->{persistent}->{'.'};
105             }
106              
107 27         127 return $self->_recurse( $query, '.' );
108             }
109              
110              
111             sub _recurse {
112 74     74   245 my ( $self, $query, $apex ) = @_;
113 74         403 $self->_diag("using cached nameservers for $apex");
114 74         206 my $nslist = $self->{persistent}->{$apex};
115 74         389 $self->nameservers(@$nslist);
116 74         319 $query->header->id(undef);
117 74         388 my $reply = $self->SUPER::send($query);
118 74         385 $self->_callback($reply);
119 74 100       296 return unless $reply;
120 71         326 my $qname = lc( ( $query->question )[0]->qname );
121 71   100     292 my $zone = $self->_referral($reply) || return $reply;
122 59 100       236 return $reply if grep { lc( $_->owner ) eq $qname } $reply->answer;
  25         151  
123 47         237 return $self->_recurse( $query, $zone );
124             }
125              
126              
127             sub _referral {
128 76     76   201 my ( $self, $packet ) = @_;
129 76 100       223 return unless $packet;
130 73         319 my @ans = $packet->answer;
131 73         257 my @auth = grep { $_->type eq 'NS' } $packet->authority, @ans;
  436         1257  
132 73 100       1971 return unless scalar(@auth);
133 61         265 my $owner = lc( $auth[0]->owner );
134 61         253 my $cache = $self->{persistent}->{$owner};
135 61 100 100     486 return $owner if $cache && scalar(@$cache);
136 22         114 my @addr = grep { $_->can('address') } $packet->additional, @ans;
  272         710  
137 22         62 my @ip;
138 22         60 my @ns = map { lc( $_->nsdname ) } @auth;
  136         378  
139              
140 22         92 foreach my $ns (@ns) {
141 136 100       269 push @ip, map { $ns eq lc( $_->owner ) ? $_->address : () } @addr;
  2130         4374  
142             }
143 22 100       145 $self->_diag("resolving missing glue for $owner") unless scalar(@ip);
144 22 100       102 @ip = $self->nameservers( $ns[0], $ns[-1] ) unless scalar(@ip);
145 22         135 $self->_diag("caching nameserver addresses for $owner");
146 22         102 $self->{persistent}->{$owner} = \@ip;
147 22         244 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 44 my ( $self, @argument ) = @_;
174 3         9 for ( grep { ref($_) eq 'CODE' } @argument ) {
  3         16  
175 2         7 $self->{callback} = $_;
176             }
177 3         8 return;
178             }
179              
180             sub _callback {
181 79     79   236 my ( $self, @argument ) = @_;
182 79         211 my $callback = $self->{callback};
183 79 100       261 $callback->(@argument) if $callback;
184 79         193 return;
185             }
186              
187             sub recursion_callback { ## historical
188 1     1 0 27 my ($self) = @_; # uncoverable pod
189 1         5 $self->_deprecate('prefer $resolver->callback(...)');
190 1         9 &callback;
191 1         2 return;
192             }
193              
194              
195             1;
196              
197             __END__