File Coverage

blib/lib/Zonemaster/Engine/Test/Address.pm
Criterion Covered Total %
statement 112 115 97.3
branch 34 42 80.9
condition 17 26 65.3
subroutine 20 20 100.0
pod 8 8 100.0
total 191 211 90.5


line stmt bran cond sub pod time code
1             package Zonemaster::Engine::Test::Address;
2              
3 26     26   153 use version; our $VERSION = version->declare("v1.0.3");
  26         57  
  26         129  
4              
5 26     26   1969 use strict;
  26         60  
  26         519  
6 26     26   121 use warnings;
  26         54  
  26         604  
7              
8 26     26   378 use 5.014002;
  26         86  
9              
10 26     26   140 use Zonemaster::Engine;
  26         55  
  26         459  
11 26     26   118 use Zonemaster::Engine::Util;
  26         53  
  26         1388  
12 26     26   144 use Zonemaster::Engine::TestMethods;
  26         52  
  26         594  
13 26     26   1209 use Zonemaster::Engine::Constants qw[:addresses :ip];
  26         56  
  26         4414  
14 26     26   182 use List::MoreUtils qw[none any];
  26         50  
  26         221  
15              
16 26     26   16742 use Carp;
  26         61  
  26         26419  
17              
18             ###
19             ### Entry Points
20             ###
21              
22             sub all {
23 6     6 1 18 my ( $class, $zone ) = @_;
24 6         13 my @results;
25              
26 6 50       21 push @results, $class->address01( $zone ) if Zonemaster::Engine->config->should_run( 'address01' );
27 5 50       26 push @results, $class->address02( $zone ) if Zonemaster::Engine->config->should_run( 'address02' );
28             # Perform ADDRESS03 if ADDRESS02 passed
29 5 100   10   50 if ( any { $_->tag eq q{NAMESERVERS_IP_WITH_REVERSE} } @results ) {
  10         263  
30 3 50       12 push @results, $class->address03( $zone ) if Zonemaster::Engine->config->should_run( 'address03' );
31             }
32              
33 5         43 return @results;
34             }
35              
36             ###
37             ### Metadata Exposure
38             ###
39              
40             sub metadata {
41 5     5 1 14 my ( $class ) = @_;
42              
43             return {
44 5         40 address01 => [
45             qw(
46             NAMESERVER_IP_PRIVATE_NETWORK
47             NO_IP_PRIVATE_NETWORK
48             )
49             ],
50             address02 => [
51             qw(
52             NAMESERVER_IP_WITHOUT_REVERSE
53             NAMESERVERS_IP_WITH_REVERSE
54             NO_RESPONSE_PTR_QUERY
55             )
56             ],
57             address03 => [
58             qw(
59             NAMESERVER_IP_WITHOUT_REVERSE
60             NAMESERVER_IP_PTR_MISMATCH
61             NAMESERVER_IP_PTR_MATCH
62             NO_RESPONSE_PTR_QUERY
63             )
64             ],
65             };
66             } ## end sub metadata
67              
68             sub translation {
69             return {
70 1     1 1 9 'NAMESERVER_IP_WITHOUT_REVERSE' => 'Nameserver {ns} has an IP address ({address}) without PTR configured.',
71             'NAMESERVER_IP_PTR_MISMATCH' =>
72             'Nameserver {ns} has an IP address ({address}) with mismatched PTR result ({names}).',
73             'NAMESERVER_IP_PRIVATE_NETWORK' =>
74             'Nameserver {ns} has an IP address ({address}) with prefix {prefix} referenced in {reference} as a \'{name}\'.',
75             'NO_IP_PRIVATE_NETWORK' => 'All Nameserver addresses are in the routable public addressing space.',
76             'NAMESERVERS_IP_WITH_REVERSE' => 'Reverse DNS entry exist for all Nameserver IP addresses.',
77             'NAMESERVER_IP_PTR_MATCH' => 'All reverse DNS entry matches name server name.',
78             'NO_RESPONSE_PTR_QUERY' => 'No response from nameserver(s) on PTR query ({reverse}).',
79             };
80             }
81              
82             sub version {
83 9     9 1 134 return "$Zonemaster::Engine::Test::Address::VERSION";
84             }
85              
86             sub find_special_address {
87 63     63 1 131 my ( $class, $ip ) = @_;
88 63         90 my @special_addresses;
89              
90 63 100       131 if ( $ip->version == $IP_VERSION_4 ) {
    50          
91 38         449 @special_addresses = @IPV4_SPECIAL_ADDRESSES;
92             }
93             elsif ( $ip->version == $IP_VERSION_6 ) {
94 25         436 @special_addresses = @IPV6_SPECIAL_ADDRESSES;
95             }
96              
97 63         4450 foreach my $ip_details ( @special_addresses ) {
98 987 100       142941 if ( $ip->overlaps( ${$ip_details}{ip} ) ) {
  987         2836  
99 32         9585 return $ip_details;
100             }
101             }
102              
103 31         2593 return;
104             }
105              
106             sub address01 {
107 6     6 1 19 my ( $class, $zone ) = @_;
108 6         16 my @results;
109             my %ips;
110              
111 6         10 foreach
112 6         39 my $local_ns ( @{ Zonemaster::Engine::TestMethods->method4( $zone ) }, @{ Zonemaster::Engine::TestMethods->method5( $zone ) } )
  6         37  
113             {
114              
115 60 100       4321 next if $ips{ $local_ns->address->short };
116              
117 31         2402 my $ip_details_ref = $class->find_special_address( $local_ns->address );
118              
119 31 100       92 if ( $ip_details_ref ) {
120             push @results,
121             info(
122             NAMESERVER_IP_PRIVATE_NETWORK => {
123             ns => $local_ns->name->string,
124             address => $local_ns->address->short,
125 1         33 prefix => ${$ip_details_ref}{ip}->print,
126 1         49 name => ${$ip_details_ref}{name},
127 1         8 reference => ${$ip_details_ref}{reference},
128             }
129 1         39 );
130             }
131              
132 31         988 $ips{ $local_ns->address->short }++;
133              
134             } ## end foreach my $local_ns ( @{ Zonemaster::Engine::TestMethods...})
135              
136 6 100 66     450 if ( scalar keys %ips and not scalar @results ) {
137 5         36 push @results, info( NO_IP_PRIVATE_NETWORK => {} );
138             }
139              
140 5         26 return @results;
141             } ## end sub address01
142              
143             sub address02 {
144 6     6 1 21 my ( $class, $zone ) = @_;
145 6         20 my @results;
146              
147             my %ips;
148 6         0 my $ptr_query;
149              
150 6         13 foreach
151 6         39 my $local_ns ( @{ Zonemaster::Engine::TestMethods->method4( $zone ) }, @{ Zonemaster::Engine::TestMethods->method5( $zone ) } )
  6         27  
152             {
153              
154 54 100       4260 next if $ips{ $local_ns->address->short };
155              
156 28         2431 my $reverse_ip_query = $local_ns->address->reverse_ip;
157 28         2104 $ptr_query = $reverse_ip_query;
158              
159 28         143 my $p = Zonemaster::Engine::Recursor->recurse( $ptr_query, q{PTR} );
160              
161             # In case of Classless IN-ADDR.ARPA delegation, query returns
162             # CNAME records. A PTR query is done on the CNAME.
163 28 100 66     210 if ( $p and $p->rcode eq q{NOERROR} and $p->get_records( q{CNAME}, q{answer} ) ) {
      100        
164 3         15 my ( $cname ) = $p->get_records( q{CNAME}, q{answer} );
165 3         70 $ptr_query = $cname->cname;
166 3         17 $p = Zonemaster::Engine::Recursor->recurse( $ptr_query, q{PTR} );
167             }
168              
169 28 50       139 if ( $p ) {
170 28 100 66     106 if ( $p->rcode ne q{NOERROR} or not $p->get_records( q{PTR}, q{answer} ) ) {
171 2         76 push @results,
172             info(
173             NAMESERVER_IP_WITHOUT_REVERSE => {
174             ns => $local_ns->name->string,
175             address => $local_ns->address->short,
176             }
177             );
178             }
179             }
180             else {
181 0         0 push @results,
182             info(
183             NO_RESPONSE_PTR_QUERY => {
184             reverse => $ptr_query,
185             }
186             );
187             }
188              
189 28         787 $ips{ $local_ns->address->short }++;
190              
191             } ## end foreach my $local_ns ( @{ Zonemaster::Engine::TestMethods...})
192              
193 6 100 66     461 if ( scalar keys %ips and not scalar @results ) {
194 4         24 push @results, info( NAMESERVERS_IP_WITH_REVERSE => {} );
195             }
196              
197 6         35 return @results;
198             } ## end sub address02
199              
200             sub address03 {
201 5     5 1 22 my ( $class, $zone ) = @_;
202 5         214 my @results;
203             my $ptr_query;
204              
205 5         0 my %ips;
206              
207 5         16 foreach my $local_ns ( @{ Zonemaster::Engine::TestMethods->method5( $zone ) } ) {
  5         31  
208              
209 25 50       108 next if $ips{ $local_ns->address->short };
210              
211 25         2104 my $reverse_ip_query = $local_ns->address->reverse_ip;
212 25         381 $ptr_query = $reverse_ip_query;
213              
214 25         128 my $p = Zonemaster::Engine::Recursor->recurse( $ptr_query, q{PTR} );
215              
216             # In case of Classless IN-ADDR.ARPA delegation, query returns
217             # CNAME records. A PTR query is done on the CNAME.
218 25 100 33     157 if ( $p and $p->rcode eq q{NOERROR} and $p->get_records( q{CNAME}, q{answer} ) ) {
      66        
219 3         13 my ( $cname ) = $p->get_records( q{CNAME}, q{answer} );
220 3         33 $ptr_query = $cname->cname;
221 3         17 $p = Zonemaster::Engine::Recursor->recurse( $ptr_query, q{PTR} );
222             }
223              
224 25 50       74 if ( $p ) {
225 25         90 my @ptr = $p->get_records_for_name( q{PTR}, $ptr_query );
226 25 50 50     105 if ( $p->rcode eq q{NOERROR} and scalar @ptr ) {
227 25 100   26   571 if ( none { name( $_->ptrdname ) eq $local_ns->name->string . q{.} } @ptr ) {
  26         346  
228             push @results,
229             info(
230             NAMESERVER_IP_PTR_MISMATCH => {
231             ns => $local_ns->name->string,
232             address => $local_ns->address->short,
233 7         172 names => join( q{/}, map { $_->ptrdname } @ptr ),
  7         468  
234             }
235             );
236             }
237             }
238             else {
239 0         0 push @results,
240             info(
241             NAMESERVER_IP_WITHOUT_REVERSE => {
242             ns => $local_ns->name->string,
243             address => $local_ns->address->short,
244             }
245             );
246             }
247             } ## end if ( $p )
248             else {
249 0         0 push @results,
250             info(
251             NO_RESPONSE_PTR_QUERY => {
252             reverse => $ptr_query,
253             }
254             );
255             }
256              
257 25         628 $ips{ $local_ns->address->short }++;
258              
259             } ## end foreach my $local_ns ( @{ Zonemaster::Engine::TestMethods...})
260              
261 5 100 66     51 if ( scalar keys %ips and not scalar @results ) {
262 3         18 push @results, info( NAMESERVER_IP_PTR_MATCH => {} );
263             }
264              
265 5         33 return @results;
266             } ## end sub address03
267              
268             1;
269              
270             =head1 NAME
271              
272             Zonemaster::Engine::Test::Address - module implementing tests focused on the Address specific test cases of the DNS tests
273              
274             =head1 SYNOPSIS
275              
276             my @results = Zonemaster::Engine::Test::Address->all($zone);
277              
278             =head1 METHODS
279              
280             =over
281              
282             =item all($zone)
283              
284             Runs the default set of tests and returns a list of log entries made by the tests
285              
286             =item metadata()
287              
288             Returns a reference to a hash, the keys of which are the names of all test methods in the module, and the corresponding values are references to
289             lists with all the tags that the method can use in log entries.
290              
291             =item translation()
292              
293             Returns a refernce to a hash with translation data. Used by the builtin translation system.
294              
295             =item version()
296              
297             Returns a version string for the module.
298              
299             =back
300              
301             =head1 TESTS
302              
303             =over
304              
305             =item address01($zone)
306              
307             Verify that IPv4 addresse are not in private networks.
308              
309             =item address02($zone)
310              
311             Verify reverse DNS entries exist for nameservers IP addresses.
312              
313             =item address03($zone)
314              
315             Verify that reverse DNS entries match nameservers names.
316              
317             =item find_special_address($ip)
318              
319             Verify that an address (Net::IP::XS) given is a special (private, reserved, ...) one.
320              
321             =back
322              
323             =cut
324