diff --git a/README b/README index c09c8e7..a44f0b8 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ ###################################################################### - DNS::ZoneParse 1.10 + DNS::ZoneParse 1.11 ###################################################################### NAME @@ -74,7 +74,8 @@ DESCRIPTION If you plan to pass a on_unparseable_line callback but do not wish to specify an $origin, pass 'undef' as the $origin parameter. - a(), cname(), srv(), mx(), ns(), ptr(), txt(), hinfo(), rp(), loc() + a(), cname(), srv(), mx(), ns(), ptr(), txt(), spf(), hinfo(), rp(), + loc() These methods return references to the resource records. For example: @@ -92,6 +93,9 @@ DESCRIPTION TXT records also have a 'text' property representing the record's 'txt-data' descriptive text. + SPF records also have a 'spf' property representing the record's + 'spf-data' descriptive text. + HINFO records also have 'cpu' and 'os' properties. RP records also have 'mbox' and 'text' properties. diff --git a/lib/DNS/ZoneParse.pm b/lib/DNS/ZoneParse.pm index dc15cc6..efeefb3 100644 --- a/lib/DNS/ZoneParse.pm +++ b/lib/DNS/ZoneParse.pm @@ -17,16 +17,20 @@ my @ESCAPABLE_CHARACTERS = qw/ ; " \\\\ /; my $rr_class = qr/(?:IN|HS|CH)/i; my $rr_ttl = qr/(?:\d+[wdhms]?)+/i; -$VERSION = '1.10'; +$VERSION = '1.11'; my ( - %dns_id, %dns_soa, %dns_ns, %dns_a, %dns_cname, %dns_mx, %dns_txt, + %dns_id, %dns_soa, %dns_ns, %dns_a, %dns_cname, %dns_mx, + %dns_txt, %dns_spf, %dns_ptr, %dns_a4, %dns_srv, %dns_hinfo, %dns_rp, %dns_loc, %dns_generate, %dns_last_name, %dns_last_origin, %dns_last_class, %dns_last_ttl, %dns_found_origins, %unparseable_line_callback, %last_parse_error_count, ); -my %possibly_quoted = map { $_ => undef } qw/ os cpu text mbox /; +my %possibly_quoted = map { $_ => undef } qw/ os cpu text mbox spf /; + +# https://kb.isc.org/article/AA-00356/0/Can-I-have-a-TXT-or-SPF-record-longer-than-255-characters.html +my %possibly_multiple = map { $_ => undef } qw/ text spf /; sub new { my $class = shift; @@ -71,6 +75,7 @@ sub DESTROY { delete $dns_cname{$self}; delete $dns_mx{$self}; delete $dns_txt{$self}; + delete $dns_spf{$self}; delete $dns_ptr{$self}; delete $dns_a4{$self}; delete $dns_srv{$self}; @@ -99,6 +104,7 @@ sub AUTOLOAD { : $method eq 'cname' ? $dns_cname{$self} : $method eq 'mx' ? $dns_mx{$self} : $method eq 'txt' ? $dns_txt{$self} + : $method eq 'spf' ? $dns_spf{$self} : $method eq 'ptr' ? $dns_ptr{$self} : $method eq 'aaaa' ? $dns_a4{$self} : $method eq 'srv' ? $dns_srv{$self} @@ -130,6 +136,7 @@ sub dump { MX => $dns_mx{$self}, PTR => $dns_ptr{$self}, TXT => $dns_txt{$self}, + SPF => $dns_spf{$self}, SRV => $dns_srv{$self}, HINFO => $dns_hinfo{$self}, RP => $dns_rp{$self}, @@ -235,7 +242,21 @@ ZONEHEADER2 next unless defined $o; next unless $o->{'ORIGIN'} eq $process_this_origin; $self->_escape_chars( $o ); - $output .= qq[$o->{name} $o->{ttl} $o->{class} TXT "$o->{text}"\n]; + my $t = $o->{text}; + if ( length($t) > 250 ) { + $t=join('" "',( $t =~ m/.{1,250}/g )); + } + $output .= qq[$o->{name} $o->{ttl} $o->{class} TXT "$t"\n]; + } + foreach my $o ( @{ $dns_spf{$self} } ) { + next unless defined $o; + next unless $o->{'ORIGIN'} eq $process_this_origin; + $self->_escape_chars( $o ); + my $t = $o->{spf}; + if ( length($t) > 250 ) { + $t=join('" "',( $t =~ m/.{1,250}/g )); + } + $output .= qq[$o->{name} $o->{ttl} $o->{class} SPF "$t"\n]; } foreach my $o ( @{ $dns_ptr{$self} } ) { next unless defined $o; @@ -361,6 +382,7 @@ sub _initialize { $dns_cname{$self} = []; $dns_mx{$self} = []; $dns_txt{$self} = []; + $dns_spf{$self} = []; $dns_ptr{$self} = []; $dns_a4{$self} = []; $dns_srv{$self} = []; @@ -613,6 +635,20 @@ sub _parse { class => $3, text => $4, } ); + } elsif ( + /($valid_name)? \s+ + $ttl_cls + SPF \s+ + ("$valid_quoted_txt_char*(?_massage( { + name => $1, + ttl => $2, + class => $3, + spf => $4, + } ); } elsif ( /^\s*\$TTL \s+ ($rr_ttl) @@ -817,9 +853,12 @@ sub _massage { $record->{$r} = ''; next; } - if ( exists $possibly_quoted{$r} ) { + if ( exists $possibly_quoted{$r} ) { + if ( exists $possibly_multiple{$r} ) { + $record->{$r} =~ s/"\s+"//; + } ( $record->{$r} =~ s/^"// ) && ( $record->{$r} =~ s/"$// ); - } + } # We return email addresses just as they are in the file... for better # or worse (mostly for backwards compatability reasons). @@ -1051,7 +1090,7 @@ how errors are handled when parsing zone files. If you plan to pass a on_unparseable_line callback but do not wish to specify an C<$origin>, pass 'undef' as the C<$origin> parameter. -=item a(), cname(), srv(), mx(), ns(), ptr(), txt(), hinfo(), rp(), loc() +=item a(), cname(), srv(), mx(), ns(), ptr(), txt(), spf(), hinfo(), rp(), loc() These methods return references to the resource records. For example: @@ -1069,6 +1108,9 @@ SRV records also have 'priority', 'weight' and 'port' properties. TXT records also have a 'text' property representing the record's 'txt-data' descriptive text. +SPF records also have a 'spf' property representing the record's 'spf-data' +descriptive text. + HINFO records also have 'cpu' and 'os' properties. RP records also have 'mbox' and 'text' properties.