zonemaster / zonemaster-ldns

A Perl interface module for Zonemaster to NLnet Labs' ldns library
Other
8 stars 12 forks source link

No easy access to raw text in Zonemaster::LDNS::RR::TXT objects #155

Closed marc-vanderwal closed 1 year ago

marc-vanderwal commented 2 years ago

I am trying to get access to the data within TXT resource records, especially for SPF and DMARC.

TXT resource records can be challenging because the text data can be broken up in several segments of up to 255 bytes. However, the documentation of the txtdata() method in the Zonemaster::LDNS::RR::TXT package does not mention at all that it only returns the text found in the first segment, and that it does so in presentation format. Both of these facts were rather unexpected to me.

In fact, TXT records for SPF and DMARC policies should be parsed as if all segments were concatenated together without spaces.

Here’s some code that illustrates the surprising behavior of the txtdata() method and something I wish I had:

#!/usr/bin/env perl

use strict;
use warnings;
use v5.010;

use Test::More;

use Zonemaster::LDNS;

my $data = q{
    txt.test. 3600 IN TXT "Handling TXT RRs can be challenging"
    txt.test. 3600 IN TXT "because " "the data can " "be spl" "it up like " "this!"
};
my @lines = map { s/^\s+|\s+$//r } (grep /TXT/, (split /\n/, $data));
my @rrs = map { Zonemaster::LDNS::RR->new($_) } @lines;

foreach my $rr ( @rrs ) {
    isa_ok( $rr, 'Zonemaster::LDNS::RR::TXT' );
}

is( $rrs[0]->txtdata(), q{"Handling TXT RRs can be challenging"} );

is( $rrs[1]->txtdata(), q{"because "} );

TODO: {
    local $TODO = "txtdata() on second RR has surprising behavior";

    is( $rrs[1]->txtdata(), q{"because " "the data can " "be spl" "it up like " "this!"} );
}

TODO: {
    todo_skip "A method I wish I had", 2;

    is( $rrs[0]->txt_concat(), q{Handling TXT RRs can be challenging} );
    is( $rrs[1]->txt_concat(), q{because the data can be split up like this!} );
}

done_testing();

(Running this file yields the following output.)

ok 1 - An object of class 'Zonemaster::LDNS::RR::TXT' isa 'Zonemaster::LDNS::RR::TXT'
ok 2 - An object of class 'Zonemaster::LDNS::RR::TXT' isa 'Zonemaster::LDNS::RR::TXT'
ok 3
ok 4
not ok 5 # TODO txtdata() on second RR has surprising behavior
#   Failed (TODO) test at test.pl line 29.
#          got: '"because "'
#     expected: '"because " "the data can " "be spl" "it up like " "this!"'
not ok 6 # TODO & SKIP A method I wish I had
not ok 7 # TODO & SKIP A method I wish I had
1..7