File Coverage

blib/lib/Zonemaster/Backend/TestAgent.pm
Criterion Covered Total %
statement 111 133 83.4
branch 27 48 56.2
condition 7 21 33.3
subroutine 15 15 100.0
pod 0 5 0.0
total 160 222 72.0


line stmt bran cond sub pod time code
1             package Zonemaster::Backend::TestAgent;
2             our $VERSION = '1.1.0';
3              
4 1     1   799 use strict;
  1         3  
  1         37  
5 1     1   34 use warnings;
  1         4  
  1         35  
6 1     1   17 use 5.14.2;
  1         5  
7              
8 1     1   8 use DBI qw(:utils);
  1         2  
  1         216  
9 1     1   9 use JSON::PP;
  1         3  
  1         85  
10              
11 1     1   8 use Zonemaster::LDNS;
  1         3  
  1         40  
12              
13 1     1   6 use Zonemaster::Engine;
  1         3  
  1         26  
14 1     1   6 use Zonemaster::Engine::Translator;
  1         3  
  1         34  
15 1     1   5 use Zonemaster::Backend::Config;
  1         3  
  1         1813  
16              
17             sub new {
18 2     2 0 7 my ( $class, $params ) = @_;
19 2         6 my $self = {};
20              
21 2 50 33     16 if ( $params && $params->{db} ) {
22 2         128 eval "require $params->{db}";
23 2         98 $self->{db} = "$params->{db}"->new();
24             }
25             else {
26 0         0 my $backend_module = "Zonemaster::Backend::DB::" . Zonemaster::Backend::Config->BackendDBType();
27 0         0 eval "require $backend_module";
28 0         0 $self->{db} = $backend_module->new();
29             }
30              
31 2         6 bless( $self, $class );
32 2         9 return $self;
33             }
34              
35             sub run {
36 2     2 0 8 my ( $self, $test_id ) = @_;
37 2         9 my @accumulator;
38             my %counter;
39 2         0 my %counter_for_progress_indicator;
40              
41 2         0 my $params;
42              
43 2         11 my $progress = $self->{db}->test_progress( $test_id, 1 );
44              
45 2         21 $params = $self->{db}->get_test_params( $test_id );
46              
47 2         25 my %methods = Zonemaster::Engine->all_methods;
48              
49 2         1417 foreach my $module ( keys %methods ) {
50 20         29 foreach my $method ( @{ $methods{$module} } ) {
  20         34  
51 120         318 $counter_for_progress_indicator{planned}{ $module . '::' . $method } = $module . '::';
52             }
53             }
54              
55 2         9 my ( $domain ) = $params->{domain};
56 2 50       7 if ( !$domain ) {
57 0         0 die "Must give the name of a domain to test.\n";
58             }
59 2         10 $domain = $self->to_idn( $domain );
60              
61 2 50 33     84 if (defined $params->{ipv4} || defined $params->{ipv4}) {
62 2 50       20 Zonemaster::Engine->config->get->{net}{ipv4} = ( $params->{ipv4} ) ? ( 1 ) : ( 0 );
63 2 100       44 Zonemaster::Engine->config->get->{net}{ipv6} = ( $params->{ipv6} ) ? ( 1 ) : ( 0 );
64             }
65             else {
66 0         0 Zonemaster::Engine->config->get->{net}{ipv4} = 1;
67 0         0 Zonemaster::Engine->config->get->{net}{ipv6} = 1;
68             }
69              
70             # used for progress indicator
71 2         37 my ( $previous_module, $previous_method ) = ( '', '' );
72              
73             # Callback defined here so it closes over the setup above.
74             Zonemaster::Engine->logger->callback(
75             sub {
76 4218     4218   51233040 my ( $entry ) = @_;
77              
78 4218         7853 foreach my $trace ( reverse @{ $entry->trace } ) {
  4218         107014  
79 53215         92185 foreach my $module_method ( keys %{ $counter_for_progress_indicator{planned} } ) {
  53215         291424  
80 3192900 100       5617533 if ( index( $trace->[1], $module_method ) > -1 ) {
81 3060         5905 my $percent_progress = 0;
82 3060         19408 my ( $module ) = ( $module_method =~ /(.+::)[^:]+/ );
83 3060 100       9832 if ( $previous_module eq $module ) {
    100          
84 3042         7987 $counter_for_progress_indicator{executed}{$module_method}++;
85             }
86             elsif ( $previous_module ) {
87 16         44 foreach my $planned_module_method ( keys %{ $counter_for_progress_indicator{planned} } ) {
  16         158  
88             $counter_for_progress_indicator{executed}{$planned_module_method}++
89 960 100       1836 if ( $counter_for_progress_indicator{planned}{$planned_module_method} eq
90             $previous_module );
91             }
92             }
93 3060         5587 $previous_module = $module;
94              
95 3060 100       8889 if ( $previous_method ne $module_method ) {
96             $percent_progress = sprintf(
97             "%.0f",
98             100 * (
99 112         406 scalar( keys %{ $counter_for_progress_indicator{executed} } ) /
100 112         276 scalar( keys %{ $counter_for_progress_indicator{planned} } )
  112         716  
101             )
102             );
103 112         1052 $self->{db}->test_progress( $test_id, $percent_progress );
104              
105 112         712 $previous_method = $module_method;
106             }
107             }
108             }
109             }
110              
111 4218         146362 $counter{ uc $entry->level } += 1;
112             }
113 2         12 );
114              
115 2 50 33     101 if ( $params->{nameservers} && @{ $params->{nameservers} } > 0 ) {
  2         11  
116 2         11 $self->add_fake_delegation( $domain, $params->{nameservers} );
117             }
118              
119 2 50 33     15 if ( $params->{ds_info} && @{ $params->{ds_info} } > 0 ) {
  2         12  
120 2         11 $self->add_fake_ds( $domain, $params->{ds_info} );
121             }
122            
123              
124             # If the profile parameter has been set in the API, then load a profile
125 2 50       14 if ( $params->{profile} ) {
126 2 50 33     16 if ( $params->{profile} eq 'test_profile_1' and Zonemaster::Backend::Config->CustomProfilesPath()) {
127             # The config has defined an alternative profile and "test_profile_1" has been set.
128 0         0 Zonemaster::Engine->config->load_policy_file( Zonemaster::Backend::Config->CustomProfilesPath() . '/iana-profile.json' );
129             }
130             else { # The profile parameter has been set to something else or alternative profile is not defined
131 2         9 Zonemaster::Engine->config->load_policy_file( 'iana-profile.json' );
132             }
133             # It will be silently ignored if the file does not exist.
134             }
135              
136              
137 2 50       89435 if ( $params->{config} ) {
138 0         0 my $config_file_path = Zonemaster::Backend::Config->GetCustomConfigParameter('ZONEMASTER', $params->{config});
139 0 0       0 if ($config_file_path) {
140 0 0       0 if (-e $config_file_path) {
141 0         0 Zonemaster::Engine->config->load_config_file( $config_file_path );
142             }
143             else {
144 0         0 die "The file specified by the config parameter value: [$params->{config}] doesn't exist";
145             }
146             }
147             else {
148 0         0 die "Unknown test configuration: [$params->{config}]\n"
149             }
150             }
151              
152             # Actually run tests!
153 2         5 eval { Zonemaster::Engine->test_zone( $domain ); };
  2         26  
154 2 50       758 if ( $@ ) {
155 0         0 my $err = $@;
156 0 0 0     0 if ( blessed $err and $err->isa( "NormalExit" ) ) {
157 0         0 say STDERR "Exited early: " . $err->message;
158             }
159             else {
160 0         0 die $err; # Don't know what it is, rethrow
161             }
162             }
163              
164 2         783 $self->{db}->test_results( $test_id, Zonemaster::Engine->logger->json( 'INFO' ) );
165              
166 2         337 $progress = $self->{db}->test_progress( $test_id );
167              
168 2         59 return;
169             } ## end sub run
170              
171             sub add_fake_delegation {
172 2     2 0 7 my ( $self, $domain, $nameservers ) = @_;
173 2         4 my @ns_with_no_ip;
174             my %data;
175              
176 2         8 foreach my $ns_ip_pair ( @$nameservers ) {
177 4 100 66     21 if ( $ns_ip_pair->{ns} && $ns_ip_pair->{ip} ) {
    50          
178 2         5 push( @{ $data{ $self->to_idn( $ns_ip_pair->{ns} ) } }, $ns_ip_pair->{ip} );
  2         7  
179             }
180             elsif ($ns_ip_pair->{ns}) {
181 2         8 push(@ns_with_no_ip, $self->to_idn( $ns_ip_pair->{ns} ) );
182             }
183             else {
184 0         0 die "Invalid ns_ip_pair";
185             }
186             }
187              
188 2         5 foreach my $ns ( @ns_with_no_ip ) {
189 2 50       8 if ( not exists $data{ $ns } ) {
190 2         7 $data{ $self->to_idn( $ns ) } = undef;
191             }
192             }
193            
194 2         12 Zonemaster::Engine->add_fake_delegation( $domain => \%data );
195              
196 2         1018 return;
197             }
198              
199             sub add_fake_ds {
200 2     2 0 8 my ( $self, $domain, $ds_info ) = @_;
201 2         5 my @data;
202              
203 2         4 foreach my $ds ( @{ $ds_info } ) {
  2         7  
204 2         17 push @data, { keytag => $ds->{keytag}, algorithm => $ds->{algorithm}, type => $ds->{digtype}, digest => $ds->{digest} };
205             }
206              
207 2         10 Zonemaster::Engine->add_fake_ds( $domain => \@data );
208              
209 2         1282 return;
210             }
211              
212             sub to_idn {
213 8     8 0 18 my ( $self, $str ) = @_;
214              
215 8 50       36 if ( $str =~ m/^[[:ascii:]]+$/ ) {
216 8         32 return $str;
217             }
218              
219 0 0         if ( Zonemaster::LDNS::has_idn() ) {
220 0           return Zonemaster::LDNS::to_idn( $str );
221             }
222             else {
223 0           warn __( "Warning: Zonemaster::LDNS not compiled with libidn, cannot handle non-ASCII names correctly." );
224 0           return $str;
225             }
226             }
227              
228             1;