Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates data file for unit test #1

Open
wants to merge 2 commits into
base: cname-in-recursor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ t/profiles/Test-syntax-all.json
t/profiles/Test-zone-all.json
t/recursor.data
t/recursor.t
t/recursor-A.data
t/recursor-A.t
t/Test-address.data
t/Test-address.t
t/Test-basic.data
Expand Down
67 changes: 62 additions & 5 deletions lib/Zonemaster/Engine/Recursor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ sub recurse {

my ( $p, $state ) =
$class->_recurse( $name, $type, $dns_class,
{ ns => [ root_servers() ], count => 0, common => 0, seen => {}, glue => {} } );
{ ns => [ root_servers() ], count => 0, common => 0, seen => {}, cseen => {}, glue => {} } );
$recurse_cache{$name}{$type}{$dns_class} = $p;

return $p;
Expand All @@ -111,7 +111,7 @@ sub parent {

my ( $p, $state ) =
$class->_recurse( $name, 'SOA', 'IN',
{ ns => [ root_servers() ], count => 0, common => 0, seen => {}, glue => {} } );
{ ns => [ root_servers() ], count => 0, common => 0, seen => {}, cseen => {}, glue => {} } );

my $pname;
if ( name( $state->{trace}[0][0] ) eq name( $name ) ) {
Expand Down Expand Up @@ -193,8 +193,65 @@ sub _recurse {
return ( $p, $state );
}

if ( $class->_is_answer( $p ) ) { # Return answer
return ( $p, $state );
if ( $class->_is_answer( $p ) ) { # Return answer, or follow CNAME
if ( not $p->has_rrs_of_type_for_name( $type, $name ) and $p->has_rrs_of_type_for_name( 'CNAME', $name ) ) {
if ( scalar $p->get_records_for_name( 'CNAME', $name, 'answer') > 1 ) { # Multiple CNAME records with QNAME as owner name
Zonemaster::Engine->logger->add( CNAME_MULTIPLE_FOR_NAME => { name => $name } );
return ( undef, $state );
}
else {
my ( %cnames, %tseen );

for my $rr ( $p->get_records( 'CNAME', 'answer' ) ) {
my $rr_owner = name( $rr->owner );
my $rr_target = name( $rr->cname );

if ( scalar $p->get_records_for_name( 'CNAME', $rr_owner, 'answer' ) > 1 ) { # Multiple CNAME records with same owner name
Zonemaster::Engine->logger->add( CNAME_MULTIPLE_FOR_NAME => { name => $rr_owner->string } );
return ( undef, $state );
}

if ( lc( $rr_owner ) eq lc( $rr_target ) or exists $tseen{lc( $rr_target )} ) { # CNAME owner name is target, or target has already been seen in this response
Zonemaster::Engine->logger->add( CNAME_LOOP_INNER => { name => $rr_owner->string, target => $rr_target->string } );
return ( undef, $state );
}

$tseen{lc( $rr_target )} = 1;
$cnames{$rr_owner} = $rr_target;
}

my $target = $name;
$target = $cnames{$target} while $cnames{$target};

if ( $state->{cseen}{lc( $target )} ) { # CNAME target has already been followed (outer loop)
Zonemaster::Engine->logger->add( CNAME_LOOP_OUTER => { name => $name, target => $target, cnames => join( ';', keys %{ $state->{cseen} } ) } );
return ( undef, $state );
}

$state->{cseen}{lc( $target )} = 1;

if ( lc( $target ) eq lc( $name ) ) { # CNAME target is QNAME (inner loop)
Zonemaster::Engine->logger->add( CNAME_LOOP_INNER => { name => $name, target => $target } );
return ( undef, $state );
}

$state->{count} += 1;
return ( undef, $state ) if $state->{count} > 100; # Loop protection

if ( $p->has_rrs_of_type_for_name( $type, $target ) ) { # RR for CNAME target is in response
return ( $p, $state );
}

# Otherwise, make a new recursive query for CNAME target
( $p, $state ) = $class->_recurse( $target, $type, $dns_class,
{ ns => [ root_servers() ], count => $state->{count}, common => 0, seen => {}, cseen => $state->{cseen}, glue => {} });

return ( $p, $state );
}
}
else {
return ( $p, $state );
}
}

# So it's not an error, not an empty response and not an answer
Expand Down Expand Up @@ -297,7 +354,7 @@ sub get_addresses_for {
my ( $class, $name, $state ) = @_;
my @res;
$state //=
{ ns => [ root_servers() ], count => 0, common => 0, seen => {} };
{ ns => [ root_servers() ], count => 0, common => 0, seen => {}, cseen => {} };

my ( $pa ) = $class->_recurse(
"$name", 'A', 'IN',
Expand Down
10 changes: 4 additions & 6 deletions lib/Zonemaster/Engine/Test/Syntax.pm
Original file line number Diff line number Diff line change
Expand Up @@ -918,16 +918,15 @@ sub syntax06 {

my $domain = ( $rname =~ s/.*@//r );
my $p_mx = Zonemaster::Engine::Recursor->recurse( $domain, q{MX} );

if ( not $p_mx or $p_mx->rcode ne 'NOERROR' ) {
push @results, info( RNAME_MAIL_DOMAIN_INVALID => { domain => $domain } );
next;
}

# Follow CNAMEs in the MX response
my %cnames =
map { $_->owner => $_->cname } $p_mx->get_records( q{CNAME}, q{answer} );
$domain .= q{.}; # Add back final dot
$domain = $cnames{$domain} while $cnames{$domain};
# Retrieve followed CNAME, if any
my @p_question_sec = $p_mx->question;
$domain = $p_question_sec[0]->owner;

# Determine mail domain(s)
my @mail_domains;
Expand All @@ -939,7 +938,6 @@ sub syntax06 {
}

for my $mail_domain ( @mail_domains ) {

# Assume mail domain is invalid until we see an actual IP address
my $exchange_valid = 0;

Expand Down
10 changes: 8 additions & 2 deletions lib/Zonemaster/Engine/Test/Zone.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1056,13 +1056,18 @@ sub zone07 {
my $soa_mname = $soa->mname;
$soa_mname =~ s/[.]\z//smx;
my $addresses_nb = 0;

foreach my $address_type ( q{A}, q{AAAA} ) {
my $p_mname = Zonemaster::Engine::Recursor->recurse( $soa_mname, $address_type );
if ( $p_mname ) {
if ( $p_mname->has_rrs_of_type_for_name( $address_type, $soa_mname ) ) {
my @p_question_sec = $p_mname->question;
my $final_name = name( $p_question_sec[0]->owner );

if ( $p_mname->has_rrs_of_type_for_name( $address_type, $final_name ) ) {
$addresses_nb++;
}
if ( $p_mname->has_rrs_of_type_for_name( q{CNAME}, $soa_mname ) ) {

if ( $final_name ne $soa_mname ) {
push @results,
info(
MNAME_IS_CNAME => {
Expand All @@ -1080,6 +1085,7 @@ sub zone07 {
}
} ## end if ( $p_mname )
} ## end foreach my $address_type ( ...)

if ( not $addresses_nb ) {
push @results,
info(
Expand Down
3 changes: 3 additions & 0 deletions share/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@
"CACHE_FETCHED" : "DEBUG2",
"CACHED_RETURN" : "DEBUG3",
"CANNOT_CONTINUE" : "CRITICAL",
"CNAME_LOOP_INNER" : "DEBUG",
"CNAME_LOOP_OUTER" : "DEBUG",
"CNAME_MULTIPLE_FOR_NAME" : "DEBUG",
"DEPENDENCY_VERSION" : "DEBUG",
"EMPTY_RETURN" : "DEBUG3",
"EXTERNAL_RESPONSE" : "DEBUG3",
Expand Down
3 changes: 3 additions & 0 deletions share/profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ test_levels:
CACHE_CREATED: DEBUG2
CACHE_FETCHED: DEBUG2
CANNOT_CONTINUE: CRITICAL
CNAME_LOOP_INNER : DEBUG
CNAME_LOOP_OUTER : DEBUG
CNAME_MULTIPLE_FOR_NAME : DEBUG
DEPENDENCY_VERSION: DEBUG
EMPTY_RETURN: DEBUG3
EXTERNAL_RESPONSE: DEBUG3
Expand Down
Loading