Skip to content

Commit

Permalink
Merge pull request #181 from zonemaster/develop
Browse files Browse the repository at this point in the history
Merge develop into master (Zonemaster-LDNS)
  • Loading branch information
matsduf authored Mar 18, 2024
2 parents 4dac113 + 785cf01 commit d302f8c
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 29 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ env:

language: perl
perl:
- "5.32"
- "5.36"
- "5.30.2"
- "5.26"
- "5.14.4"

before_install:
# quoting preserves newlines in the script and then avoid error if the
Expand Down
14 changes: 14 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
Release history for Zonemaster component Zonemaster-LDNS

4.0.0 2024-03-18 (public release version)

[Breaking change]
- Fix crashing induced by Zonemaster::LDNS::RR::NSEC3::salt()
method (#177)
- Fix other methods in Zonemaster::LDNS::RR::NSEC3 and
::NSEC3PARAM (#178)

[Fixes]
- Ignore incomplete RRs (#136)
- Validate inputs to Zonemaster::LDNS::RR::NSEC3::covers()
(#176)


3.2.0 2023-06-21 (public fix version)
[Feature]
- Expand DNAME support (#170)
Expand Down
1 change: 1 addition & 0 deletions Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ configure_requires 'Module::Install::XSUtil';
test_requires 'JSON::PP';
test_requires 'Test::Fatal';
test_requires 'Test::More' => 1.302015;
test_requires 'MIME::Base32';

use_ppport 3.19;
cc_include_paths 'include';
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Compile-time dependencies (only when installing from source):
* `automake` (if [Internal ldns] is enabled)

Test-time dependencies:
* `MIME::Base32`
* `Test::Fatal`

There is a small part in the code that may not be compatible with non-Unix
Expand Down
2 changes: 1 addition & 1 deletion lib/Zonemaster/LDNS.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package Zonemaster::LDNS;

use 5.014;

our $VERSION = '3.2.0';
our $VERSION = '4.0.0';

use parent 'Exporter';
our @EXPORT_OK = qw[to_idn has_idn ldns_version load_zonefile];
Expand Down
16 changes: 15 additions & 1 deletion lib/Zonemaster/LDNS/Packet.pm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ sub answer {
my @records = $self->answer_unfiltered;

for ( my $i = $#records ; $i >= 0 ; --$i ) {
if ( $records[$i]->type() eq 'DNSKEY' && $records[$i]->keysize() == -1 ) {
if ( !$records[$i]->check_rd_count()
|| ( $records[$i]->type() eq 'DNSKEY' && $records[$i]->keysize() == -1 ) )
{
splice @records, $i, 1;
}
}
Expand All @@ -45,6 +47,12 @@ sub authority {

my @records = $self->authority_unfiltered;

for ( my $i = $#records ; $i >= 0 ; --$i ) {
if ( !$records[$i]->check_rd_count() ) {
splice @records, $i, 1;
}
}

return @records;
}

Expand All @@ -53,6 +61,12 @@ sub additional {

my @records = $self->additional_unfiltered;

for ( my $i = $#records ; $i >= 0 ; --$i ) {
if ( !$records[$i]->check_rd_count() ) {
splice @records, $i, 1;
}
}

return @records;
}

Expand Down
1 change: 1 addition & 0 deletions lib/Zonemaster/LDNS/RR.pm
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ use Zonemaster::LDNS::RR::RKEY;
use Zonemaster::LDNS::RR::RP;
use Zonemaster::LDNS::RR::RRSIG;
use Zonemaster::LDNS::RR::RT;
use Zonemaster::LDNS::RR::SIG;
use Zonemaster::LDNS::RR::SINK;
use Zonemaster::LDNS::RR::SOA;
use Zonemaster::LDNS::RR::SPF;
Expand Down
4 changes: 2 additions & 2 deletions lib/Zonemaster/LDNS/RR/NSEC3.pm
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ Returns the iteration count.
=item salt()
Returns the cryptographic salt, in binary form.
Returns the contents of the salt field as a binary string, if non-empty; otherwise, returns an empty string. If there was a problem accessing the salt field, returns undef.
=item next_owner()
Returns the next owner field.
Returns the next hashed owner name field, in binary form. To convert the return value to the human-readable presentation format, use L<MIME::Base32/encode_base32hex>.
=item typelist()
Expand Down
2 changes: 1 addition & 1 deletion lib/Zonemaster/LDNS/RR/NSEC3PARAM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ Returns the iteration count.
=item salt()
Returns the cryptographic salt in binary form.
Returns the contents of the salt field as a binary string, if non-empty; otherwise, returns an empty string.
=back
112 changes: 98 additions & 14 deletions src/LDNS.xs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,46 @@ rr_rdf(obj,n)
OUTPUT:
RETVAL

bool
rr_check_rd_count(obj)
Zonemaster::LDNS::RR obj;
CODE:
ldns_rr_type rr_type = ldns_rr_get_type(obj);
ldns_rr_descriptor *desc = ldns_rr_descript(rr_type);
size_t rd_min = ldns_rr_descriptor_minimum(desc);
size_t rd_max = ldns_rr_descriptor_maximum(desc);
size_t rd_count = ldns_rr_rd_count(obj);

// Workaround for when the last field is variable length with length
// zero, and ldns represents this by omitting the last field from
// the field list.
if (rd_min > 0 && rd_min == rd_max)
{
switch (ldns_rr_descriptor_field_type(desc,rd_min-1))
{
// This list is taken from ldns_wire2rdf()
case LDNS_RDF_TYPE_APL:
case LDNS_RDF_TYPE_B64:
case LDNS_RDF_TYPE_HEX:
case LDNS_RDF_TYPE_NSEC:
case LDNS_RDF_TYPE_UNKNOWN:
case LDNS_RDF_TYPE_SERVICE:
case LDNS_RDF_TYPE_LOC:
case LDNS_RDF_TYPE_WKS:
case LDNS_RDF_TYPE_NSAP:
case LDNS_RDF_TYPE_ATMA:
case LDNS_RDF_TYPE_IPSECKEY:
case LDNS_RDF_TYPE_LONG_STR:
case LDNS_RDF_TYPE_AMTRELAY:
case LDNS_RDF_TYPE_NONE:
rd_min -= 1;
}
}

RETVAL = rd_min <= rd_count && rd_count <= rd_max;
OUTPUT:
RETVAL

void
rr_DESTROY(obj)
Zonemaster::LDNS::RR obj;
Expand Down Expand Up @@ -2275,20 +2315,36 @@ rr_nsec3_iterations(obj)
SV *
rr_nsec3_salt(obj)
Zonemaster::LDNS::RR::NSEC3 obj;
PPCODE:
if(ldns_nsec3_salt_length(obj) > 0)
{
ldns_rdf *buf = ldns_nsec3_salt(obj);
ST(0) = sv_2mortal(newSVpvn((char *)ldns_rdf_data(buf), ldns_rdf_size(buf)));
ldns_rdf_deep_free(buf);
CODE:
{
uint8_t *salt = ldns_nsec3_salt_data(obj);
if (salt) {
RETVAL = newSVpvn((char *)salt, ldns_nsec3_salt_length(obj));
LDNS_FREE(salt);
}
}
OUTPUT:
RETVAL

SV *
rr_nsec3_next_owner(obj)
Zonemaster::LDNS::RR::NSEC3 obj;
INIT:
ldns_rdf *buf = NULL;
size_t size;
CODE:
ldns_rdf *buf = ldns_nsec3_next_owner(obj);
RETVAL = newSVpvn((char *)ldns_rdf_data(buf), ldns_rdf_size(buf));
buf = ldns_nsec3_next_owner(obj);
if (!buf) {
XSRETURN_UNDEF;
}
size = ldns_rdf_size(buf);
if (size < 1) {
XSRETURN_UNDEF;
}

/* ldns_rdf_data(buf) points to the hashed next owner name preceded by a
* length byte, which we don't want. */
RETVAL = newSVpvn((char *)(ldns_rdf_data(buf) + 1), size - 1);
OUTPUT:
RETVAL

Expand Down Expand Up @@ -2337,22 +2393,45 @@ bool
rr_nsec3_covers(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;

/* Sanity test on hashed next owner field */
ldns_rdf *next_owner = ldns_nsec3_next_owner(obj);
if (!next_owner || ldns_rdf_size(next_owner) <= 1)
XSRETURN_UNDEF;

CODE:
{
ldns_rr *clone;
ldns_rdf *dname;
ldns_rdf *hashed;
ldns_rdf *chopped;

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

ldns_dname2canonical(dname);

chopped = ldns_dname_left_chop(dname);
if (!chopped) {
ldns_rdf_deep_free(dname);
XSRETURN_UNDEF;
}

clone = ldns_rr_clone(obj);
ldns_rr2canonical(clone);
hashed = ldns_nsec3_hash_name_frm_nsec3(clone, dname);
chopped = ldns_dname_left_chop(dname);

ldns_rdf_deep_free(dname);

ldns_dname_cat(hashed,chopped);

RETVAL = ldns_nsec_covers_name(clone,hashed);

ldns_rdf_deep_free(hashed);
ldns_rdf_deep_free(chopped);
ldns_rr_free(clone);
Expand Down Expand Up @@ -2390,12 +2469,17 @@ rr_nsec3param_iterations(obj)
SV *
rr_nsec3param_salt(obj)
Zonemaster::LDNS::RR::NSEC3PARAM obj;
PPCODE:
ldns_rdf *rdf = ldns_rr_rdf(obj,3);
if(ldns_rdf_size(rdf) > 0)
CODE:
{
ldns_rdf *rdf = ldns_rr_rdf(obj, 3);
size_t size = ldns_rdf_size(rdf);
if (size > 0)
{
mPUSHs(newSVpvn((char *)ldns_rdf_data(rdf), ldns_rdf_size(rdf)));
RETVAL = newSVpvn((char *)(ldns_rdf_data(rdf) + 1), size - 1);
}
}
OUTPUT:
RETVAL

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

Expand Down
21 changes: 21 additions & 0 deletions t/dnssec.t
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,31 @@ my $nsec = Zonemaster::LDNS::RR->new('xx.se. 7200 IN NSEC xx0r.se. NS RRSIG NS
isa_ok($nsec, 'Zonemaster::LDNS::RR::NSEC');
ok($nsec->covers('xx-example.se'), 'Covers xx-example.se');

ok(!$nsec->covers('.'), 'Does not cover the root domain');

my $nsec3 = Zonemaster::LDNS::RR->new('NR2E513KM693MBTNVHH56ENF54F886T0.com. 86400 IN NSEC3 1 1 0 - NR2FUHQVR56LH70L6F971J3L6N1RH2TU NS DS RRSIG');
isa_ok($nsec3, 'Zonemaster::LDNS::RR::NSEC3');
ok($nsec3->covers('xx-example.com'), 'Covers xx-example.com');

is($nsec3->covers('.'), undef, 'Does not cover the root domain');

subtest 'malformed NSEC3 do not cover anything' => sub {
# Malformed resource record lacking a next hashed owner name field in its
# RDATA. The only way to synthesize such a datum is to use the RFC 3597
# syntax.
my $example = Zonemaster::LDNS::RR->new(
q{example. 0 IN NSEC3 \# 15 01 00 0001 01 AB 00 0006 400000000002}
);
is( $example->covers("example"), undef );

# NSEC3 resource record whose owner name is the root name. This should
# normally not happen.
$example = Zonemaster::LDNS::RR->new(
q{. 0 IN NSEC3 1 0 1 ab 01234567 A RRSIG}
);
is( $example->covers("example"), undef );
};

SKIP: {
skip 'no network', 3 unless $ENV{TEST_WITH_NETWORK};

Expand Down
38 changes: 35 additions & 3 deletions t/packet.t
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use Test::More;
use Test::Fatal;

use strict;
use warnings;
use Test::More;

use MIME::Base64;
use Test::Differences;
use Test::Fatal;

use_ok('Zonemaster::LDNS');

Expand Down Expand Up @@ -59,4 +61,34 @@ subtest "croak when stringifying packet with malformed CAA" => sub {
like( exception { $will_croak->() }, qr/^Failed to convert packet to string/ );
};

subtest "Answer section" => sub {
# Parse a packet with a single incomplete MX record
my $data = decode_base64( "EjSFgAABAAIAAAAAB2V4YW1wbGUCc2UAAA8AAcAMAA8AAQABUYAAAgAKwAwAAQABAAFRgAAEwAACAQ==");
my $p = Zonemaster::LDNS::Packet->new_from_wireformat( $data );

my $rr_count = scalar $p->answer;

is $rr_count, 1, "keep complete RRs but ignore incomplete ones";
};

subtest "Authority section" => sub {
# Parse a packet with a single incomplete MX record
my $data = decode_base64( "EjSFgAABAAAAAgAAB2V4YW1wbGUCc2UAAA8AAcAMAA8AAQABUYAAAgAKwAwAAQABAAFRgAAEwAACAQ==" );
my $p = Zonemaster::LDNS::Packet->new_from_wireformat( $data );

my $rr_count = scalar $p->authority;

is $rr_count, 1, "keep complete RRs but ignore incomplete ones";
};

subtest "Additional section" => sub {
# Parse a packet with a single incomplete MX record
my $data = decode_base64( "EjSFgAABAAAAAAACB2V4YW1wbGUCc2UAAA8AAcAMAA8AAQABUYAAAgAKwAwAAQABAAFRgAAEwAACAQ==" );
my $p = Zonemaster::LDNS::Packet->new_from_wireformat( $data );

my $rr_count = scalar $p->additional;

is $rr_count, 1, "keep complete RRs but ignore incomplete ones";
};

done_testing();
Loading

0 comments on commit d302f8c

Please sign in to comment.