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   885480 use version; our $VERSION = version->declare("v2.0.1");
  26         22525  
  26         150  
4              
5 26     26   2450 use 5.014002;
  26         125  
6 26     26   6003 use Moose;
  26         6783211  
  26         172  
7              
8 26     26   185296 use Zonemaster::Engine::Nameserver;
  26         85  
  26         1119  
9 26     26   10245 use Zonemaster::Engine::Logger;
  26         101  
  26         1095  
10 26     26   10807 use Zonemaster::Engine::Config;
  26         99  
  26         1091  
11 26     26   10523 use Zonemaster::Engine::Zone;
  26         110  
  26         1181  
12 26     26   11580 use Zonemaster::Engine::Test;
  26         105  
  26         979  
13 26     26   180 use Zonemaster::Engine::Recursor;
  26         52  
  26         567  
14 26     26   129 use Zonemaster::Engine::ASNLookup;
  26         54  
  26         24139  
15              
16             our $logger;
17             our $config;
18             our $recursor = Zonemaster::Engine::Recursor->new;
19              
20             sub logger {
21 89381   66 89381 1 1210796 return $logger //= Zonemaster::Engine::Logger->new;
22             }
23              
24             sub config {
25 137419 100   137419 1 518665 if ( not defined $config ) {
26 23         891 $config = Zonemaster::Engine::Config->new;
27             }
28              
29 137419         492740 return $config;
30             }
31              
32             sub ns {
33 326740     326740 1 804189 my ( $class, $name, $address ) = @_;
34              
35 326740         1560908 return Zonemaster::Engine::Nameserver->new( { name => $name, address => $address } );
36             }
37              
38             sub zone {
39 163     163 1 27775 my ( $class, $name ) = @_;
40              
41 163         5923 return Zonemaster::Engine::Zone->new( { name => $name } );
42             }
43              
44             sub test_zone {
45 3     3 1 82 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 5113 my ( $class, $module, $zname ) = @_;
52              
53 61         355 return Zonemaster::Engine::Test->run_module( $module, $class->zone( $zname ) );
54             }
55              
56             sub test_method {
57 184     184 1 18615 my ( $class, $module, $method, @arguments ) = @_;
58              
59 184         913 return Zonemaster::Engine::Test->run_one( $module, $method, @arguments );
60             }
61              
62             sub all_tags {
63 1     1 1 234 my ( $class ) = @_;
64 1         2 my @res;
65              
66 1         4 foreach my $module ( 'Basic', sort { $a cmp $b } Zonemaster::Engine::Test->modules ) {
  18         22  
67 10         16 my $full = "Zonemaster::Engine::Test::$module";
68 10         30 my $ref = $full->metadata;
69 10         14 foreach my $list ( values %{$ref} ) {
  10         19  
70 60         73 push @res, map { uc( $module ) . q{:} . $_ } sort { $a cmp $b } @{$list};
  260         502  
  383         432  
  60         89  
71             }
72             }
73              
74 1         34 return @res;
75             }
76              
77             sub all_methods {
78 1     1 1 951 my ( $class ) = @_;
79 1         3 my %res;
80              
81 1         5 foreach my $module ( 'Basic', Zonemaster::Engine::Test->modules ) {
82 10         15 my $full = "Zonemaster::Engine::Test::$module";
83 10         73 my $ref = $full->metadata;
84 10         13 foreach my $method ( sort { $a cmp $b } keys %{$ref} ) {
  113         128  
  10         29  
85 60         68 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         14 return $recursor->recurse( $qname, $qtype, $qclass );
98             }
99              
100             sub add_fake_delegation {
101 4     4 1 792 my ( $class, $domain, $href ) = @_;
102              
103             # Check fake delegation
104 4         10 foreach my $name ( keys %{$href} ) {
  4         28  
105 14 100 66     45 if ( not defined $href->{$name} or not scalar @{ $href->{$name} } ) {
  14         48  
106 2 100       67 if ( Zonemaster::Engine::Zone->new( { name => $domain } )->is_in_zone( $name ) ) {
107 1         5 Zonemaster::Engine->logger->add(
108             FAKE_DELEGATION_IN_ZONE_NO_IP => { domain => $domain , ns => $name }
109             );
110             }
111             else {
112 1         19931 my @ips = Zonemaster::LDNS->new->name2addr($name);
113 1 50       38 if ( @ips ) {
114 0         0 push @{ $href->{$name} }, @ips;
  0         0  
115             }
116             else {
117 1         12 Zonemaster::Engine->logger->add(
118             FAKE_DELEGATION_NO_IP => { domain => $domain , ns => $name }
119             );
120             }
121             }
122             }
123             }
124 4         32 my $parent = $class->zone( $recursor->parent( $domain ) );
125 4         12 foreach my $ns ( @{ $parent->ns } ) {
  4         152  
126 48         175 $ns->add_fake_delegation( $domain => $href );
127             }
128              
129 4         134 return;
130             }
131              
132             sub add_fake_ds {
133 1     1 1 3 my ( $class, $domain, $aref ) = @_;
134              
135 1         6 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         27  
141 6         20 $ns->add_fake_ds( $domain => $aref );
142             }
143              
144 1         29 return;
145             }
146              
147             sub can_continue {
148 211     211 1 497 my ( $class ) = @_;
149              
150 211         682 return 1;
151            
152             }
153              
154             sub save_cache {
155 1     1 1 336 my ( $class, $filename ) = @_;
156              
157 1         6 return Zonemaster::Engine::Nameserver->save( $filename );
158             }
159              
160             sub preload_cache {
161 1     1 1 7 my ( $class, $filename ) = @_;
162              
163 1         9 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 10 return Zonemaster::Engine::Test->modules;
174             }
175              
176             sub start_time_now {
177 248     248 1 1271 Zonemaster::Engine::Logger->start_time_now();
178 248         445 return;
179             }
180              
181             sub reset {
182 2     2 1 170 Zonemaster::Engine::Logger->start_time_now();
183 2         17 Zonemaster::Engine::Nameserver->empty_cache();
184 2 100       14 $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   191 no Moose;
  26         55  
  26         230  
343             __PACKAGE__->meta->make_immutable;
344              
345             1;