Skip to content

Commit

Permalink
Merge pull request #211 from tgreenx/add-nsec3-hashname
Browse files Browse the repository at this point in the history
Compute hashed name from a Zonemaster::LDNS::RR::{NSEC3,NSEC3PARAM} record
  • Loading branch information
tgreenx authored Nov 19, 2024
2 parents ae904a7 + b316053 commit 15cfc11
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 6 deletions.
4 changes: 4 additions & 0 deletions lib/Zonemaster/LDNS/RR/NSEC3.pm
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ Returns the typelist as a reference to a hash where the included types are keys
Returns true or false depending on if the record covers the given name or not.
=item hash_name($name)
Computes and returns a hash, in canonical form, of the given name using the parameters (algorithm, iterations, salt) of the resource record.
=back
4 changes: 4 additions & 0 deletions lib/Zonemaster/LDNS/RR/NSEC3PARAM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ Returns the iteration count.
Returns the contents of the salt field as a binary string, if non-empty; otherwise, returns an empty string.
=item hash_name($name)
Computes and returns a hash, in canonical form, of the given name using the parameters (algorithm, iterations, salt) of the resource record.
=back
65 changes: 64 additions & 1 deletion src/LDNS.xs
Original file line number Diff line number Diff line change
Expand Up @@ -2506,7 +2506,6 @@ rr_nsec3_covers(obj,name)
ldns_rdf *next_owner = ldns_nsec3_next_owner(obj);
if (!next_owner || ldns_rdf_size(next_owner) <= 1)
XSRETURN_UNDEF;

CODE:
{
ldns_rr *clone;
Expand Down Expand Up @@ -2543,6 +2542,38 @@ rr_nsec3_covers(obj,name)
OUTPUT:
RETVAL

SV *
rr_nsec3_hash_name(obj,name)
Zonemaster::LDNS::RR::NSEC3 obj;
const char *name;
INIT:
/* Sanity test on owner name */
if (ldns_dname_label_count(ldns_rr_owner(obj)) == 0)
XSRETURN_UNDEF;
CODE:
{
ldns_rdf *hashed;
ldns_rdf *dname;

dname = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, name);
if (!dname)
XSRETURN_UNDEF;

hashed = ldns_nsec3_hash_name_frm_nsec3(obj, dname);

ldns_rdf_deep_free(dname);

if (!hashed || ldns_rdf_size(hashed) < 1 ) {
XSRETURN_UNDEF;
}

char *hash_str = ldns_rdf2str(hashed);
RETVAL = newSVpv(hash_str, ldns_rdf_size(hashed) - 2);
free(hash_str);
}
OUTPUT:
RETVAL

MODULE = Zonemaster::LDNS PACKAGE = Zonemaster::LDNS::RR::NSEC3PARAM PREFIX=rr_nsec3param_

U8
Expand Down Expand Up @@ -2585,6 +2616,38 @@ rr_nsec3param_salt(obj)
OUTPUT:
RETVAL

SV *
rr_nsec3param_hash_name(obj,name)
Zonemaster::LDNS::RR::NSEC3PARAM obj;
const char *name;
INIT:
/* Sanity test on owner name */
if (ldns_dname_label_count(ldns_rr_owner(obj)) == 0)
XSRETURN_UNDEF;
CODE:
{
ldns_rdf *hashed;
ldns_rdf *dname;

dname = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, name);
if (!dname)
XSRETURN_UNDEF;

hashed = ldns_nsec3_hash_name_frm_nsec3(obj, dname);

ldns_rdf_deep_free(dname);

if (!hashed || ldns_rdf_size(hashed) < 1 ) {
XSRETURN_UNDEF;
}

char *hash_str = ldns_rdf2str(hashed);
RETVAL = newSVpv(hash_str, ldns_rdf_size(hashed) - 2);
free(hash_str);
}
OUTPUT:
RETVAL

MODULE = Zonemaster::LDNS PACKAGE = Zonemaster::LDNS::RR::PTR PREFIX=rr_ptr_

char *
Expand Down
47 changes: 42 additions & 5 deletions t/rr.t
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ subtest 'CDS' => sub {
};

subtest 'NSEC3 without salt' => sub {
my $name = 'com';
my $nsec3 = Zonemaster::LDNS::RR->new_from_string(
'VD0J8N54V788IUBJL9CN5MUD416BS5I6.com. 86400 IN NSEC3 1 1 0 - VD0N3HDL5MG940MOUBCF5MNLKGDT9RFT NS DS RRSIG' );
isa_ok( $nsec3, 'Zonemaster::LDNS::RR::NSEC3' );
Expand All @@ -286,26 +287,47 @@ subtest 'NSEC3 without salt' => sub {
is( $nsec3->salt, '' );
is( encode_base32hex( $nsec3->next_owner ), "VD0N3HDL5MG940MOUBCF5MNLKGDT9RFT" );
is( $nsec3->typelist, 'NS DS RRSIG ' );
eq_or_diff( $nsec3->hash_name( $name ), 'ck0pojmg874ljref7efn8430qvit8bsm' );

is_deeply( [ sort keys %{ $nsec3->typehref } ], [qw(DS NS RRSIG)] );
};

subtest 'NSEC3 with salt' => sub {
my $name = 'bad-values.dnssec03.xa';
my $nsec3 = Zonemaster::LDNS::RR->new_from_string(
'BP7OICBR09FICEULBF46U8DMJ1J1V8R3.bad-values.dnssec03.xa. 900 IN NSEC3 2 1 1 8104 c91qe244nd0q5qh3jln35a809mik8d39 A NS SOA MX TXT RRSIG DNSKEY NSEC3PARAM' );
'BP7OICBR09FICEULBF46U8DMJ1J1V8R3.bad-values.dnssec03.xa. 900 IN NSEC3 1 1 3 8104 c91qe244nd0q5qh3jln35a809mik8d39 A NS SOA MX TXT RRSIG DNSKEY NSEC3PARAM' );
isa_ok( $nsec3, 'Zonemaster::LDNS::RR::NSEC3' );
is( $nsec3->algorithm, 2 );
is( $nsec3->algorithm, 1 );
is( $nsec3->flags, 1 );
ok( $nsec3->optout );
is( $nsec3->iterations, 1 );
is( $nsec3->iterations, 3 );
is( unpack('H*', $nsec3->salt), '8104' );
is( encode_base32hex( $nsec3->next_owner ), "C91QE244ND0Q5QH3JLN35A809MIK8D39" );
is( $nsec3->typelist, 'A NS SOA MX TXT RRSIG DNSKEY NSEC3PARAM ' );
eq_or_diff( $nsec3->hash_name( $name ), 'u6sj7nlrc80gcqem0ip18mji3vk60djt' );

is_deeply( [ sort keys %{ $nsec3->typehref } ], [qw(A DNSKEY MX NS NSEC3PARAM RRSIG SOA TXT)] );
};

subtest 'NSEC3 with unknown algorithm' => sub {
my $name = 'nsec3-mismatches-apex-1.dnssec10.xa.';
my $nsec3 = Zonemaster::LDNS::RR->new_from_string(
'G4CPF6T01H8B5U5O996K8HI4OTEE12AA.nsec3-mismatches-apex-1.dnssec10.xa. 86400 IN NSEC3 3 0 0 - UP848IGD2MT1JGD0ISJEB6LAS1DCB11R NS SOA RRSIG DNSKEY NSEC3PARAM TYPE65534' );
isa_ok( $nsec3, 'Zonemaster::LDNS::RR::NSEC3' );
is( $nsec3->algorithm, 3 );
is( $nsec3->flags, 0 );
is( $nsec3->optout, '' );
is( $nsec3->iterations, 0 );
is( unpack('H*', $nsec3->salt), '' );
is( encode_base32hex( $nsec3->next_owner ), "UP848IGD2MT1JGD0ISJEB6LAS1DCB11R" );
is( $nsec3->typelist, 'NS SOA RRSIG DNSKEY NSEC3PARAM TYPE65534 ' );
eq_or_diff( $nsec3->hash_name( $name ), undef );

is_deeply( [ sort keys %{ $nsec3->typehref } ], [qw(DNSKEY NS NSEC3PARAM RRSIG SOA TYPE65534)] );
};

subtest 'NSEC3PARAM without salt and non-zero flags' => sub {
my $name = 'empty-nsec3param.example';
my $nsec3param = Zonemaster::LDNS::RR->new_from_string(
'empty-nsec3param.example. 86400 IN NSEC3PARAM 1 165 0 -' );
isa_ok( $nsec3param, 'Zonemaster::LDNS::RR::NSEC3PARAM' );
Expand All @@ -314,16 +336,31 @@ subtest 'NSEC3PARAM without salt and non-zero flags' => sub {
is( $nsec3param->iterations, 0 );
is( $nsec3param->salt, '', 'Salt');
is( lc($nsec3param->owner), 'empty-nsec3param.example.' );
eq_or_diff( $nsec3param->hash_name( $name ), 'l73q01jb3imjq6krmm5h00evfsdpmbvl' );
};

subtest 'NSEC3PARAM with salt' => sub {
my $nsec3param = Zonemaster::LDNS::RR->new_from_string( 'whitehouse.gov. 3600 IN NSEC3PARAM 1 0 1 B2C19AB526819347' );
my $name = 'whitehouse.gov.';
my $nsec3param = Zonemaster::LDNS::RR->new_from_string( 'whitehouse.gov. 3600 IN NSEC3PARAM 1 0 2 B2C19AB526819347' );
isa_ok( $nsec3param, 'Zonemaster::LDNS::RR::NSEC3PARAM' );
is( $nsec3param->algorithm, 1 );
is( $nsec3param->flags, 0 );
is( $nsec3param->iterations, 1, "Iterations" );
is( $nsec3param->iterations, 2, "Iterations" );
is( uc(unpack( 'H*', $nsec3param->salt )), 'B2C19AB526819347', "Salt" );
is( lc($nsec3param->owner), 'whitehouse.gov.' );
eq_or_diff( $nsec3param->hash_name( $name ), '2mo42ugf34bnruvljv91vv9vqd4kckda' );
};

subtest 'NSEC3PARAM with unknown algorithm' => sub {
my $name = 'GOOD-NSEC3-1.dnssec10.xa';
my $nsec3param = Zonemaster::LDNS::RR->new_from_string( 'good-nsec3-1.dnssec10.xa. 0 IN NSEC3PARAM 3 0 0 -' );
isa_ok( $nsec3param, 'Zonemaster::LDNS::RR::NSEC3PARAM' );
is( $nsec3param->algorithm, 3 );
is( $nsec3param->flags, 0 );
is( $nsec3param->iterations, 0 );
is( unpack('H*', $nsec3param->salt), '' );
is( lc($nsec3param->owner), 'good-nsec3-1.dnssec10.xa.' );
eq_or_diff( $nsec3param->hash_name( $name ), undef );
};

subtest 'SIG' => sub {
Expand Down

0 comments on commit 15cfc11

Please sign in to comment.