Skip Menu |

This queue is for tickets about the DNS-ZoneParse CPAN distribution.

Report information
The Basics
Id: 7363
Status: resolved
Priority: 0/
Queue: DNS-ZoneParse

People
Owner: Nobody in particular
Requestors: mv [...] pdv-systeme.de
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.91
Fixed in: (no value)



Subject: Wrong parsing of defaulted names & general comment
1. @ IN SOA ... 86400 IN NS ns... gets parsed wrong (name=>86400,ttl=>''). 2. 253 IN PTR x... IN PTR y... IN PTR z... The second and all following RR get thrown away because no regexp matches a PTR RR where the name is defaulted. 3. If the header doesn't have the format DNS::ZoneParse expects, there's no origin. RFC 1035 states in 5.1 that "the actual domain name is the concatenation of the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as an argument to the master file loading routine.". _load_file() and _parse() could be extended to accept an optional $origin parameter. General comments: I would modify the line my @records = map {s/^\s+//g; $_} split (m|$/|, $zone); in _clean_records() to not throw away the information that a RR starts with a <blank> (i.e. the "name" field is defaulted), and modify the regular expressions in _parse() and _massage() to react accordingly to a <blank>. #1 above and the ambiguity in PTR RRs with respect to the TTL would thus be resolved. Compressing all consecutive whitespace to one blank would simplify the regexps. The module should react to a RR line it can't parse, i.e. there should be an "else" branch in _parse() that contains at least a call to carp(). Thanks for listening.
From: mv [...] pdv-systeme.de
[guest - Mon Aug 16 09:09:59 2004]: Show quoted text
> ...
The attached patch resolves all these issues. Best regards, Martin
diff -urdN DNS-ZoneParse-0.91-orig/lib/DNS/ZoneParse.pm DNS-ZoneParse-0.91/lib/DNS/ZoneParse.pm --- DNS-ZoneParse-0.91-orig/lib/DNS/ZoneParse.pm 2003-08-03 16:28:24.000000000 +0200 +++ DNS-ZoneParse-0.91/lib/DNS/ZoneParse.pm 2004-08-17 14:24:26.000000000 +0200 @@ -152,7 +152,7 @@ sub _load_file { - my ($self, $zonefile) = @_; + my ($self, $zonefile, $origin) = @_; my $zone_contents; if(ref($zonefile) eq "SCALAR") { $zone_contents = $$zonefile; @@ -165,73 +165,75 @@ croak qq[DNS::ZoneParse Could not open input file: "$zonefile":$!] } } - if ($self->_parse( $zonefile, $zone_contents )) { return 1; } + if ($self->_parse( $zonefile, $zone_contents, $origin )) { return 1; } } sub _parse { - my ($self, $zonefile, $contents) = @_; + my ($self, $zonefile, $contents, $origin) = @_; $self->_initialize(); my $chars = qr/[a-z\-\.0-9]+/i; $contents =~ /Database file ($chars)( dns)? for ($chars) zone/si; - $dns_id{$self} = _massage({ ZoneFile => $1 || $zonefile, Origin => $3 }); + $dns_id{$self} = _massage({ ZoneFile => $1 || $zonefile, Origin => $3 || $origin }); my $records = $self->_clean_records($contents); my $valid_name = qr/[\@a-z_\-\.0-9\*]+/i; my $valid_ip6 = qr/[\@a-z_\-\.0-9\*:]+/i; - my $rr_class = qr/\b(?:in|hs|ch)\b/i; - my $rr_types = qr/\b(?:ns|a|cname)\b/i; + my $rr_class = qr/\b(?:IN|HS|CH)\b/i; + my $rr_type = qr/\b(?:NS|A|CNAME)\b/i; my $rr_ttl = qr/(?:\d+[wdhms]?)+/i; - my $ttl_cls = qr/(?:($rr_ttl)\s+)?(?:\b($rr_class)\s+)?\s*/; + my $ttl_cls = qr/(?:($rr_ttl)\s)?(?:($rr_class)\s)?/; _massage(); # reset last_name foreach (@$records) { - if (/^($valid_name)? \s* # host - (?:($rr_ttl)\s+)? # ttl - (?:\b($rr_class)\s+)?\s* # class - ($rr_types) \s+ # record type + if (/^($valid_name|\s)? \s* # host + $ttl_cls # ttl & class + ($rr_type) \s # record type ($valid_name) # record data /ix) { my ($name, $ttl, $class, $type, $host) = ($1, $2, $3, $4, $5); - if (!$class && defined $name && $name =~ /^$rr_class$/) { - $class = uc $name; - undef $name; - } my $dns_thing = uc $type eq 'NS' ? $dns_ns{$self} : uc $type eq 'A' ? $dns_a{$self} : $dns_cname{$self}; push @$dns_thing, _massage({name => $name, class=> $class, host => $host, ttl => $ttl}) } - elsif (/($valid_name)? \s* $ttl_cls AAAA \s+ ($valid_ip6)/) - { + elsif (/^($valid_name|\s)? \s* + $ttl_cls + AAAA \s + ($valid_ip6) + /x) { my ($name, $ttl, $class, $host) = ($1, $2, $3, $4); - if (!$class && defined $name && $name =~ /^$rr_class$/) { - $class = uc $name; - undef $name; - } push @{$dns_a4{$self}}, _massage({name => $name, class=> $class, host => $host, ttl => $ttl}) } - elsif (/($valid_name)? \s* $ttl_cls MX \s+ (\d+) \s+ ($valid_name)/ix) - { + elsif (/^($valid_name|\s)? \s* + $ttl_cls + MX \s + (\d+) \s + ($valid_name) + /ix) { # host ttl class mx pri dest my ($name, $ttl, $class, $pri, $host) = ($1, $2, $3, $4, $5); - if (!$class && defined $name && $name =~ /^$rr_class$/) { - $class = uc $name; - undef $name; - } push @{$dns_mx{$self}}, _massage({ name => $name, priority => $pri, host => $host, ttl => $ttl, class => $class}) } - elsif (/($valid_name) \s+ $ttl_cls - SOA \s+ ($valid_name) \s+ ($valid_name) \s* - \(?\s* ($rr_ttl) \s+ ($rr_ttl) \s+ ($rr_ttl) \s+ - ($rr_ttl) \s+ ($rr_ttl) \s* \)? /ix) - { + elsif (/^($valid_name) \s + $ttl_cls + SOA \s + ($valid_name) \s + ($valid_name) \s* + \(? \s* + ($rr_ttl) \s + ($rr_ttl) \s + ($rr_ttl) \s + ($rr_ttl) \s + ($rr_ttl) \s* + \)? + /ix) { # SOA record my $ttl = $dns_soa{$self}->{ttl} || $2 || ''; $dns_soa{$self} = @@ -239,8 +241,11 @@ email =>$5, serial => $6, refresh=> $7, retry=> $8, expire=> $9, minimumTTL => $10 }); } - elsif (/(\d$valid_name+)\s+($rr_ttl)?\s*?($rr_class)?\s*?PTR\s+($valid_name)/i) - { + elsif (/^($valid_name|\s) \s* + $ttl_cls + PTR \s + ($valid_name) + /ix) { # PTR push @{$dns_ptr{$self}}, _massage({ name => $1, class => $3, ttl => $2, host => $4 }); @@ -253,6 +258,9 @@ elsif (/\$TTL\s+($rr_ttl)/i) { $dns_soa{$self}->{ttl} = $1; } + else { + carp "Unparseable line\n $_\n"; + } } return 1; } @@ -261,21 +269,23 @@ my $self = shift; my ($zone) = shift; - $zone =~ s<\;.{0,}$><>mg; # Remove comments - $zone =~ s<^\s*?$><>mg; # Remove empty lines - $zone =~ s!$/{2,}!$/!g; # Remove double carriage returns + $zone =~ s<\;.*$> <>mg; # Remove comments + $zone =~ s<^\s*$> <>mg; # Remove empty lines + $zone =~ s<$/+> <$/>g; # Remove multiple carriage returns + $zone =~ s<[ \t]+>< >g; # Collapse whitespace, turn TABs to spaces # Concatenate everything split over multiple lines i.e. elements surrounded # by parentheses can be split over multiple lines. See RFC 1035 section 5.1 $zone =~ s{(\([^\)]*?\))}{_concatenate($1)}egs; - my @records = map {s/^\s+//g; $_} split (m|$/|, $zone); + # Split into multiple records, and kick out empty lines + my @records = grep !/^$/, split (m|$/|, $zone); return \@records; } sub _concatenate { my $text_in_parenth= shift; - $text_in_parenth=~ s{$/}{}g; + $text_in_parenth=~ s{\s*$/\s*}{ }g; return $text_in_parenth; } @@ -288,7 +298,7 @@ $record->{$_} = uc $record->{$_} if $_ eq 'class'; } return $record unless defined $record->{name}; - if (length $record->{name}) { + if (length $record->{name} && $record->{name} ne ' ') { $last_name = $record->{name}; } else { $record->{name} = $last_name; diff -urdN DNS-ZoneParse-0.91-orig/t/dns-zoneparse.t DNS-ZoneParse-0.91/t/dns-zoneparse.t --- DNS-ZoneParse-0.91-orig/t/dns-zoneparse.t 2003-08-03 16:29:02.000000000 +0200 +++ DNS-ZoneParse-0.91/t/dns-zoneparse.t 2004-08-17 11:57:56.000000000 +0200 @@ -64,19 +64,22 @@ 'ttl' => '', 'name' => 'www', 'class' => 'IN', 'host' => '10.0.0.2' }, { - 'ttl' => '', 'name' => 'www', 'class' => '', 'host' => '10.0.0.4' + 'ttl' => '43200', 'name' => 'www', 'class' => 'IN', 'host' => '10.0.0.3' }, { - 'ttl' => '', 'name' => 'foo', 'class' => 'IN', 'host' => '10.0.0.5' + 'ttl' => '', 'name' => 'www', 'class' => '', 'host' => '10.0.0.5' + }, + { + 'ttl' => '', 'name' => 'foo', 'class' => 'IN', 'host' => '10.0.0.6' }, { - 'ttl' => '', 'name' => 'mini', 'class' => '', 'host' => '10.0.0.6' + 'ttl' => '', 'name' => 'mini', 'class' => '', 'host' => '10.0.0.7' }, ], 'A records parsed OK'); is_deeply($zf->ns, [ { - 'ttl' => '', + 'ttl' => '43200', 'name' => '@', 'class' => 'IN', 'host' => 'ns0.dns-zoneparse-test.net.' @@ -102,7 +105,7 @@ 'ttl' => '', 'name' => 'www', 'class' => 'IN', - 'host' => '10.0.0.3' + 'host' => '10.0.0.4' }, ], 'MX records parsed OK'); diff -urdN DNS-ZoneParse-0.91-orig/t/test-zone.db DNS-ZoneParse-0.91/t/test-zone.db --- DNS-ZoneParse-0.91-orig/t/test-zone.db 2003-08-03 16:27:04.000000000 +0200 +++ DNS-ZoneParse-0.91/t/test-zone.db 2004-08-17 11:57:56.000000000 +0200 @@ -8,7 +8,7 @@ 691200 ; expire 86400 ) ; minimum TTL -@ IN NS ns0.dns-zoneparse-test.net. + 43200 IN NS ns0.dns-zoneparse-test.net. @ IN NS ns1.dns-zoneparse-test.net. @ IN A 127.0.0.1 @@ -18,10 +18,11 @@ mail IN A 127.0.0.1 www IN A 127.0.0.1 in a 10.0.0.2 - IN MX 10 10.0.0.3 - A 10.0.0.4 -foo IN A 10.0.0.5 -mini A 10.0.0.6 + 43200 IN A 10.0.0.3 + IN MX 10 10.0.0.4 + A 10.0.0.5 +foo IN A 10.0.0.6 +mini A 10.0.0.7 icarus IN AAAA fe80::0260:83ff:fe7c:3a2a soup IN TXT "This is a text message" txta TXT "This is another text message"
Patch applied. v0.92 that has just been uploaded to CPAN. Thank you Simon Flack