File Coverage

blib/lib/Zonemaster/Engine/Test/Connectivity.pm
Criterion Covered Total %
statement 126 136 92.6
branch 34 46 73.9
condition 4 12 33.3
subroutine 18 18 100.0
pod 7 7 100.0
total 189 219 86.3


line stmt bran cond sub pod time code
1             package Zonemaster::Engine::Test::Connectivity;
2              
3 26     26   9897 use version; our $VERSION = version->declare("v1.0.8");
  26         62  
  26         171  
4              
5 26     26   2087 use strict;
  26         54  
  26         613  
6 26     26   120 use warnings;
  26         51  
  26         604  
7              
8 26     26   399 use 5.014002;
  26         87  
9              
10 26     26   133 use Zonemaster::Engine;
  26         45  
  26         493  
11 26     26   111 use Zonemaster::Engine::Util;
  26         54  
  26         1508  
12 26     26   143 use Zonemaster::Engine::TestMethods;
  26         57  
  26         692  
13 26     26   126 use Zonemaster::Engine::Constants qw[:ip];
  26         49  
  26         2568  
14 26     26   6425 use Zonemaster::Engine::ASNLookup;
  26         61  
  26         668  
15 26     26   147 use Carp;
  26         52  
  26         1428  
16              
17 26     26   152 use List::MoreUtils qw[uniq];
  26         51  
  26         191  
18              
19             ###
20             ### Entry Points
21             ###
22              
23             sub all {
24 9     9 1 31 my ( $class, $zone ) = @_;
25 9         17 my @results;
26              
27 9 100       32 if ( Zonemaster::Engine->config->should_run( 'connectivity01' ) ) {
28 7         35 push @results, $class->connectivity01( $zone );
29             }
30 9 100       39 if ( Zonemaster::Engine->config->should_run( 'connectivity02' ) ) {
31 7         41 push @results, $class->connectivity02( $zone );
32             }
33 9 100       38 if ( Zonemaster::Engine->config->should_run( 'connectivity03' ) ) {
34 7         36 push @results, $class->connectivity03( $zone );
35             }
36              
37 9         70 return @results;
38             }
39              
40             ###
41             ### Metadata Exposure
42             ###
43              
44             sub metadata {
45 2     2 1 6 my ( $class ) = @_;
46              
47             return {
48 2         15 connectivity01 => [
49             qw(
50             NAMESERVER_HAS_UDP_53
51             NAMESERVER_NO_UDP_53
52             IPV4_DISABLED
53             IPV6_DISABLED
54             )
55             ],
56             connectivity02 => [
57             qw(
58             NAMESERVER_HAS_TCP_53
59             NAMESERVER_NO_TCP_53
60             IPV4_DISABLED
61             IPV6_DISABLED
62             )
63             ],
64             connectivity03 => [
65             qw(
66             NAMESERVERS_IPV4_NO_AS
67             NAMESERVERS_IPV4_WITH_MULTIPLE_AS
68             NAMESERVERS_IPV4_WITH_UNIQ_AS
69             NAMESERVERS_IPV6_NO_AS
70             NAMESERVERS_IPV6_WITH_MULTIPLE_AS
71             NAMESERVERS_IPV6_WITH_UNIQ_AS
72             NAMESERVERS_NO_AS
73             NAMESERVERS_WITH_MULTIPLE_AS
74             NAMESERVERS_WITH_UNIQ_AS
75             IPV4_ASN
76             IPV6_ASN
77             ASN_INFOS_RAW
78             ASN_INFOS_ANNOUNCE_BY
79             ASN_INFOS_ANNOUNCE_IN
80             IPV4_DISABLED
81             IPV6_DISABLED
82             )
83             ],
84             };
85             } ## end sub metadata
86              
87             sub translation {
88             return {
89 1     1 1 17 'NAMESERVERS_IPV4_WITH_UNIQ_AS' => 'All nameservers IPv4 addresses are in the same AS ({asn}).',
90             'NAMESERVERS_IPV6_WITH_UNIQ_AS' => 'All nameservers IPv6 addresses are in the same AS ({asn}).',
91             'NAMESERVERS_WITH_MULTIPLE_AS' => 'Domain\'s authoritative nameservers do not belong to the same AS.',
92             'NAMESERVERS_WITH_UNIQ_AS' => 'All nameservers are in the same AS ({asn}).',
93             'NAMESERVERS_IPV4_NO_AS' => 'No IPv4 nameserver address is in an AS.',
94             'NAMESERVERS_IPV4_WITH_MULTIPLE_AS' => 'Authoritative IPv4 nameservers are in more than one AS.',
95             'NAMESERVERS_IPV6_NO_AS' => 'No IPv6 nameserver address is in an AS.',
96             'NAMESERVERS_IPV6_WITH_MULTIPLE_AS' => 'Authoritative IPv6 nameservers are in more than one AS.',
97             'NAMESERVERS_NO_AS' => 'No nameserver address is in an AS.',
98             'NAMESERVER_HAS_TCP_53' => 'Nameserver {ns}/{address} accessible over TCP on port 53.',
99             'NAMESERVER_HAS_UDP_53' => 'Nameserver {ns}/{address} accessible over UDP on port 53.',
100             'NAMESERVER_NO_TCP_53' => 'Nameserver {ns}/{address} not accessible over TCP on port 53.',
101             'NAMESERVER_NO_UDP_53' => 'Nameserver {ns}/{address} not accessible over UDP on port 53.',
102             'IPV4_DISABLED' => 'IPv4 is disabled, not sending "{rrtype}" query to {ns}/{address}.',
103             'IPV6_DISABLED' => 'IPv6 is disabled, not sending "{rrtype}" query to {ns}/{address}.',
104             'IPV4_ASN' => 'Name servers have IPv4 addresses in the following ASs: {asn}.',
105             'IPV6_ASN' => 'Name servers have IPv6 addresses in the following ASs: {asn}.',
106             'ASN_INFOS_RAW' => '[ASN:RAW] {address};{data}',
107             'ASN_INFOS_ANNOUNCE_BY' => '[ASN:ANNOUNCE_BY] {address};{asn}',
108             'ASN_INFOS_ANNOUNCE_IN' => '[ASN:ANNOUNCE_IN] {address};{prefix}',
109             };
110             } ## end sub translation
111              
112             sub version {
113 9     9 1 103 return "$Zonemaster::Engine::Test::Connectivity::VERSION";
114             }
115              
116             ###
117             ### Tests
118             ###
119              
120             sub connectivity01 {
121 7     7 1 24 my ( $class, $zone ) = @_;
122 7         13 my @results;
123 7         15 my $query_type = q{SOA};
124              
125 7         16 my %ips;
126              
127 7         15 foreach
128 7         45 my $local_ns ( @{ Zonemaster::Engine::TestMethods->method4( $zone ) }, @{ Zonemaster::Engine::TestMethods->method5( $zone ) } )
  7         50  
129             {
130              
131 68 50 33     3105 if ( not Zonemaster::Engine->config->ipv6_ok and $local_ns->address->version == $IP_VERSION_6 ) {
132 0         0 push @results,
133             info(
134             IPV6_DISABLED => {
135             ns => $local_ns->name->string,
136             address => $local_ns->address->short,
137             rrtype => $query_type,
138             }
139             );
140 0         0 next;
141             }
142              
143 68 50 33     146 if ( not Zonemaster::Engine->config->ipv4_ok and $local_ns->address->version == $IP_VERSION_4 ) {
144 0         0 push @results,
145             info(
146             IPV4_DISABLED => {
147             ns => $local_ns->name->string,
148             address => $local_ns->address->short,
149             rrtype => $query_type,
150             }
151             );
152 0         0 next;
153             }
154              
155 68 100       1805 next if $ips{ $local_ns->address->short };
156              
157 39         3112 my $p = $local_ns->query( $zone->name, $query_type, { usevc => 0 } );
158              
159 39 100       99 if ( $p ) {
160 38         926 push @results,
161             info(
162             NAMESERVER_HAS_UDP_53 => {
163             ns => $local_ns->name->string,
164             address => $local_ns->address->short,
165             }
166             );
167             }
168             else {
169 1         26 push @results,
170             info(
171             NAMESERVER_NO_UDP_53 => {
172             ns => $local_ns->name->string,
173             address => $local_ns->address->short,
174             }
175             );
176             }
177              
178 39         983 $ips{ $local_ns->address->short }++;
179              
180             } ## end foreach my $local_ns ( @{ Zonemaster::Engine::TestMethods...})
181              
182 7         459 return @results;
183             } ## end sub connectivity01
184              
185             sub connectivity02 {
186 7     7 1 27 my ( $class, $zone ) = @_;
187 7         15 my @results;
188             my %ips;
189 7         14 my $query_type = q{SOA};
190              
191 7         14 foreach
192 7         42 my $local_ns ( @{ Zonemaster::Engine::TestMethods->method4( $zone ) }, @{ Zonemaster::Engine::TestMethods->method5( $zone ) } )
  7         37  
193             {
194              
195 68 50 33     3104 if ( not Zonemaster::Engine->config->ipv6_ok and $local_ns->address->version == $IP_VERSION_6 ) {
196 0         0 push @results,
197             info(
198             IPV6_DISABLED => {
199             ns => $local_ns->name->string,
200             address => $local_ns->address->short,
201             rrtype => $query_type,
202             }
203             );
204 0         0 next;
205             }
206              
207 68 50 33     149 if ( not Zonemaster::Engine->config->ipv4_ok and $local_ns->address->version == $IP_VERSION_4 ) {
208 0         0 push @results,
209             info(
210             IPV4_DISABLED => {
211             ns => $local_ns->name->string,
212             address => $local_ns->address->short,
213             rrtype => $query_type,
214             }
215             );
216 0         0 next;
217             }
218              
219 68 100       1869 next if $ips{ $local_ns->address->short };
220              
221 39         3030 my $p = $local_ns->query( $zone->name, $query_type, { usevc => 1 } );
222              
223 39 100       111 if ( $p ) {
224 38         1118 push @results,
225             info(
226             NAMESERVER_HAS_TCP_53 => {
227             ns => $local_ns->name->string,
228             address => $local_ns->address->short,
229             }
230             );
231             }
232             else {
233 1         26 push @results,
234             info(
235             NAMESERVER_NO_TCP_53 => {
236             ns => $local_ns->name->string,
237             address => $local_ns->address->short,
238             }
239             );
240             }
241              
242 39         1014 $ips{ $local_ns->address->short }++;
243              
244             } ## end foreach my $local_ns ( @{ Zonemaster::Engine::TestMethods...})
245              
246 7         502 return @results;
247             } ## end sub connectivity02
248              
249             sub connectivity03 {
250 7     7 1 23 my ( $class, $zone ) = @_;
251 7         14 my @results;
252              
253 7         58 my %ips = ( $IP_VERSION_4 => {}, $IP_VERSION_6 => {} );
254              
255 7         82 foreach my $ns ( @{ Zonemaster::Engine::TestMethods->method4( $zone ) } ) {
  7         37  
256 34         94 my $addr = $ns->address;
257 34         85 $ips{ $addr->version }{ $addr->ip } = $addr;
258             }
259              
260 7         17 my @v4ips = values %{ $ips{$IP_VERSION_4} };
  7         29  
261 7         55 my @v6ips = values %{ $ips{$IP_VERSION_6} };
  7         18  
262              
263 7         48 my @v4asns;
264             my @v6asns;
265              
266 7         26 foreach my $v4ip ( @v4ips ) {
267 21         105 my ( $asnref, $prefix, $raw ) = Zonemaster::Engine::ASNLookup->get_with_prefix( $v4ip );
268 21 50       96 if ( $raw ) {
269 21         77 push @results,
270             info(
271             ASN_INFOS_RAW => {
272             address => $v4ip->short,
273             data => $raw,
274             }
275             );
276             }
277 21 50       63 if ( $asnref ) {
278             push @results,
279             info(
280             ASN_INFOS_ANNOUNCE_BY => {
281             address => $v4ip->short,
282 21         72 asn => join( q{,}, @{$asnref} ),
  21         635  
283             }
284             );
285 21         43 push @v4asns, @{$asnref};
  21         49  
286             }
287 21 50       66 if ( $prefix ) {
288 21         126 push @results,
289             info(
290             ASN_INFOS_ANNOUNCE_IN => {
291             address => $v4ip->short,
292             prefix => sprintf "%s/%d",
293             $prefix->ip, $prefix->prefixlen,
294             }
295             );
296             }
297             } ## end foreach my $v4ip ( @v4ips )
298 7         26 foreach my $v6ip ( @v6ips ) {
299 13         56 my ( $asnref, $prefix, $raw ) = Zonemaster::Engine::ASNLookup->get_with_prefix( $v6ip );
300 13 50       57 if ( $raw ) {
301 13         53 push @results,
302             info(
303             ASN_INFOS_RAW => {
304             address => $v6ip->short,
305             data => $raw,
306             }
307             );
308             }
309 13 50       42 if ( $asnref ) {
310             push @results,
311             info(
312             ASN_INFOS_ANNOUNCE_BY => {
313             address => $v6ip->short,
314 13         53 asn => join( q{,}, @{$asnref} ),
  13         1170  
315             }
316             );
317 13         30 push @v6asns, @{$asnref};
  13         32  
318             }
319 13 50       46 if ( $prefix ) {
320 13         88 push @results,
321             info(
322             ASN_INFOS_ANNOUNCE_IN => {
323             address => $v6ip->short,
324             prefix => sprintf "%s/%d",
325             $prefix->short, $prefix->prefixlen,
326             }
327             );
328             }
329             } ## end foreach my $v6ip ( @v6ips )
330              
331 7         66 @v4asns = uniq @v4asns;
332 7         35 @v6asns = uniq @v6asns;
333 7         35 my @all_asns = uniq( @v4asns, @v6asns );
334              
335 7         28 push @results, info( IPV4_ASN => { asn => \@v4asns } );
336 7         33 push @results, info( IPV6_ASN => { asn => \@v6asns } );
337              
338 7 100       38 if ( @v4asns == 1 ) {
    50          
339 3         14 push @results, info( NAMESERVERS_IPV4_WITH_UNIQ_AS => { asn => $v4asns[0] } );
340             }
341             elsif ( @v4asns > 1 ) {
342 4         16 push @results, info( NAMESERVERS_IPV4_WITH_MULTIPLE_AS => { asn => \@v4asns } );
343             }
344             else {
345 0         0 push @results, info( NAMESERVERS_IPV4_NO_AS => {} );
346             }
347              
348 7 100       36 if ( @v6asns == 1 ) {
    100          
349 2         10 push @results, info( NAMESERVERS_IPV6_WITH_UNIQ_AS => { asn => $v6asns[0] } );
350             }
351             elsif ( @v6asns > 1 ) {
352 3         14 push @results, info( NAMESERVERS_IPV6_WITH_MULTIPLE_AS => { asn => \@v6asns } );
353             }
354             else {
355 2         8 push @results, info( NAMESERVERS_IPV6_NO_AS => {} );
356             }
357              
358 7 100       45 if ( @all_asns == 1 ) {
    50          
359 3         14 push @results, info( NAMESERVERS_WITH_UNIQ_AS => { asn => $all_asns[0] } );
360             }
361             elsif ( @all_asns > 1 ) {
362 4         15 push @results, info( NAMESERVERS_WITH_MULTIPLE_AS => { asn => \@all_asns } );
363             }
364             else {
365 0         0 push @results, info( NAMESERVERS_NO_AS => {} ); # Shouldn't pass Basic
366             }
367              
368 7         82 return @results;
369             } ## end sub connectivity03
370              
371             1;
372              
373             =head1 NAME
374              
375             Zonemaster::Engine::Test::Connectivity - module implementing tests of nameservers reachability
376              
377             =head1 SYNOPSIS
378              
379             my @results = Zonemaster::Engine::Test::Connectivity->all($zone);
380              
381             =head1 METHODS
382              
383             =over
384              
385             =item all($zone)
386              
387             Runs the default set of tests and returns a list of log entries made by the tests
388              
389             =item metadata()
390              
391             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
392             lists with all the tags that the method can use in log entries.
393              
394             =item translation()
395              
396             Returns a refernce to a hash with translation data. Used by the builtin translation system.
397              
398             =item version()
399              
400             Returns a version string for the module.
401              
402             =back
403              
404             =head1 TESTS
405              
406             =over
407              
408             =item connectivity01($zone)
409              
410             Verify nameservers UDP port 53 reachability.
411              
412             =item connectivity02($zone)
413              
414             Verify nameservers TCP port 53 reachability.
415              
416             =item connectivity03($zone)
417              
418             Verify that all nameservers do not belong to the same AS.
419              
420             =back
421              
422             =cut