Skip to content

Commit

Permalink
Update MethodsV2 get_parent_ns_ips() to match new Basic01 algorithm
Browse files Browse the repository at this point in the history
Based on zonemaster/zonemaster#1287

- Update Zonemaster::Engine::TestMethodsV2->get_parent_ns_ips() code
- Add new message tag 'LOOP_PROTECTION' (used in three places)
- Add loop protection mechanism
- Update unit tests data
  • Loading branch information
tgreenx committed Jul 11, 2024
1 parent b83a7a7 commit 5ca62cc
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 89 deletions.
14 changes: 11 additions & 3 deletions lib/Zonemaster/Engine/Recursor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,21 @@ sub _recurse {
$state->{seen}{$zname} = 1;
my $common = name( $zname )->common( name( $state->{qname} ) );

next
if $common < $state->{common}; # Redirect going up the hierarchy is not OK
next if $common < $state->{common}; # Redirect going up the hierarchy is not OK

$state->{common} = $common;
$state->{ns} = $class->get_ns_from( $p, $state ); # Follow redirect
$state->{count} += 1;
return ( undef, $state ) if $state->{count} > 20; # Loop protection
if ( $state->{count} > 20 ) { # Loop protection
Zonemaster::Engine->logger->add( LOOP_PROTECTION => {
caller => 'Zonemaster::Engine::Recursor->_recurse',
child_zone_name => $name,
name => $zname
}
);

return ( undef, $state );
}
unshift @{ $state->{trace} }, [ $zname, $ns, $p->answerfrom ];

next;
Expand Down
14 changes: 14 additions & 0 deletions lib/Zonemaster/Engine/Test/Basic.pm
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,23 @@ sub basic01 {

my $intermediate_query_name = name( $zone_name );
my $loop_zone_name = $zone_name;
my $loop_counter = 0;

LOOP:
while() {
$loop_counter += 1;
if ( $loop_counter >= 1000 ) {
Zonemaster::Engine->logger->add( LOOP_PROTECTION => {
caller => 'Zonemaster::Engine::Test::Basic->basic01',
child_zone_name => $zone->name,
name => $loop_zone_name,
intermediate_query_name => $intermediate_query_name
}
);

return ( @results, _emit_log( TEST_CASE_END => { testcase => $Zonemaster::Engine::Logger::TEST_CASE_NAME } ) );
}

last if scalar @{ $intermediate_query_name->labels } >= scalar @{ $zone->name->labels };
$intermediate_query_name = name( @{ $zone->name->labels }[ ( scalar @{ $zone->name->labels } - scalar @{ $intermediate_query_name->labels } ) - 1 ] . '.' . $intermediate_query_name->string );

Expand Down
207 changes: 204 additions & 3 deletions lib/Zonemaster/Engine/TestMethodsV2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,211 @@ sub get_parent_ns_ips {
return [];
}

my $parent = $zone->parent;
my %handled_servers;
my @parent_found;

if ( $parent ) {
return $parent->ns;
my %rrs_ns;
my $type_soa = q{SOA};
my $type_ns = q{NS};

my %all_servers = ( '.' => [ Zonemaster::Engine::Recursor->root_servers ] );
my @all_labels = ( '.' );
my @remaining_labels = ( '.' );

ALL_SERVERS:
while ( my $zone_name = shift @remaining_labels ) {
my @remaining_servers = @{ $all_servers{$zone_name} };

CUR_SERVERS:
while ( my $ns = shift @remaining_servers ) {
next CUR_SERVERS if grep { $_ eq $ns->address->short } @{ $handled_servers{$zone_name} };
push @{ $handled_servers{$zone_name} }, $ns->address->short;

if ( ( $ns->address->version == 4 and not Zonemaster::Engine::Profile->effective->get( q{net.ipv4} ) )
or ( $ns->address->version == 6 and not Zonemaster::Engine::Profile->effective->get( q{net.ipv6} ) ) ) {
next CUR_SERVERS;
}

my $p_soa = $ns->query( $zone_name, $type_soa );

unless ( $p_soa and $p_soa->rcode eq 'NOERROR' and $p_soa->aa and scalar $p_soa->get_records_for_name( $type_soa, $zone_name, 'answer' ) == 1 ) {
next CUR_SERVERS;
}

my $p_ns = $ns->query( $zone_name, $type_ns );

unless ( $p_ns and $p_ns->rcode eq 'NOERROR' and $p_ns->aa and scalar $p_ns->get_records( $type_ns, 'answer' ) > 0
and scalar $p_ns->get_records( $type_ns, 'answer' ) == scalar $p_ns->get_records_for_name( $type_ns, $zone_name, 'answer' )
) {
next CUR_SERVERS;
}

$rrs_ns{name( $_->nsdname )->string} = [] for $p_ns->get_records_for_name( $type_ns, $zone_name, 'answer' );

foreach my $rr ( $p_ns->get_records( 'A', 'additional' ), $p_ns->get_records( 'AAAA', 'additional' ) ) {
if ( exists $rrs_ns{name( $rr->owner )->string} ) {
push @{ $rrs_ns{name( $rr->owner )->string} }, $rr->address;
}
}

foreach my $ns_name ( keys %rrs_ns ) {
unless ( scalar @{ $rrs_ns{$ns_name} } > 0 ) {
my $p_a = Zonemaster::Engine::Recursor->recurse( $ns_name, q{A} );

if ( $p_a and $p_a->rcode eq 'NOERROR' ) {
push @{ $rrs_ns{$ns_name} }, $_->address for $p_a->get_records_for_name( 'A', $ns_name );
}

my $p_aaaa = Zonemaster::Engine::Recursor->recurse( $ns_name, q{AAAA} );

if ( $p_aaaa and $p_aaaa->rcode eq 'NOERROR' ) {
push @{ $rrs_ns{$ns_name} }, $_->address for $p_aaaa->get_records_for_name( 'AAAA', $ns_name );
}
}

foreach my $ns_ip ( @{ $rrs_ns{$ns_name} } ) {
unless ( grep { $_ eq $ns_ip } @{ $handled_servers{$zone_name} } ) {
push @{ $all_servers{$zone_name} }, ns( $ns_name, $ns_ip );

unless ( grep { $_ eq $zone_name } @all_labels ) {
push @remaining_labels, $zone_name;
push @all_labels, $zone_name;
}
}
}
}

my $intermediate_query_name = name( $zone_name );
my $loop_zone_name = $zone_name;
my $loop_counter = 0;

LOOP:
while() {
$loop_counter += 1;
if ( $loop_counter >= 1000 ) {
Zonemaster::Engine->logger->add( LOOP_PROTECTION => {
caller => 'Zonemaster::Engine::TestMethodsV2->get_parent_ns_ips',
child_zone_name => $zone->name,
name => $loop_zone_name,
intermediate_query_name => $intermediate_query_name
}
);

return undef;
}

last if scalar @{ $intermediate_query_name->labels } >= scalar @{ $zone->name->labels };
$intermediate_query_name = name( @{ $zone->name->labels }[ ( scalar @{ $zone->name->labels } - scalar @{ $intermediate_query_name->labels } ) - 1 ] . '.' . $intermediate_query_name->string );

$p_soa = $ns->query( $intermediate_query_name, $type_soa );

unless ( $p_soa ) {
next CUR_SERVERS;
}

if ( $p_soa->rcode eq 'NOERROR' and $p_soa->aa and scalar $p_soa->get_records_for_name( $type_soa, $intermediate_query_name, 'answer' ) == 1 ) {
if ( $intermediate_query_name->string eq $zone->name->string ) {
push @parent_found, $ns;
}
else {
$p_ns = $ns->query( $intermediate_query_name, $type_ns );

unless ( $p_ns and $p_ns->rcode eq 'NOERROR' and $p_ns->aa and scalar $p_ns->get_records( $type_ns, 'answer' ) > 0
and scalar $p_ns->get_records( $type_ns, 'answer' ) == scalar $p_ns->get_records_for_name( $type_ns, $intermediate_query_name, 'answer' )
) {
next CUR_SERVERS;
}

my %rrs_ns_bis;
$rrs_ns_bis{name( $_->nsdname )->string} = [] for $p_ns->get_records_for_name( $type_ns, $intermediate_query_name, 'answer' );

foreach my $rr ( $p_ns->get_records( 'A', 'additional' ), $p_ns->get_records( 'AAAA', 'additional' ) ) {
if ( exists $rrs_ns_bis{name( $rr->owner )->string} ) {
push @{ $rrs_ns_bis{name( $rr->owner )->string} }, $rr->address;
}
}

foreach my $ns_name ( keys %rrs_ns_bis ) {
unless ( scalar @{ $rrs_ns_bis{$ns_name} } > 0 ) {
my $p_a = Zonemaster::Engine::Recursor->recurse( $ns_name, q{A} );

if ( $p_a and $p_a->rcode eq 'NOERROR' ) {
push @{ $rrs_ns_bis{$ns_name} }, $_->address for $p_a->get_records_for_name( 'A', $ns_name );
}

my $p_aaaa = Zonemaster::Engine::Recursor->recurse( $ns_name, q{AAAA} );

if ( $p_aaaa and $p_aaaa->rcode eq 'NOERROR' ) {
push @{ $rrs_ns_bis{$ns_name} }, $_->address for $p_aaaa->get_records_for_name( 'AAAA', $ns_name );
}
}

foreach my $ns_ip ( @{ $rrs_ns_bis{$ns_name} } ) {
unless ( grep { $_ eq $ns_ip } @{ $handled_servers{$intermediate_query_name} } ) {
push @{ $all_servers{$intermediate_query_name} }, ns( $ns_name, $ns_ip );

unless ( grep { $_ eq $intermediate_query_name } @all_labels ) {
push @remaining_labels, $intermediate_query_name;
push @all_labels, $intermediate_query_name;
}
}
}
}

$loop_zone_name = $intermediate_query_name->string;
next LOOP;
}
}
elsif ( $p_soa->is_redirect and scalar $p_soa->get_records_for_name( $type_ns, $intermediate_query_name, 'authority' ) ) {
if ( $intermediate_query_name->string eq $zone->name->string ) {
push @parent_found, $ns;
}
else {
my %rrs_ns_bis;
$rrs_ns_bis{name( $_->nsdname )->string} = [] for $p_soa->get_records_for_name( $type_ns, $intermediate_query_name, 'authority' );

foreach my $rr ( $p_soa->get_records( 'A', 'additional' ), $p_soa->get_records( 'AAAA', 'additional' ) ) {
if ( exists $rrs_ns_bis{name( $rr->owner )->string} ) {
push @{ $rrs_ns_bis{name( $rr->owner )->string} }, $rr->address;
}
}

foreach my $ns_name ( keys %rrs_ns_bis ) {
unless ( scalar @{ $rrs_ns_bis{$ns_name} } > 0 ) {
my $p_a = Zonemaster::Engine::Recursor->recurse( $ns_name, q{A} );

if ( $p_a and $p_a->rcode eq 'NOERROR' ) {
push @{ $rrs_ns_bis{$ns_name} }, $_->address for $p_a->get_records_for_name( 'A', $ns_name );
}

my $p_aaaa = Zonemaster::Engine::Recursor->recurse( $ns_name, q{AAAA} );

if ( $p_aaaa and $p_aaaa->rcode eq 'NOERROR' ) {
push @{ $rrs_ns_bis{$ns_name} }, $_->address for $p_aaaa->get_records_for_name( 'AAAA', $ns_name );
}
}

foreach my $ns_ip ( @{ $rrs_ns_bis{$ns_name} } ) {
unless ( grep { $_ eq $ns_ip } @{ $handled_servers{$intermediate_query_name} } ) {
push @{ $all_servers{$intermediate_query_name} }, ns( $ns_name, $ns_ip );

unless ( grep { $_ eq $intermediate_query_name } @all_labels ) {
push @remaining_labels, $intermediate_query_name;
push @all_labels, $intermediate_query_name;
}
}
}
}
}
}

next CUR_SERVERS;
}
}
}

if ( scalar @parent_found ) {
return [ uniq sort @parent_found ]
}
else {
return undef;
Expand Down
1 change: 1 addition & 0 deletions share/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@
"IS_REDIRECT" : "DEBUG2",
"LOGGER_CALLBACK_ERROR" : "DEBUG",
"LOOKUP_ERROR" : "DEBUG",
"LOOP_PROTECTION" : "DEBUG2",
"MODULE_ERROR" : "CRITICAL",
"MODULE_VERSION" : "DEBUG",
"MODULE_END" : "DEBUG",
Expand Down
1 change: 1 addition & 0 deletions share/profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ test_levels:
IS_REDIRECT: DEBUG2
LOGGER_CALLBACK_ERROR: DEBUG
LOOKUP_ERROR: DEBUG
LOOP_PROTECTION: DEBUG2
MODULE_END: DEBUG
MODULE_ERROR: CRITICAL
MODULE_VERSION: DEBUG
Expand Down
Loading

0 comments on commit 5ca62cc

Please sign in to comment.