File Coverage

blib/lib/Zonemaster/Engine/Test/Basic.pm
Criterion Covered Total %
statement 109 125 87.2
branch 33 46 71.7
condition 16 33 48.4
subroutine 23 23 100.0
pod 9 9 100.0
total 190 236 80.5


line stmt bran cond sub pod time code
1             package Zonemaster::Engine::Test::Basic;
2              
3 26     26   155 use version; our $VERSION = version->declare("v1.0.6");
  26         58  
  26         139  
4              
5 26     26   1988 use strict;
  26         61  
  26         452  
6 26     26   115 use warnings;
  26         51  
  26         547  
7              
8 26     26   346 use 5.014002;
  26         129  
9              
10 26     26   129 use Zonemaster::Engine;
  26         71  
  26         509  
11 26     26   142 use Zonemaster::Engine::Util;
  26         72  
  26         1486  
12 26     26   6514 use Zonemaster::Engine::TestMethods;
  26         89  
  26         905  
13 26     26   7140 use Zonemaster::Engine::Test::Address;
  26         77  
  26         791  
14 26     26   8494 use Zonemaster::Engine::Test::Syntax;
  26         85  
  26         993  
15 26     26   199 use Zonemaster::Engine::Constants qw[:ip :name];
  26         65  
  26         3588  
16 26     26   193 use List::MoreUtils qw[any none];
  26         64  
  26         201  
17              
18 26     26   16976 use Carp;
  26         65  
  26         31534  
19              
20             ###
21             ### Entry Points
22             ###
23              
24             sub all {
25 10     10 1 28 my ( $class, $zone ) = @_;
26 10         20 my @results;
27              
28 10         35 push @results, $class->basic00( $zone );
29              
30 10 100       113 if (
31             none {
32 3 100 100 3   82 $_->tag eq q{DOMAIN_NAME_LABEL_TOO_LONG}
33             or $_->tag eq q{DOMAIN_NAME_ZERO_LENGTH_LABEL}
34             or $_->tag eq q{DOMAIN_NAME_TOO_LONG}
35             }
36             @results
37             )
38             {
39 7         36 push @results, $class->basic01( $zone );
40              
41 7         1890 push @results, $class->basic02( $zone );
42              
43             # Perform BASIC3 if BASIC2 failed
44 7 100   20   72 if ( none { $_->tag eq q{HAS_NAMESERVERS} } @results ) {
  20         500  
45 4 50       13 push @results, $class->basic03( $zone ) if Zonemaster::Engine->config->should_run( 'basic03' );
46             }
47             else {
48 3         73 push @results,
49             info(
50             HAS_NAMESERVER_NO_WWW_A_TEST => {
51             zname => $zone->name,
52             }
53             );
54             }
55             } ## end if ( none { $_->tag eq...})
56              
57 10         84 return @results;
58             } ## end sub all
59              
60             sub can_continue {
61 2     2 1 9 my ( $class, @results ) = @_;
62 2         6 my %tag = map { $_->tag => 1 } @results;
  32         837  
63              
64 2 50 33     15 if ( not $tag{NO_GLUE_PREVENTS_NAMESERVER_TESTS} and $tag{HAS_NAMESERVERS} ) {
65 2         19 return 1;
66             }
67             else {
68 0         0 return;
69             }
70             }
71              
72             ###
73             ### Metadata Exposure
74             ###
75              
76             sub metadata {
77 9     9 1 23 my ( $class ) = @_;
78              
79             return {
80 9         123 basic00 => [
81             qw(
82             DOMAIN_NAME_LABEL_TOO_LONG
83             DOMAIN_NAME_ZERO_LENGTH_LABEL
84             DOMAIN_NAME_TOO_LONG
85             )
86             ],
87             basic01 => [
88             qw(
89             NO_PARENT
90             HAS_PARENT
91             )
92             ],
93             basic02 => [
94             qw(
95             NO_GLUE_PREVENTS_NAMESERVER_TESTS
96             NS_FAILED
97             NS_NO_RESPONSE
98             HAS_NAMESERVERS
99             IPV4_DISABLED
100             IPV6_DISABLED
101             IPV4_ENABLED
102             IPV6_ENABLED
103             )
104             ],
105             basic03 => [
106             qw(
107             NO_NAMESERVER_PREVENTS_WWW_A_TEST
108             HAS_A_RECORDS
109             IPV4_DISABLED
110             IPV6_DISABLED
111             IPV4_ENABLED
112             IPV6_ENABLED
113             A_QUERY_NO_RESPONSES
114             )
115             ],
116             };
117             } ## end sub metadata
118              
119             sub translation {
120             return {
121 1     1 1 20 "DOMAIN_NAME_LABEL_TOO_LONG" => "Domain name ({dname}) has a label ({dlabel}) too long ({dlength}/{max}).",
122             "DOMAIN_NAME_ZERO_LENGTH_LABEL" => "Domain name ({dname}) has a zero length label.",
123             "DOMAIN_NAME_TOO_LONG" => "Domain name is too long ({fqdnlength}/{max}).",
124             'NO_PARENT' => 'No parent domain could be found for the tested domain.',
125             'HAS_PARENT' => 'Parent domain \'{pname}\' was found for the tested domain.',
126             'HAS_A_RECORDS' => 'Nameserver {ns} returned A record(s) for {dname}.',
127             'NO_A_RECORDS' => 'Nameserver {ns} did not return A record(s) for {dname}.',
128             'HAS_NAMESERVERS' => 'Nameserver {ns} listed these servers as glue: {nsnlist}.',
129             'NO_GLUE_PREVENTS_NAMESERVER_TESTS' => 'No NS records for tested zone from parent. NS tests aborted.',
130             'NS_FAILED' => 'Nameserver {ns}/{address} did not return NS records. RCODE was {rcode}.',
131             'NS_NO_RESPONSE' => 'Nameserver {ns}/{address} did not respond to NS query.',
132             'A_QUERY_NO_RESPONSES' => 'Nameservers did not respond to A query.',
133             'HAS_NAMESERVER_NO_WWW_A_TEST' => 'Functional nameserver found. "A" query for www.{zname} test aborted.',
134             'IPV4_DISABLED' => 'IPv4 is disabled, not sending "{rrtype}" query to {ns}/{address}.',
135             'IPV4_ENABLED' => 'IPv4 is enabled, can send "{rrtype}" query to {ns}/{address}.',
136             'IPV6_DISABLED' => 'IPv6 is disabled, not sending "{rrtype}" query to {ns}/{address}.',
137             'IPV6_ENABLED' => 'IPv6 is enabled, can send "{rrtype}" query to {ns}/{address}.',
138             };
139             } ## end sub translation
140              
141             sub version {
142 17     17 1 168 return "$Zonemaster::Engine::Test::Basic::VERSION";
143             }
144              
145             ###
146             ### Tests
147             ###
148              
149             sub basic00 {
150 16     16 1 43 my ( $class, $zone ) = @_;
151 16         64 my $name = name( $zone );
152 16         33 my @results;
153              
154 16         35 foreach my $local_label ( @{ $name->labels } ) {
  16         461  
155 48 100       366 if ( length $local_label > $LABEL_MAX_LENGTH ) {
    100          
156 2         18 push @results,
157             info(
158             q{DOMAIN_NAME_LABEL_TOO_LONG} => {
159             dname => "$name",
160             dlabel => $local_label,
161             dlength => length( $local_label ),
162             max => $LABEL_MAX_LENGTH,
163             }
164             );
165             }
166             elsif ( length $local_label == 0 ) {
167 2         13 push @results,
168             info(
169             q{DOMAIN_NAME_ZERO_LENGTH_LABEL} => {
170             dname => "$name",
171             }
172             );
173             }
174             } ## end foreach my $local_label ( @...)
175              
176 16         128 my $fqdn = $name->fqdn;
177 16 100       61 if ( length( $fqdn ) > $FQDN_MAX_LENGTH ) {
178 2         18 push @results,
179             info(
180             q{DOMAIN_NAME_TOO_LONG} => {
181             fqdn => $fqdn,
182             fqdnlength => length( $fqdn ),
183             max => $FQDN_MAX_LENGTH,
184             }
185             );
186             }
187              
188 16         544 return @results;
189              
190             } ## end sub basic00
191              
192             sub basic01 {
193 7     7 1 23 my ( $class, $zone ) = @_;
194 7         20 my @results;
195 7         214 my $parent = $zone->parent;
196              
197 7 50       24 if ( not $parent ) {
198 0         0 push @results,
199             info(
200             NO_PARENT => {
201             zone => $zone->name->string,
202             }
203             );
204             }
205             else {
206 7         205 push @results,
207             info(
208             HAS_PARENT => {
209             zone => $zone->name->string,
210             pname => $parent->name->string,
211             }
212             );
213             }
214              
215 7         30 return @results;
216             } ## end sub basic01
217              
218             sub basic02 {
219 7     7 1 27 my ( $class, $zone ) = @_;
220 7         18 my @results;
221 7         12 my $query_type = q{NS};
222 7         17 my @ns = @{ Zonemaster::Engine::TestMethods->method4( $zone ) };
  7         56  
223              
224 7 100       245 if ( not scalar @ns ) {
225 3         17 push @results,
226             info(
227             NO_GLUE_PREVENTS_NAMESERVER_TESTS => {}
228             );
229             }
230              
231 7         29 foreach my $ns ( @ns ) {
232 21 50 33     86 if ( not Zonemaster::Engine->config->ipv4_ok and $ns->address->version == $IP_VERSION_4 ) {
    100 66        
233 0         0 push @results,
234             info(
235             IPV4_DISABLED => {
236             ns => $ns->name->string,
237             address => $ns->address->short,
238             rrtype => $query_type,
239             }
240             );
241 0         0 next;
242             }
243             elsif ( Zonemaster::Engine->config->ipv4_ok and $ns->address->version == $IP_VERSION_4 ) {
244 12         480 push @results,
245             info(
246             IPV4_ENABLED => {
247             ns => $ns->name->string,
248             address => $ns->address->short,
249             rrtype => $query_type,
250             }
251             );
252             }
253              
254 21 50 33     182 if ( not Zonemaster::Engine->config->ipv6_ok and $ns->address->version == $IP_VERSION_6 ) {
    100 66        
255 0         0 push @results,
256             info(
257             IPV6_DISABLED => {
258             ns => $ns->name->string,
259             address => $ns->address->short,
260             rrtype => $query_type,
261             }
262             );
263 0         0 next;
264             }
265             elsif ( Zonemaster::Engine->config->ipv6_ok and $ns->address->version == $IP_VERSION_6 ) {
266 9         298 push @results,
267             info(
268             IPV6_ENABLED => {
269             ns => $ns->name->string,
270             address => $ns->address->short,
271             rrtype => $query_type,
272             }
273             );
274             }
275              
276 21         631 my $p = $ns->query( $zone->name, $query_type );
277              
278 21 100       66 if ( $p ) {
279 19 50       473 if ( $p->has_rrs_of_type_for_name( $query_type, $zone->name ) ) {
280             push @results,
281             info(
282             HAS_NAMESERVERS => {
283             nsnlist =>
284 19         471 join( q{,}, sort map { $_->nsdname } $p->get_records_for_name( $query_type, $zone->name ) ),
  64         877  
285             ns => $ns->name->string,
286             address => $ns->address->short,
287             }
288             );
289             }
290             else {
291 0         0 push @results,
292             info(
293             NS_FAILED => {
294             ns => $ns->name->string,
295             address => $ns->address->short,
296             rcode => $p->rcode,
297             }
298             );
299             }
300             } ## end if ( $p )
301             else {
302 2         51 push @results,
303             info(
304             NS_NO_RESPONSE => {
305             ns => $ns->name->string,
306             address => $ns->address->short,
307             }
308             );
309             }
310             } ## end foreach my $ns ( @{ Zonemaster::Engine::TestMethods...})
311              
312 7         59 return @results;
313             } ## end sub basic02
314              
315             sub basic03 {
316 4     4 1 14 my ( $class, $zone ) = @_;
317 4         7 my @results;
318 4         10 my $query_type = q{A};
319              
320 4         100 my $name = q{www.} . $zone->name;
321 4         8 my $response_nb = 0;
322 4         10 foreach my $ns ( @{ Zonemaster::Engine::TestMethods->method4( $zone ) } ) {
  4         19  
323 2 50 33     7 if ( not Zonemaster::Engine->config->ipv4_ok and $ns->address->version == $IP_VERSION_4 ) {
    50 33        
324 0         0 push @results,
325             info(
326             IPV4_DISABLED => {
327             ns => $ns->name->string,
328             address => $ns->address->short,
329             rrtype => $query_type,
330             }
331             );
332 0         0 next;
333             }
334             elsif ( Zonemaster::Engine->config->ipv4_ok and $ns->address->version == $IP_VERSION_4 ) {
335 2         83 push @results,
336             info(
337             IPV4_ENABLED => {
338             ns => $ns->name->string,
339             address => $ns->address->short,
340             rrtype => $query_type,
341             }
342             );
343             }
344              
345 2 50 33     8 if ( not Zonemaster::Engine->config->ipv6_ok and $ns->address->version == $IP_VERSION_6 ) {
    50 33        
346 0         0 push @results,
347             info(
348             IPV6_DISABLED => {
349             ns => $ns->name->string,
350             address => $ns->address->short,
351             rrtype => $query_type,
352             }
353             );
354 0         0 next;
355             }
356             elsif ( Zonemaster::Engine->config->ipv6_ok and $ns->address->version == $IP_VERSION_6 ) {
357 0         0 push @results,
358             info(
359             IPV6_ENABLED => {
360             ns => $ns->name->string,
361             address => $ns->address->short,
362             rrtype => $query_type,
363             }
364             );
365             }
366              
367 2         26 my $p = $ns->query( $name, $query_type );
368 2 50       12 next if not $p;
369 0         0 $response_nb++;
370 0 0       0 if ( $p->has_rrs_of_type_for_name( $query_type, $name ) ) {
371 0         0 push @results,
372             info(
373             HAS_A_RECORDS => {
374             ns => $ns->name->string,
375             address => $ns->address->short,
376             dname => $name,
377             }
378             );
379             }
380             else {
381 0         0 push @results,
382             info(
383             NO_A_RECORDS => {
384             ns => $ns->name->string,
385             address => $ns->address->short,
386             dname => $name,
387             }
388             );
389             }
390             } ## end foreach my $ns ( @{ Zonemaster::Engine::TestMethods...})
391              
392 4 100 66     9 if ( scalar( @{ Zonemaster::Engine::TestMethods->method4( $zone ) } ) and not $response_nb ) {
  4         12  
393 1         6 push @results, info( A_QUERY_NO_RESPONSES => {} );
394             }
395              
396 4         12 return @results;
397             } ## end sub basic03
398              
399             1;
400              
401             =head1 NAME
402              
403             Zonemaster::Engine::Test::Basic - module implementing test for very basic domain functionality
404              
405             =head1 SYNOPSIS
406              
407             my @results = Zonemaster::Engine::Test::Basic->all($zone);
408              
409             =head1 METHODS
410              
411             =over
412              
413             =item all($zone)
414              
415             Runs between one and three tests, depending on the zone. If L<basic01> passes, L<basic02> is run. If L<basic02> fails, L<basic03> is run.
416              
417             =item metadata()
418              
419             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
420             lists with all the tags that the method can use in log entries.
421              
422             =item translation()
423              
424             Returns a refernce to a hash with translation data. Used by the builtin translation system.
425              
426             =item version()
427              
428             Returns a version string for the module.
429              
430             =item can_continue(@results)
431              
432             Looks at the provided log entries and returns true if they indicate that further testing of the relevant zone is possible.
433              
434             =back
435              
436             =head1 TESTS
437              
438             =over
439              
440             =item basic00
441              
442             Checks if the domain name to be tested is valid. Not all syntax tests are done here, it "just" checks domain name total length and labels length.
443             In case of failure, all other tests are aborted.
444              
445             =item basic01
446              
447             Checks that we can find a parent zone for the zone we're testing. If we can't, no further testing is done.
448              
449             =item basic02
450              
451             Checks that the nameservers for the parent zone returns NS records for the tested zone, and that at least one of the nameservers thus pointed out
452             responds sensibly to an NS query for the tested zone.
453              
454             =item basic03
455              
456             Checks if at least one of the nameservers pointed out by the parent zone gives a useful response when sent an A query for the C<www> label in the
457             tested zone (that is, if we're testing C<example.org> this test will as for A records for C<www.example.org>). This test is only run if the
458             L<basic02> test has I<failed>.
459              
460             =back
461              
462             =cut