File Coverage

blib/lib/Zonemaster/Engine.pm
Criterion Covered Total %
statement 110 115 95.6
branch 10 12 83.3
condition 6 10 60.0
subroutine 29 30 96.6
pod 19 19 100.0
total 174 186 93.5


line stmt bran cond sub pod time code
1             package Zonemaster::Engine;
2              
3 26     26   922204 use version; our $VERSION = version->declare("v2.0.0");
  26         24660  
  26         160  
4              
5 26     26   2652 use 5.014002;
  26         144  
6 26     26   6406 use Moose;
  26         6695873  
  26         168  
7              
8 26     26   173356 use Zonemaster::Engine::Nameserver;
  26         121  
  26         1101  
9 26     26   10857 use Zonemaster::Engine::Logger;
  26         88  
  26         1133  
10 26     26   10697 use Zonemaster::Engine::Config;
  26         101  
  26         1011  
11 26     26   10505 use Zonemaster::Engine::Zone;
  26         102  
  26         1066  
12 26     26   11151 use Zonemaster::Engine::Test;
  26         99  
  26         897  
13 26     26   161 use Zonemaster::Engine::Recursor;
  26         53  
  26         487  
14 26     26   126 use Zonemaster::Engine::ASNLookup;
  26         55  
  26         22680  
15              
16             our $logger;
17             our $config;
18             our $recursor = Zonemaster::Engine::Recursor->new;
19              
20             sub logger {
21 89381   66 89381 1 1178779 return $logger //= Zonemaster::Engine::Logger->new;
22             }
23              
24             sub config {
25 137419 100   137419 1 511803 if ( not defined $config ) {
26 23         821 $config = Zonemaster::Engine::Config->new;
27             }
28              
29 137419         510596 return $config;
30             }
31              
32             sub ns {
33 326740     326740 1 810844 my ( $class, $name, $address ) = @_;
34              
35 326740         1556477 return Zonemaster::Engine::Nameserver->new( { name => $name, address => $address } );
36             }
37              
38             sub zone {
39 163     163 1 31069 my ( $class, $name ) = @_;
40              
41 163         5861 return Zonemaster::Engine::Zone->new( { name => $name } );
42             }
43              
44             sub test_zone {
45 3     3 1 112 my ( $class, $zname ) = @_;
46              
47 3         11 return Zonemaster::Engine::Test->run_all_for( $class->zone( $zname ) );
48             }
49              
50             sub test_module {
51 61     61 1 3832 my ( $class, $module, $zname ) = @_;
52              
53 61         309 return Zonemaster::Engine::Test->run_module( $module, $class->zone( $zname ) );
54             }
55              
56             sub test_method {
57 184     184 1 24534 my ( $class, $module, $method, @arguments ) = @_;
58              
59 184         986 return Zonemaster::Engine::Test->run_one( $module, $method, @arguments );
60             }
61              
62             sub all_tags {
63 1     1 1 247 my ( $class ) = @_;
64 1         2 my @res;
65              
66 1         17 foreach my $module ( 'Basic', sort { $a cmp $b } Zonemaster::Engine::Test->modules ) {
  18         22  
67 10         15 my $full = "Zonemaster::Engine::Test::$module";
68 10         31 my $ref = $full->metadata;
69 10         13 foreach my $list ( values %{$ref} ) {
  10         22  
70 60         71 push @res, map { uc( $module ) . q{:} . $_ } sort { $a cmp $b } @{$list};
  260         528  
  383         435  
  60         91  
71             }
72             }
73              
74 1         31 return @res;
75             }
76              
77             sub all_methods {
78 1     1 1 992 my ( $class ) = @_;
79 1         7 my %res;
80              
81 1         4 foreach my $module ( 'Basic', Zonemaster::Engine::Test->modules ) {
82 10         18 my $full = "Zonemaster::Engine::Test::$module";
83 10         71 my $ref = $full->metadata;
84 10         14 foreach my $method ( sort { $a cmp $b } keys %{$ref} ) {
  111         124  
  10         30  
85 60         64 push @{ $res{$module} }, $method;
  60         110  
86             }
87             }
88              
89 1         7 return %res;
90             }
91              
92             sub recurse {
93 2     2 1 12 my ( $class, $qname, $qtype, $qclass ) = @_;
94 2   50     7 $qtype //= 'A';
95 2   50     11 $qclass //= 'IN';
96              
97 2         13 return $recursor->recurse( $qname, $qtype, $qclass );
98             }
99              
100             sub add_fake_delegation {
101 4     4 1 1395 my ( $class, $domain, $href ) = @_;
102              
103             # Check fake delegation
104 4         10 foreach my $name ( keys %{$href} ) {
  4         24  
105 14 100 66     49 if ( not defined $href->{$name} or not scalar @{ $href->{$name} } ) {
  14         52  
106 2 100       78 if ( Zonemaster::Engine::Zone->new( { name => $domain } )->is_in_zone( $name ) ) {
107 1         7 Zonemaster::Engine->logger->add(
108             FAKE_DELEGATION_IN_ZONE_NO_IP => { domain => $domain , ns => $name }
109             );
110             }
111             else {
112 1         19731 my @ips = Zonemaster::LDNS->new->name2addr($name);
113 1 50       14 if ( @ips ) {
114 0         0 push @{ $href->{$name} }, @ips;
  0         0  
115             }
116             else {
117 1         6 Zonemaster::Engine->logger->add(
118             FAKE_DELEGATION_NO_IP => { domain => $domain , ns => $name }
119             );
120             }
121             }
122             }
123             }
124 4         30 my $parent = $class->zone( $recursor->parent( $domain ) );
125 4         12 foreach my $ns ( @{ $parent->ns } ) {
  4         107  
126 48         146 $ns->add_fake_delegation( $domain => $href );
127             }
128              
129 4         118 return;
130             }
131              
132             sub add_fake_ds {
133 1     1 1 4 my ( $class, $domain, $aref ) = @_;
134              
135 1         5 my $parent = $class->zone( scalar( $recursor->parent( $domain ) ) );
136 1 50       5 if ( not $parent ) {
137 0         0 die "Failed to find parent for $domain";
138             }
139              
140 1         2 foreach my $ns ( @{ $parent->ns } ) {
  1         28  
141 6         19 $ns->add_fake_ds( $domain => $aref );
142             }
143              
144 1         28 return;
145             }
146              
147             sub can_continue {
148 211     211 1 534 my ( $class ) = @_;
149              
150 211         695 return 1;
151            
152             }
153              
154             sub save_cache {
155 1     1 1 364 my ( $class, $filename ) = @_;
156              
157 1         9 return Zonemaster::Engine::Nameserver->save( $filename );
158             }
159              
160             sub preload_cache {
161 1     1 1 6 my ( $class, $filename ) = @_;
162              
163 1         8 return Zonemaster::Engine::Nameserver->restore( $filename );
164             }
165              
166             sub asn_lookup {
167 0     0 1 0 my ( undef, $ip ) = @_;
168              
169 0         0 return Zonemaster::Engine::ASNLookup->get( $ip );
170             }
171              
172             sub modules {
173 1     1 1 8 return Zonemaster::Engine::Test->modules;
174             }
175              
176             sub start_time_now {
177 248     248 1 1273 Zonemaster::Engine::Logger->start_time_now();
178 248         456 return;
179             }
180              
181             sub reset {
182 2     2 1 166 Zonemaster::Engine::Logger->start_time_now();
183 2         13 Zonemaster::Engine::Nameserver->empty_cache();
184 2 100       11 $logger->clear_history() if $logger;
185 2         16 Zonemaster::Engine::Recursor->clear_cache();
186              
187 2         6 return;
188             }
189              
190             =head1 NAME
191              
192             Zonemaster - A tool to check the quality of a DNS zone
193              
194             =head1 SYNOPSIS
195              
196             my @results = Zonemaster::Engine->test_zone('iis.se')
197              
198             =head1 INTRODUCTION
199              
200             This manual describes the main L<Zonemaster> module. If what you're after is documentation on the Zonemaster test engine as a whole, see L<Zonemaster::Engine::Overview>.
201              
202             =head1 METHODS
203              
204             =over
205              
206             =item test_zone($name)
207              
208             Runs all available tests and returns a list of L<Zonemaster::Engine::Logger::Entry> objects.
209              
210             =item test_module($module, $name)
211              
212             Runs all available tests for the zone with the given name in the specified module.
213              
214             =item test_method($module, $method, @arguments)
215              
216             Run one particular test method in one particular module. The requested module must be in the list of active loaded modules (that is, not the Basic
217             module and not a module disabled by the current policy), and the method must be listed in the metadata the module exports. If those requirements
218             are fulfilled, the method will be called with the provided arguments.
219              
220             =item zone($name)
221              
222             Returns a L<Zonemaster::Engine::Zone> object for the given name.
223              
224             =item ns($name, $address)
225              
226             Returns a L<Zonemaster::Engine::Nameserver> object for the given name and address.
227              
228             =item config()
229              
230             Returns the global L<Zonemaster::Engine::Config> object.
231              
232             =item logger()
233              
234             Returns the global L<Zonemaster::Engine::Logger> object.
235              
236             =item all_tags()
237              
238             Returns a list of all the tags that can be logged for all avilable test modules.
239              
240             =item all_methods()
241              
242             Returns a hash, where the keys are test module names and the values are lists with the names of the test methods in that module.
243              
244             =item recurse($name, $type, $class)
245              
246             Does a recursive lookup for the given name, type and class, and returns the resulting packet (if any). Simply calls
247             L<Zonemaster::Engine::Recursor/recurse> on a globally stored object.
248              
249             =item can_continue()
250              
251             In case of critical condition that prevents tool to process tests, add test here and return False.
252              
253             =item save_cache($filename)
254              
255             After running the tests, save the accumulated cache to a file with the given name.
256              
257             =item preload_cache($filename)
258              
259             Before running the tests, load the cache with information from a file with the given name. This file must have the same format as is produced by
260             L</save_cache()>.
261              
262             =item asn_lookup($ip)
263              
264             Takes a single IP address and returns one of three things:
265              
266             =over
267              
268             =item
269              
270             Nothing, if the IP address is not in any AS.
271              
272             =item
273              
274             If called in list context, a list of AS number and a L<Net::IP::XS> object representing the prefix it's in.
275              
276             =item
277              
278             If called in scalar context, only the AS number.
279              
280             =back
281              
282             =item modules()
283              
284             Returns a list of the loaded test modules. Exactly the same as L<Zonemaster::Engine::Test/modules>.
285              
286             =item add_fake_delegation($domain, $data)
287              
288             This method adds some fake delegation information to the system. The arguments are a domain name, and a reference to a hash with delegation
289             information. The keys in the hash must be nameserver names, and the values references to lists of IP addresses (which can be left empty) for
290             the corresponding nameserver. If IP addresses are not provided for nameservers, the engine will perform queries to find them, except for
291             in-bailiwick nameservers.
292              
293             Example:
294              
295             Zonemaster::Engine->add_fake_delegation(
296             'lysator.liu.se' => {
297             'ns1.nic.fr' => [ ],
298             'ns.nic.se' => [ '212.247.7.228', '2a00:801:f0:53::53' ],
299             'i.ns.se' => [ '194.146.106.22', '2001:67c:1010:5::53' ],
300             'ns3.nic.se' => [ '212.247.8.152', '2a00:801:f0:211::152' ]
301             }
302             );
303              
304             =item add_fake_ds($domain, $data)
305              
306             This method adds fake DS records to the system. The arguments are a domain
307             name, and a reference to a list of references to hashes. The hashes in turn
308             must have the keys C<keytag>, C<algorithm>, C<type> and C<digest>, with the
309             values holding the corresponding data. The digest data should be a single
310             unbroken string of hexadecimal digits.
311              
312             Example:
313              
314             Zonemaster::Engine->add_fake_ds(
315             'nic.se' => [
316             { keytag => 16696, algorithm => 5, type => 2, digest => '40079DDF8D09E7F10BB248A69B6630478A28EF969DDE399F95BC3B39F8CBACD7' },
317             { keytag => 16696, algorithm => 5, type => 1, digest => 'EF5D421412A5EAF1230071AFFD4F585E3B2B1A60' },
318             ]
319             );
320              
321             =item start_time_now()
322              
323             Set the logger's start time to the current time.
324              
325             =item reset()
326              
327             Reset logger start time to current time, empty the list of log messages, clear
328             nameserver object cache and recursor cache.
329              
330             =back
331              
332             =head1 AUTHORS
333              
334             Vincent Levigneron <vincent.levigneron at nic.fr>
335             - Current maintainer
336              
337             Calle Dybedahl <calle at init.se>
338             - Original author
339              
340             =cut
341              
342 26     26   185 no Moose;
  26         51  
  26         234  
343             __PACKAGE__->meta->make_immutable;
344              
345             1;