Net-DNS-ZoneFile-Fast-1.24/0000755000237200023720000000000012402370347015344 5ustar hardakerhardakerNet-DNS-ZoneFile-Fast-1.24/t/0000755000237200023720000000000012402370347015607 5ustar hardakerhardakerNet-DNS-ZoneFile-Fast-1.24/t/zone.t0000644000237200023720000001052612402367562016760 0ustar hardakerhardaker# This is -*- perl -*- use Net::DNS; use Net::DNS::ZoneFile::Fast; my $zone = <<'EOF'; $ORIGIN 2.1.in-addr.arpa. $TTL 600 3 IN SOA dns1.acme.com. hostmaster.acme.com. ( ; The host that should give auth answers ; The one who cares when it doesn't ; this comment is a pain... 1000 ; The serial number 180 ; The refresh interval 60 ; The retry interval 1800 ; The expire interval 1000 ; The minimum TTL ) IN NS dns1.acme.com. IN NS dns2.acme.com. 1.3 600 IN PTR host1.acme.com. 2.3 100 IN PTR host2.acme.com. 3.3 50 PTR host3.acme.com. 4.3 PTR dns1.acme.com. 5.3 1800 IN PTR dns2.acme.com. $ORIGIN acme.com. @ IN SOA dns1.acme.com. hostmaster.acme.com. ( ; The host that should give auth answers ; The one who cares when it doesn't 1000 ; The serial number 180 ; The refresh interval 60 ; The retry interval 1800 ; The expire interval 1000 ; The minimum TTL ) 3600 IN NS dns1.acme.com. IN NS dns2.acme.com. NS dns3.acme.com. IN MX 10 mail1.acme.com. 3600 MX 20 mail2.acme.com. MX 30 coyote.acme.com. RP postmaster @ dns1 1000 IN A 1.2.3.4 dns2.acme.com. 1000 IN A 1.2.3.5 @ 10 IN CNAME host1.acme.com. . IN A 1.2.3.1 host1 IN A 1.2.3.1 IN TXT "This is the first host" RP tobez.tobez.org. host1 coyote IN CNAME @ ; some comments to make life interesting EOF BEGIN { @rr = ( [ Net::DNS::RR->new("3.2.1.in-addr.arpa. 600 IN SOA dns1.acme.com. hostmaster.acme.com. 1000 180 60 1800 1000")->string, "IN-ADDR.ARPA SOA", 3 ], [ Net::DNS::RR->new("3.2.1.in-addr.arpa. 600 IN NS dns1.acme.com.")->string, "First NS RR", 12 ], [ Net::DNS::RR->new("3.2.1.in-addr.arpa. 600 IN NS dns2.acme.com.")->string, "Second NS RR", 13 ], [ Net::DNS::RR->new("1.3.2.1.in-addr.arpa. 600 IN PTR host1.acme.com.")->string, "PTR RR with default TTL", 15 ], [ Net::DNS::RR->new("2.3.2.1.in-addr.arpa. 100 IN PTR host2.acme.com.")->string, "PTR RR with explicit TTL", 16 ], [ Net::DNS::RR->new("3.3.2.1.in-addr.arpa. 50 PTR host3.acme.com.")->string, "PTR RR with no class", 17 ], [ Net::DNS::RR->new("4.3.2.1.in-addr.arpa. 600 PTR dns1.acme.com.")->string, "PTR RR with no class and default TTL", 18 ], [ Net::DNS::RR->new("5.3.2.1.in-addr.arpa. 1800 IN PTR dns2.acme.com.")->string, "Plan PTR RR", 19 ], [ Net::DNS::RR->new("acme.com. 600 IN SOA dns1.acme.com. hostmaster.acme.com. 1000 180 60 1800 1000")->string, "acme.com. SOA", 22 ], [ Net::DNS::RR->new("acme.com. 3600 IN NS dns1.acme.com.")->string, "First NS RR", 29 ], [ Net::DNS::RR->new("acme.com. 600 IN NS dns2.acme.com.")->string, "Second NS RR", 30 ], [ Net::DNS::RR->new("acme.com. 600 IN NS dns3.acme.com.")->string, "Third NS RR", 31 ], [ Net::DNS::RR->new("acme.com. 600 IN MX 10 mail1.acme.com.")->string, "Innocent MX", 33 ], [ Net::DNS::RR->new("acme.com. 3600 IN MX 20 mail2.acme.com.")->string, "MX with TTL", 34 ], [ Net::DNS::RR->new("acme.com. 600 IN MX 30 coyote.acme.com.")->string, "Compact MX", 35 ], [ Net::DNS::RR->new("acme.com. 600 IN RP postmaster.acme.com. acme.com.")->string, "RP with implicit mbox and \@-txtdname", 36 ], [ Net::DNS::RR->new("dns1.acme.com. 1000 IN A 1.2.3.4")->string, "Simple A RR", 38 ], [ Net::DNS::RR->new("dns2.acme.com. 1000 IN A 1.2.3.5")->string, "FQDN A RR", 39 ], [ Net::DNS::RR->new("acme.com. 10 IN CNAME host1.acme.com.")->string, "\@ CNAME", 40 ], [ Net::DNS::RR->new(". 600 IN A 1.2.3.1")->string, "A RR for the root domain (invalid anyway)", 41 ], [ Net::DNS::RR->new("host1.acme.com. 600 IN A 1.2.3.1")->string, "Simple A RR", 42 ], [ Net::DNS::RR->new("host1.acme.com. 600 IN TXT \"This is the first host\"")->string, "dangling TXT RR", 43 ], [ Net::DNS::RR->new("host1.acme.com. 600 IN RP tobez.tobez.org. host1.acme.com.")->string, "RP with implicit txtdname", 44 ], [ Net::DNS::RR->new("coyote.acme.com. 600 IN CNAME acme.com.")->string, "@ on the RHS", 46 ], ); }; use Test::More tests => (1 + 2*@rr); my $rrset = Net::DNS::ZoneFile::Fast::parse($zone); ok(defined $rrset, "Parsing zone file"); for my $rr (@rr) { my $trr = shift @$rrset; is($trr->string, $rr->[0], $rr->[1]); is($trr->Line, $rr->[2], "$rr->[1] - line number"); } Net-DNS-ZoneFile-Fast-1.24/t/origin.t0000644000237200000120000000222310672540723016576 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 9; ok(defined Net::DNS::ZoneFile::Fast::parse(q{}), 'Empty zone'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$ORIGIN acme.com.}), 'Simple $ORIGIN clause'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$ORIGIN acme.com. ; comment}), 'Simple $ORIGIN clause with comments'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$ORIGIN acme.com}), 'Simple $ORIGIN clause with no trailing dot'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$ORIGIN acme.com ; comment}), 'Simple $ORIGIN clause with comments and no dot'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$ORIGIN . ; comment}), 'Simple $ORIGIN clause with comments and just a dot'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$ORIGIN .}), 'Simple $ORIGIN clause with just a dot'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => q{$ORIGIN}, quiet => 1, soft_errors => 1), '$ORIGIN token alone in the file'); my $rr = Net::DNS::ZoneFile::Fast::parse("\$ORIGIN bork.\n\$ORIGIN moo.bla\nmoof A 1.2.3.4"); ok(defined($rr) && 1 == @$rr && $rr->[0]->name eq "moof.moo.bla.bork", 'Relative origin'); Net-DNS-ZoneFile-Fast-1.24/t/ttl.t0000644000237200000120000000435711072715111016112 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 24; ok(defined Net::DNS::ZoneFile::Fast::parse(q{$TTL 30}), 'Simple $TTL clause'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{$TTL 30 ; comment}), 'Simple $TTL clause with comments'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "\$TTL\n30", quiet => 1, soft_errors => 1), '$TTL clause spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => q{$TTL}, quiet => 1, soft_errors => 1), '$TTL token alone in the file'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => q{$TTL 1C3F}, quiet => 1, soft_errors => 1), '$TTL expressed in cats per forthnight'); my $p; ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 1800\na.b. A 1.2.3.4")), '$TTL as a number'); is($p->[0]->ttl, 1800, "TTL == 1800 seconds"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 1S\na.b. A 1.2.3.4")), '$TTL with seconds'); is($p->[0]->ttl, 1, "TTL == 1 second"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 1M\na.b. A 1.2.3.4")), '$TTL with minutes'); is($p->[0]->ttl, 60, "TTL == 1 minute"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 1H\na.b. A 1.2.3.4")), '$TTL with hours'); is($p->[0]->ttl, 3600, "TTL == 1 hour"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 2H3h\na.b. A 1.2.3.4")), '$TTL with some hours'); is($p->[0]->ttl, 5*3600, "TTL == 5 hours"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 1D\na.b. A 1.2.3.4")), '$TTL with days'); is($p->[0]->ttl, 86400, "TTL == 1 day"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 1W\na.b. A 1.2.3.4")), '$TTL with weeks'); is($p->[0]->ttl, 604800, "TTL == 1 week"); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 2W3D4H30M45S\na.b. A 1.2.3.4")), '$TTL with weeks, days, hours, minutes, and seconds [1]'); is($p->[0]->ttl, 1485045, "TTL == something [1]"); ok(!defined($p = Net::DNS::ZoneFile::Fast::parse(text => "\$TTL 2W3D4H30M45x\na.b. A 1.2.3.4", quiet => 1, soft_errors => 1)), '$TTL with weeks, days, hours, minutes, and seconds [2]'); ok(defined($p = Net::DNS::ZoneFile::Fast::parse("\$TTL 30M45S2W\na.b. A 1.2.3.4")), '$TTL with minutes, seconds, and weeks'); is($p->[0]->ttl, 1211445, "TTL == 2w30m45s"); Net-DNS-ZoneFile-Fast-1.24/t/generate.t0000644000237200000120000000173610672540723017111 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 7 ; my $data = q{ $GENERATE 1-5 $.1.1.1.in-addr.arpa. IN PTR host-$.acme.com. }; my @rr = ( [ Net::DNS::RR->new("1.1.1.1.in-addr.arpa. IN PTR host-1.acme.com.")->string, "First RR of a \$GENERATE" ], [ Net::DNS::RR->new("2.1.1.1.in-addr.arpa. IN PTR host-2.acme.com.")->string, "Second RR of a \$GENERATE" ], [ Net::DNS::RR->new("3.1.1.1.in-addr.arpa. IN PTR host-3.acme.com.")->string, "Third RR of a \$GENERATE" ], [ Net::DNS::RR->new("4.1.1.1.in-addr.arpa. IN PTR host-4.acme.com.")->string, "Fourth RR of a \$GENERATE" ], [ Net::DNS::RR->new("5.1.1.1.in-addr.arpa. IN PTR host-5.acme.com.")->string, "Last RR of a \$GENERATE" ], ); my $rrset = Net::DNS::ZoneFile::Fast::parse($data); ok(defined $rrset, "Parsing of a \$GENERATE statement"); is(scalar @$rrset, scalar @rr, "Number of parsed RRs"); for my $rr (@rr) { my $rrt = shift @$rrset; is($rrt->string, $rr->[0], $rr->[1]); } Net-DNS-ZoneFile-Fast-1.24/t/00-load.t0000644000237200000120000000123510672540723016445 0ustar hardakerwheel# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..1\n"; } END {print "not ok 1\n" unless $loaded;} use Net::DNS::ZoneFile::Fast; $loaded = 1; print "ok 1\n"; ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): Net-DNS-ZoneFile-Fast-1.24/t/soattl.t0000644000237200000120000000156510672540723016625 0ustar hardakerwheel# This is -*- perl -*- use IO::File; use Net::DNS; use Net::DNS::RR; use Net::DNS::ZoneFile::Fast; use Test::More tests => 5; END { unlink "./read.txt"; } my $zone = q{a.com. 30 IN SOA dns1.a.com. hostmaster.a.com. (1 1 1 1 1)}; ok(defined Net::DNS::ZoneFile::Fast::parse($zone), "parse of the test zone"); my $fh = new IO::File "./read.txt", "w" or die "# Failed to create test file\n"; print $fh $zone; $fh->close; $fh = new IO::File "./read.txt" or die "# Failed to open test file\n"; ok(defined Net::DNS::ZoneFile::Fast::parse(fh => $fh), 'readfh'); $fh->close; ok(defined Net::DNS::ZoneFile::Fast::parse(file => "./read.txt"), 'read'); my $rrset = Net::DNS::ZoneFile::Fast::parse(file => "./read.txt"); ok(defined $rrset, 're-read'); die unless defined $rrset; my $rr = new Net::DNS::RR $zone; ok($rr->string eq $rrset->[0]->string, "RR comparison"); Net-DNS-ZoneFile-Fast-1.24/t/rr-multi-a.t0000644000237200000120000000211410672540723017277 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 6; $zone = q{ $ORIGIN acme.com. www 926 IN A 10.10.10.10 925 IN A 11.11.11.11 }; $crr = Net::DNS::RR->new("www.acme.com 925 IN A 11.11.11.11")->string; my $rrset = Net::DNS::ZoneFile::Fast::parse($zone); is (scalar @$rrset, 2, "Correct number of RRs in zone"); is ($rrset->[1]->string, $crr, "Correct (empty) RR parsed"); $zone = q{ $ORIGIN acme.com. www 926 IN A 10.10.10.10 www 925 IN A 11.11.11.11 }; $crr = Net::DNS::RR->new("www.acme.com 925 IN A 11.11.11.11")->string; $rrset = Net::DNS::ZoneFile::Fast::parse($zone); is (scalar @$rrset, 2, "Correct number of RRs in zone"); is ($rrset->[1]->string, $crr, "Correct (partial) RR parsed"); $zone = q{ $ORIGIN acme.com. www 926 IN A 10.10.10.10 www.acme.com. 925 IN A 11.11.11.11 }; $crr = Net::DNS::RR->new("www.acme.com 925 IN A 11.11.11.11")->string; $rrset = Net::DNS::ZoneFile::Fast::parse($zone); is (scalar @$rrset, 2, "Correct number of RRs in zone"); is ($rrset->[1]->string, $crr, "Correct (fqdn) RR parsed"); Net-DNS-ZoneFile-Fast-1.24/t/newlines.t0000644000237200000120000000615711251275266017146 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 18; ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "\$ORIGIN\nmicrosoft.com.", quiet => 1, soft_errors => 1), '$ORIGIN clause spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "\$TTL\n30", quiet => 1, soft_errors => 1), '$TTL clause spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "\$GENERATE\n1-5 \$.1.1.1.in-addr.arpa. IN PTR host-\$.acme.com.", quiet => 1, soft_errors => 1), '$GENERATE clause spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. 30 IN SOA dns1.a.com. hostmaster.a.com.\n(1 1 1 1 1)", quiet => 1, soft_errors => 1), 'SOA record spanning multiple lines in a wrong way 1'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com.\n30 IN SOA dns1.a.com. hostmaster.a.com. (1 1 1 1 1)", quiet => 1, soft_errors => 1), 'SOA record spanning multiple lines in a wrong way 2'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. 30\nIN SOA dns1.a.com. hostmaster.a.com. (1 1 1 1 1)", quiet => 1, soft_errors => 1), 'SOA record spanning multiple lines in a wrong way 3'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. 30 IN\nSOA dns1.a.com. hostmaster.a.com. (1 1 1 1 1)", quiet => 1, soft_errors => 1), 'SOA record spanning multiple lines in a wrong way 4'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. 30 IN SOA\ndns1.a.com. hostmaster.a.com. (1 1 1 1 1)", quiet => 1, soft_errors => 1), 'SOA record spanning multiple lines in a wrong way 5'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. 30 IN SOA dns1.a.com.\nhostmaster.a.com. (1 1 1 1 1)", quiet => 1, soft_errors => 1), 'SOA record spanning multiple lines in a wrong way 6'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "4.3.2.1.in-addr.arpa. PTR\nbla.com.", quiet => 1, soft_errors => 1), 'PTR record spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. NS\nns.a.com.", quiet => 1, soft_errors => 1), 'NS record spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. CNAME\nb.com.", quiet => 1, soft_errors => 1), 'CNAME record spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. MX 0\nmail.a.com.", quiet => 1, soft_errors => 1), 'MX record spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. TXT\n\"hello, world\"", quiet => 1, soft_errors => 1), 'TXT record spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. HINFO Small-Laptop\nFreeBSD", quiet => 1, soft_errors => 1), 'HINFO record spanning multiple lines'); ok(defined Net::DNS::ZoneFile::Fast::parse("a.com. AAAA 3ffe:8050:201:1860:42::1"), 'normal AAAA record'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. AAAA\n3ffe:8050:201:1860:42::1", quiet => 1, soft_errors => 1), 'AAAA record spanning multiple lines'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "a.com. A\n1.2.3.4", quiet => 1, soft_errors => 1), 'A record spanning multiple lines'); Net-DNS-ZoneFile-Fast-1.24/t/rrs-ws.t0000644000237200000120000000426110672540723016550 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; BEGIN { @test = ( q{. 300 IN A 127.0.0.1}, q{localhost. 300 IN A 127.0.0.1}, q{localhost IN A 127.0.0.1}, q{localhost.acme IN A 127.0.0.1}, q{localhost A 127.0.0.1}, q{localhost. 300 A 127.0.0.1}, q{10.10.10.10.in-addr.arpa 300 IN PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa. 300 IN PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa. 300 PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa. IN PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa PTR www.acme.com.}, q{. 3600 IN NS dns1.acme.com.}, q{acme.com. 3600 IN NS dns1.acme.com.}, q{@ 3600 IN NS dns1.acme.com.}, q{acme.com. 100 IN CNAME www.acme.com.}, q{acme.com 100 IN CNAME www.acme.com.}, q{text.acme.com. 100 IN TXT "This is a quite long text"}, q{text.acme.com IN TXT "This is another piece"}, q{text.acme.com TXT "This is another piece"}, q{* 100 IN MX 10 mailhost.acme.com.}, q{* IN A 1.2.3.4}, q{* 10 IN A 1.2.3.4}, # q{* IN 10 A 1.2.3.4}, XXX newer Net::DNS does not like this syntax q{acme.com. 200 IN MX 10 mailhost.acme.com.}, q{acme.com. IN MX 10 mailhost.acme.com.}, q{acme.com. MX 10 mailhost.acme.com.}, q{acme.com 200 IN MX 10 mailhost.acme.com.}, q{acme.com IN MX 10 mailhost.acme.com.}, q{acme.com MX 10 mailhost.acme.com.}, q{acme.com. IN SOA dns1.acme.com. me.acme.com. ( 1 2 3 4 5 )}, q{. IN SOA dns1.acme.com. hostmaster.acme.com. ( 1 1 1 1 1 )}, q{@ IN SOA dns1.acme.com. hostmaster.acme.com. ( 1 1 1 1 1 )}, q{. IN SOA dns1.acme.com. hostmaster.acme.com. ( 1 1 1 1 1 )}, q{acme.com. IN AAAA 2001:688:0:102::1:2}, q{acme.com. IN AAAA 2001:688:0:102::3}, q{acme.com. IN RP abuse.acme.com. acme.com.}, ); } use Test::More tests => 2 * scalar @test; for my $rrdata (@test) { my $test = "\n \n" . $rrdata . "\n\n\n \n"; my $rrset = Net::DNS::ZoneFile::Fast::parse($test); (my $adata = $rrdata) =~ s/@/./; $adata =~ s/(IN SOA .* (\d+)) \)/ $2 $1/; my $rr = new Net::DNS::RR $adata; (my $rrcorrect = $rr->string) =~ s/\s+/ /g; ok(defined $rrset, "Parsing $rrdata"); (my $rrtxt = $rrset->[0]->string) =~ s/\s+/ /g; is($rrtxt, $rrcorrect, "RR comparison for $rrdata + whitespace"); } Net-DNS-ZoneFile-Fast-1.24/t/lines.t0000644000237200000120000000140410672540723016421 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 4; my $ln = -1; my %h = ( quiet => 1, on_error => sub { $ln = $_[0]; }, ); my $z1 = < $z1, %h), 'Bad zone parse.'); ok($ln == 6, 'Error must be at line 6.'); ok(!defined Net::DNS::ZoneFile::Fast::parse(text => $z2, %h), 'Bad zone parse.'); ok($ln == 4, 'Error must be at line 4.'); Net-DNS-ZoneFile-Fast-1.24/t/null-rr.t0000644000237200023720000000114412402367562017374 0ustar hardakerhardaker# This is -*- perl -*- use Net::DNS; use Net::DNS::ZoneFile::Fast; use Test::More tests => 4; my $zone = q{ $ORIGIN choicecarecard.com. dddwww IN A 199.93.70.72 IN A 199.93.70.210 }; my $rrset = Net::DNS::ZoneFile::Fast::parse($zone); ok(defined $rrset, "Parsing of the zone file"); ok(@$rrset == 2, "Correct number of records"); ok($rrset->[0]->string eq Net::DNS::RR->new("dddwww.choicecarecard.com. 0 IN A 199.93.70.72")->string, "First dummy RR is ok"); ok($rrset->[1]->string eq Net::DNS::RR->new("dddwww.choicecarecard.com. 0 IN A 199.93.70.210")->string, "Second dummy RR is ok"); Net-DNS-ZoneFile-Fast-1.24/t/read.t0000644000237200000120000000155010672540723016224 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use IO::File; use Test::More tests => 3; END { unlink "./read.txt"; } my $zone = q{ ; This is a real zone, changed to protect the innocent ; $ORIGIN 10.10.10.in-addr.arpa. ; @ 30 IN SOA dns1.acme.com. hostmaster.acme.com. ( 2002040300 ; Serial Number 172800 ; Refresh 48 hours 3600 ; Retry 1 hours 1728000 ; Expire 20 days 172800 ) ; Minimum 48 hours }; ok(defined Net::DNS::ZoneFile::Fast::parse($zone), "parse of the test zone"); my $fh = new IO::File "./read.txt", "w" or die "# Failed to create test file\n"; print $fh $zone; $fh->close; $fh = new IO::File "./read.txt" or die "# Failed to open test file\n"; ok(defined Net::DNS::ZoneFile::Fast::parse(fh => $fh), 'readfh'); $fh->close; ok(defined Net::DNS::ZoneFile::Fast::parse(file => "./read.txt"), 'read'); Net-DNS-ZoneFile-Fast-1.24/t/comment.t0000644000237200000120000000132110672540723016747 0ustar hardakerwheel# This is -*- perl -*- use Net::DNS::ZoneFile::Fast; use Test::More tests => 6; ok(defined Net::DNS::ZoneFile::Fast::parse(q{; just a comment}), 'Zone with just a bare comment'); ok(defined Net::DNS::ZoneFile::Fast::parse(q{;}), 'Zone with just an empty comment'); ok(defined Net::DNS::ZoneFile::Fast::parse(qq{;\n}), 'Zone with just an empty comment and a \n'); ok(defined Net::DNS::ZoneFile::Fast::parse(qq{;two\n;things\n}), 'Two comments back to back'); ok(defined Net::DNS::ZoneFile::Fast::parse(qq{; just a comment\n. IN A 127.0.0.1}), 'A comment and an RR'); ok(defined Net::DNS::ZoneFile::Fast::parse(qq{;two\n;things\n. IN A 127.0.0.1}), 'Two comments back to back and an RR'); Net-DNS-ZoneFile-Fast-1.24/t/rrs.t0000644000237200023720000000754212377672372016630 0ustar hardakerhardaker# This is -*- perl -*- use Net::DNS; use Net::DNS::ZoneFile::Fast; BEGIN { @test = ( q{. 300 IN A 127.0.0.1}, q{localhost. 300 IN A 127.0.0.1}, q{localhost IN A 127.0.0.1}, q{localhost A 127.0.0.1}, q{localhost. 300 A 127.0.0.1}, q{*.acme.com. 300 IN MX 10 host.acme.com.}, q{* 300 IN MX 10 host.acme.com.}, q{10.10.10.10.in-addr.arpa 300 IN PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa. 300 IN PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa. 300 PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa. IN PTR www.acme.com.}, q{10.10.10.10.in-addr.arpa PTR www.acme.com.}, q{10.10/10.10.10.in-addr.arpa. IN PTR www.acme.com.}, q{. 3600 IN NS dns1.acme.com.}, q{acme.com. 3600 IN NS dns1.acme.com.}, q{@ 3600 IN NS dns1.acme.com.}, q{acme.com. 100 IN CNAME www.acme.com.}, q{acme.com 100 IN CNAME www.acme.com.}, q{acme.com. 100 IN DNAME example.com.}, q{acme.com 100 IN DNAME example.com.}, q{text.acme.com. 100 IN TXT "This is a quite long text"}, q{text.acme.com IN TXT "This is another piece"}, q{text.acme.com TXT "This is another piece"}, q{text.acme.com. 100 IN SPF "SPF record - contents not checked for SPF validity"}, q{text.acme.com IN SPF "SPF record - contents not checked for SPF validity"}, q{text.acme.com SPF "SPF record - contents not checked for SPF validity"}, q{* 100 IN MX 10 mailhost.acme.com.}, q{* IN A 1.2.3.4}, q{* 10 IN A 1.2.3.4}, # q{* IN 10 A 1.2.3.4}, XXX newer Net::DNS does not like this syntax q{fo\157.acme.com. 10 IN A 1.2.3.4}, q{acme.com. 200 IN MX 10 mailhost.acme.com.}, q{acme.com. 200 IN MX 10 .}, q{acme.com. IN MX 10 mailhost.acme.com.}, q{acme.com. MX 10 mailhost.acme.com.}, q{acme.com 200 IN MX 10 mailhost.acme.com.}, q{acme.com IN MX 10 mailhost.acme.com.}, q{acme.com MX 10 mailhost.acme.com.}, q{acme.com. IN SOA dns1.acme.com. me.acme.com. ( 1 2 3 4 5 )}, q{. IN SOA dns1.acme.com. hostmaster.acme.com. ( 1 1 1 1 1 )}, q{@ IN SOA dns1.acme.com. hostmaster.acme.com. ( 1 1 1 1 1 )}, q{. IN SOA dns1.acme.com. hostmaster.acme.com. ( 1 1 1 1 1 )}, # included te test cpan bug 17745 q{. IN SOA dns1.acme.com. hostmaster.acme.com ( 1 1 1 1 1 )}, q{. IN SOA dns1.acme.com. hostmaster ( 1 1 1 1 1 )}, q{. IN SOA dns1.acme.com hostmaster.acme.com. ( 1 1 1 1 1 )}, q{. IN SOA dns1 hostmaster. ( 1 1 1 1 1 )}, q{. IN SOA @ hostmaster.acme.com. ( 1 1 1 1 1 )}, q{. IN SOA @ hostmaster+foo.acme.com. ( 1 1 1 1 1 )}, q{acme.com. IN AAAA 2001:688:0:102::1:2}, q{acme.com. IN AAAA 2001:688:0:102::3}, q{acme.com. IN RP abuse.acme.com. acme.com.}, q{acme.com. IN SSHFP 2 1 123456789ABCDEF67890123456789ABCDEF67890}, q{acme.com. IN HINFO SUN4/110 UNIX}, q{acme.com. IN HINFO "SUN4/110 foo" UNIX}, q{acme.com. IN HINFO "SUN4/110 foo" "UNIX bar"}, ); if ($Net::DNS::VERSION > 0.72) { push @test, q{acme.com. IN TLSA 3 0 1 1BFC4290C5798EFCC6D4A1F2D79C3C5F49ACCAC687DF42974B68A45F 05BA074F}; push @test, q{acme.com. IN TLSA 3 0 1 ( 1BFC4290C5798EFCC6D4A1F2D79C3C5F49ACCAC687DF42974B68A45F 05BA074F )}; push @test, q{acme.com. IN TLSA 3 0 1 ( 1BFC4290C5798EFCC6D4A1F2D79C3C5F49ACCAC687DF42974B68A45F 05BA074F )}; } } use Test::More tests => 2 + 2 * scalar @test; for my $rrdata (@test) { my $rrset = Net::DNS::ZoneFile::Fast::parse($rrdata); (my $adata = $rrdata) =~ s/@/./; $adata =~ s/(IN SOA .* (\d+)) \)/ $2 $1/; my $rr = new Net::DNS::RR $adata; (my $rrcorrect = $rr->string) =~ s/\s+/ /g; ok(defined $rrset, "Parsing $rrdata"); (my $rrtxt = $rrset->[0]->string) =~ s/\s+/ /g; is($rrtxt, $rrcorrect, "RR comparison for $rrdata"); } ok(!defined Net::DNS::ZoneFile::Fast::parse(text => "acme.com. in aaaa ok", quiet => 1, soft_errors => 1), "Bad AAAA"); ok(!defined eval { Net::DNS::ZoneFile::Fast::parse(text => "acme.com. in aaaa ok", quiet => 1) }, "Bad AAAA with die"); Net-DNS-ZoneFile-Fast-1.24/t/rr-dnssec.t0000644000237200023720000001446212147513074017705 0ustar hardakerhardaker # This is -*- perl -*- use Net::DNS::ZoneFile::Fast; # we test multiple line and single line parsing for each record # data is pulled from the dnssec-tools.org zone BEGIN { @test = ( q{dnssec-tools.org. 86400 RRSIG SOA 5 2 86400 20060429134027 ( 20060330134027 42869 dnssec-tools.org. QZzAz5sVC5+n7vJhkXfFaN/sdjKXVpT/nv22 NJI+sDde180Sj1pDXW6mFt+Efg4uUAuyLQup jLv20EVM8/oBPA7DjNu2CZHGe8UDeuAoqIth Q/79Ltw4NtP7W1zWAs/ms/oSKiKYrAUHqt0U UskiXkCA1GKn6RNqVT4+IuSUgALLYSMLjlaA 2kE/KaffqeGnynIO2AC5BMFzUlxCSMDSxus3 bJm2xAIxMiUYomw9XfbGfrkCtfIrH+H/LVG/ X2K/kKxjcFcQhkukMUxjzzmAP3xJHq6vgwVM tpm/qaR0g5jH5B46iJefGYzwcMAyexbLOQEW uv1Xs6i/lBwQo9T7xw== )}, q{. 86400 RRSIG SOA 5 0 86400 20090913150000 ( 20090907150000 18160 . kgbUsnZwzY/s9zGcHvQ30tTNk5raweJuvo71 9GzBI+Ennjn25bGp7CYbfLk0tMk9Fcai5nfg 8hDshKwEUemign1r+SkHsiwISOr3vpTAUANg +GzQwLSrZHT81wvS06DYXE/O0L/pxSKfQBON 8owuxhnczIDncP3xeh0Stai2jeU= )}, # failing oddly during text compare. spacing is different. q{nospace.dnssec-tools.org. 86400 RRSIG SOA 5 2 86400 20060429134027 20060330134027 42869 dnssec-tools.org. QZzAz5sVC5+n7vJhkXfFaN/sdjKXVpT/nv22 NJI+sDde180Sj1pDXW6mFt+Efg4uUAuyLQup jLv20EVM8/oBPA7DjNu2CZHGe8UDeuAoqIth Q/79Ltw4NtP7W1zWAs/ms/oSKiKYrAUHqt0U UskiXkCA1GKn6RNqVT4+IuSUgALLYSMLjlaA 2kE/KaffqeGnynIO2AC5BMFzUlxCSMDSxus3 bJm2xAIxMiUYomw9XfbGfrkCtfIrH+H/LVG/ X2K/kKxjcFcQhkukMUxjzzmAP3xJHq6vgwVM tpm/qaR0g5jH5B46iJefGYzwcMAyexbLOQEW uv1Xs6i/lBwQo9T7xw= }, q{dnssec-tools.org. 10800 NSEC cvs.dnssec-tools.org. A DNSKEY MX NS NSEC RRSIG SOA TXT}, q{dnssec-tools.org. 10800 NSEC mor\145text.dnssec-tools.org. CNAME RRSIG NSEC}, q{THA2IPMDLT9RU307BO9LQ6MF5K565A6M.example.com. 10800 IN NSEC3 1 0 100 610b88f0d9f42c74 QIVB7DNNE2T5J9HLI4FRE9PN61F754CK A RRSIG}, q{THA2IPMDLT9RU307BO9LQ6MF5K565A6M.example.com. 10800 IN NSEC3 1 0 100 610b88f0d9f42c74 ( QIVB7DNNE2T5J9HLI4FRE9PN61F754CK A RRSIG )}, q{THA2IPMDLT9RU307BO9LQ6MF5K565A6M.example.com. 10800 IN NSEC3 1 0 100 610b88f0d9f42c74 QIVB7DNNE2T5J9HLI4FRE9PN61F754CK}, q{THA2IPMDLT9RU307BO9LQ6MF5K565A6M.example.com. 10800 IN NSEC3 1 0 100 610b88f0d9f42c74 ( QIVB7DNNE2T5J9HLI4FRE9PN61F754CK )}, q{example.com 0 NSEC3PARAM 1 0 100 610b88f0d9f42c74}, q{dnssec-tools.org. 86400 DNSKEY 256 3 5 ( AQOfW6Uo0QQZS1fJmtx3XoX+B67Bxfyn+uhe py5JifpPWPnx+O0bR30+Oi4bpVrXtipGK3EW ouDWy4eAflrsdIgunotWE1H4/rQaXxc4IowJ V8dm5xejyMswUzPOxL3mnbhQ0gUtSTSO7/Ho EJisuqy50/pg1y8a09PiicJefXaB31IawDXn IZz2QYluyxS2zYPnb/2RjeCxgTzGgtjUYlw5 0czRYDARyGlkAiwxch/RfgEcqoLk+dPwmSU9 l4Shu8XkkpiAFEUqs3cTooA2UltVvKFpqoMT q0EVHcdHDuIExeGCxCw/RjsiOOIey3BKo86T NCU8USUWld3FinA4BPnB ) ; key id = 42869}, q{dnssec-tools.org. 86400 DNSKEY 256 3 5 AQOfW6Uo0QQZS1fJmtx3XoX+B67Bxfyn+uhe py5JifpPWPnx+O0bR30+Oi4bpVrXtipGK3EW ouDWy4eAflrsdIgunotWE1H4/rQaXxc4IowJ V8dm5xejyMswUzPOxL3mnbhQ0gUtSTSO7/Ho EJisuqy50/pg1y8a09PiicJefXaB31IawDXn IZz2QYluyxS2zYPnb/2RjeCxgTzGgtjUYlw5 0czRYDARyGlkAiwxch/RfgEcqoLk+dPwmSU9 l4Shu8XkkpiAFEUqs3cTooA2UltVvKFpqoMT q0EVHcdHDuIExeGCxCw/RjsiOOIey3BKo86T NCU8USUWld3FinA4BPnB}, q{dnssec-tools.org. 86400 DNSKEY 256 3 5 AQOfW6Uo0QQZS1fJmtx3XoX+B67Bxfyn+uhe py5JifpPWPnx+O0bR30+Oi4bpVrXtipGK3EW ouDWy4eAflrsdIgunotWE1H4/rQaXxc4IowJ V8dm5xejyMswUzPOxL3mnbhQ0gUtSTSO7/Ho EJisuqy50/pg1y8a09PiicJefXaB31IawDXn IZz2QYluyxS2zYPnb/2RjeCxgTzGgtjUYlw5 0czRYDARyGlkAiwxch/RfgEcqoLk+dPwmSU9 l4Shu8XkkpiAFEUqs3cTooA2UltVvKFpqoMT q0EVHcdHDuIExeGCxCw/RjsiOOIey3BKo86T NCU8USUWld3FinA4BPnB}, q{dnssec-tools.org. 86400 DNSKEY 256 3 5 AQOfW6Uo0QQZS1fJmtx3XoX+B67Bxfyn+uhe py5JifpPWPnx+O0bR30+Oi4bpVrXtipGK3EW ouDWy4eAflrsdIgunotWE1H4/rQaXxc4IowJ V8dm5xejyMswUzPOxL3mnbhQ0gUtSTSO7/Ho EJisuqy50/pg1y8a09PiicJefXaB31IawDXn IZz2QYluyxS2zYPnb/2RjeCxgTzGgtjUYlw5 0czRYDARyGlkAiwxch/RfgEcqoLk+dPwmSU9 l4Shu8XkkpiAFEUqs3cTooA2UltVvKFpqoMT q0EVHcdHDuIExeGCxCw/RjsiOOIey3BKo86T NCU8USUWld3FinA4BPnB;bogus extra comment}, q{test.dnssec-tools.org. 86400 IN DS 28827 5 1 23a4c97124ab46e7fb7abb58e36887ff78745ac8}, q{test.dnssec-tools.org. 86400 IN DS 28827 5 2 7d06a161755f7c7ca0d15b8039c7d7b45fb8e5dd025fcebe209cb07756bbae07}, q{test.dnssec-tools.org. 86400 IN DS 28827 5 2 ( 7d06a161755f7c7ca0d15b8039c7d7b45fb8e5dd025fcebe209cb07756bbae07 ) }, q{test.dnssec-tools.org. 86400 DS 28827 5 1 23a4c97124ab46e7fb7abb58e36887ff78745ac8}, # a specific test for ttl values that could accidentially match DS q{test.dnssec-tools.org. DS 28827 5 2 7d06a161755f7c7ca0d15b8039c7d7b45fb8e5dd025fcebe209cb07756bbae07}, # bind 10 puts parens in new places: q{example.com 10 RRSIG SOA 5 2 10 20080613221109 ( 20080514221109 51389 example.com. rQ1d9a6ZCbZvwx47efKJL2s1FbcHzLt4SKca F2Xwr8YyPyhMffjkdFwtXGLFwvaQ9SE2ocEU /QpxKmvsqSyE3SyinuuCaR/XF/7XKK/PShUg iRJ7S/GExtJDfheJ04zydDyIYM8M96GpE920 0LfJVZuo+gxwvrvTZiejVn1aNnc= )}, q{example.com 10 RRSIG SOA 5 2 10 ( 20080613221109 20080514221109 51389 example.com. rQ1d9a6ZCbZvwx47efKJL2s1FbcHzLt4SKca F2Xwr8YyPyhMffjkdFwtXGLFwvaQ9SE2ocEU /QpxKmvsqSyE3SyinuuCaR/XF/7XKK/PShUg iRJ7S/GExtJDfheJ04zydDyIYM8M96GpE920 0LfJVZuo+gxwvrvTZiejVn1aNnc= )}, ); } use Test::More tests => 2 * scalar @test; for my $rrdata (@test) { my $rrset = Net::DNS::ZoneFile::Fast::parse($rrdata); (my $adata = $rrdata) =~ s/@/./; $adata =~ s/(IN SOA .* (\d+)) \)/ $2 $1/; my $rr = new Net::DNS::RR $adata; (my $rrcorrect = $rr->string) =~ s/\s+/ /g; ok(defined $rrset, "Parsing $rrdata"); (my $rrtxt = $rrset->[0]->string) =~ s/\s+/ /g; if ($rrtxt =~ /^nospace/) { $rrtxt =~ s/\s//g; $rrcorrect =~ s/\s//g; } is($rrtxt, $rrcorrect, "RR comparison for $rrdata"); } Net-DNS-ZoneFile-Fast-1.24/MANIFEST0000644000237200023720000000056712402370347016505 0ustar hardakerhardakerChanges MANIFEST Makefile.PL README Fast.pm t/00-load.t t/comment.t t/generate.t t/lines.t t/newlines.t t/null-rr.t t/origin.t t/read.t t/rr-multi-a.t t/rrs-ws.t t/rrs.t t/soattl.t t/ttl.t t/zone.t t/rr-dnssec.t META.yml Module meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Net-DNS-ZoneFile-Fast-1.24/Fast.pm0000644000237200023720000014234012402370344016600 0ustar hardakerhardaker# ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42) # wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return. Anton Berezin # ---------------------------------------------------------------------------- # Copyright (c) 2005-2013 SPARTA, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the name of SPARTA, Inc nor the names of its contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS # IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- # Copyright (c) 2013-2013 PARSONS, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # * Neither the name of SPARTA, Inc nor the names of its contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS # IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $Id: Fast.pm 8304 2014-09-05 17:20:49Z hardaker $ # package Net::DNS::ZoneFile::Fast; # documentation at the __END__ of the file use strict; use 5.005; use vars qw($VERSION); use IO::File; use Net::DNS; use Net::DNS::RR; use MIME::Base64; $VERSION = '1.24'; my $MAXIMUM_TTL = 0x7fffffff; my $pat_ttl = qr{\d+[\dwdhms]*}i; my $pat_skip = qr{\s*(?:;.*)?}; my $pat_name = qr{(?:[-\*\w\$\d\/*]|\\[0-2]\d\d)+(?:\.(?:[-\*\w\$\d\/]|\\[0-2]\d\d)+)*}; my $pat_maybefullnameorroot = qr{(?:\.|(?:[-\w\$\d\/*]|\\[0-2]\d\d)+(?:\.(?:[-\w\$\d\/]|\\[0-2]\d\d)+)*\.?)}; # # Added the ability to have a backslash in the SOA username. This is to # provide for the RFC-allowed "Joe\.Jones.example.com" construct to allow # dots in usernames. Keeping the original version here for easy reference. # # my $pat_maybefullname = qr{[-\w\$\d\/*]+(?:\.[-\w\$\d\/]+)*\.?}; my $pat_maybefullname = qr{(?:[-\+\w\$\d\/*\\]|\\[0-2]\d\d)+(?:\.(?:[-\+\w\$\d\/]|\\[0-2]\d\d)+)*\.?}; my $debug; my $domain; my $parse; my $ln; my $default_ttl; my $minimum; my $origin; my $ttl; my @zone; my $soa; my $rrsig; my $sshfp; my $key; my $dnskey; my $ds; my $nsec3; my $tlsa; my $on_error; my $quiet; my $soft_errors; my $fh; my @fhs; my @lns; my $includes_root; my $globalerror; my $nsec3capable; # boot strap optional DNSSEC module functions # (not optional if trying to parse a signed zone, but we don't need # these modules unless we are. $nsec3capable = eval { require Net::DNS::RR::NSEC; require Net::DNS::RR::DNSKEY; require Net::DNS::RR::NSEC3; require Net::DNS::RR::NSEC3PARAM; require MIME::Base32; }; sub parse { my %param; my $text; $on_error = undef; $parse = \&parse_line; $ln = 0; $domain = "."; $default_ttl = -1; $minimum = -1; @zone = (); if (@_ == 1) { $text = shift; } else { %param = @_; if (defined $param{text}) { $text = $param{text}; } elsif (defined $param{fh}) { $fh = $param{fh}; } elsif (defined $param{file}) { $fh = IO::File->new($param{file}, "r"); error("cannot open $param{file}: $!") unless defined $fh; } else { error("want zone text, or file, or fh"); } } $debug = $param{debug}; $quiet = $param{quiet}; $origin = $param{origin}; $origin = "." unless defined $origin; $origin = ".$origin" unless $origin =~ /^\./; $origin = "$origin." unless $origin =~ /\.$/; $on_error = $param{on_error} || undef; $param{soft_errors} = 1 if $on_error && !exists $param{soft_errors}; $quiet = 1 if $on_error && !exists $param{quiet}; $soft_errors = $param{soft_errors}; $includes_root = $param{includes_root}; eval { if ($fh) { do { while ($_ = readline($fh)) { $ln++; $parse->(); } $fh = shift @fhs; $ln = shift @lns; } while ($fh); } else { my @text = split "\n", $text; for (@text) { $ln++; $parse->(); } } }; if ($@) { die "$globalerror (at input line #$ln)" if ($globalerror); return undef if $param{soft_errors}; die; } my @r; $minimum = 0 if $minimum < 0; for my $z (@zone) { $z->{ttl} = $minimum if $z->{ttl} <= 0; chop $z->{name}; my $line = $z->{Line}; my $lines = $z->{Lines} || 1; delete $z->{Line}; delete $z->{Lines}; if ($param{tolower}) { $z->{name} = lc $z->{name}; $z->{cname} = lc $z->{cname} if defined $z->{cname}; $z->{dname} = lc $z->{dname} if defined $z->{dname}; $z->{exchange} = lc $z->{exchange} if defined $z->{exchange}; $z->{mname} = lc $z->{mname} if defined $z->{mname}; $z->{rname} = lc $z->{rname} if defined $z->{rname}; $z->{nsdname} = lc $z->{nsdname} if defined $z->{nsdname}; $z->{ptrdname} = lc $z->{ptrdname} if defined $z->{ptrdname}; $z->{target} = lc $z->{target} if defined $z->{target}; $z->{mbox} = lc $z->{mbox} if defined $z->{mbox}; $z->{txtdname} = lc $z->{txtdname} if defined $z->{txtdname}; } elsif ($param{toupper}) { $z->{name} = uc $z->{name}; $z->{cname} = uc $z->{cname} if defined $z->{cname}; $z->{dname} = uc $z->{dname} if defined $z->{dname}; $z->{exchange} = uc $z->{exchange} if defined $z->{exchange}; $z->{mname} = uc $z->{mname} if defined $z->{mname}; $z->{rname} = uc $z->{rname} if defined $z->{rname}; $z->{nsdname} = uc $z->{nsdname} if defined $z->{nsdname}; $z->{ptrdname} = uc $z->{ptrdname} if defined $z->{ptrdname}; $z->{target} = uc $z->{target} if defined $z->{target}; $z->{mbox} = uc $z->{mbox} if defined $z->{mbox}; $z->{txtdname} = uc $z->{txtdname} if defined $z->{txtdname}; } my $newrec = Net::DNS::RR->new(%$z); if ($newrec->{'type'} eq 'DNSKEY') { if (ref($newrec) ne 'Net::DNS::RR::DNSKEY') { warn "Failed to define a DNSSEC object (got: " . ref($newrec) . "); you're probably missing either MIME::Base64 or MIME::Base32"; } else { $newrec->setkeytag; } } # no longer an issue with recent Net::DNS #if ($newrec->{'type'} eq 'RRSIG') { # fix an issue with RRSIG's signame being stripped of # the trailing dot. # $newrec->{'signame'} .= "." # if ($newrec->{'signame'} !~ /\.$/); #} push @r, $newrec; $r[-1]->{Line} = $line; $r[-1]->{Lines} = $lines; } return \@r; } sub error { if ($on_error) { eval { $on_error->($ln, @_) }; if($@ ne '') { # set global error so parse can die appropriately later. $globalerror = $@; die; } } else { warn "@_, line $ln\n" if $soft_errors && !$quiet; } die "@_, line $ln\n"; } sub parse_line { if (/^\$include[ \t]+/ig) { if (!/\G[\"\']*([^\s\'\"]+)[\"\']*/igc) { error("no include file specified $_"); return; } my $fn = $1; if (! -f $fn) { # expand file according to includes_root if ($includes_root && -f $includes_root . '/'. $fn) { $fn = $includes_root . '/'. $fn; } else { error("could not find file $fn"); return; } } unshift @fhs, $fh; unshift @lns, $ln; $fh = IO::File->new($fn, "r"); $ln = 0; error("cannot open include file $fn: $!") unless defined $fh; return; } elsif (/^\$origin[ \t]+/ig) { if (/\G($pat_maybefullname)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; $origin = $name; $origin = ".$origin" unless $origin =~ /^\./; return; } elsif (/\G\.$pat_skip$/gc) { $origin = "."; return; } else { error("bad \$ORIGIN"); } } elsif (/^\$generate[ \t]+/ig) { if (/\G(\d+)\s*-\s*(\d+)\s+(.*)$/) { my $from = $1; my $to = $2; my $pat = $3; error("bad range in \$GENERATE") if $from > $to; error("\$GENERATE pattern without a wildcard") if $pat !~ /\$/; while ($from <= $to) { $_ = $pat; s{\$ (?:\{ ([\d+-]+) (?:, (\d+) (?:, ([doxX]) )? )? \})?} { my ($offset, $width, $base) = ($1, $2, $3); $offset ||= 0; $width ||= 0; $base ||= 'd'; sprintf "%0$width$base", $offset + $from; }xge; $parse->(); $from++; } return; } else { error("bad \$GENERATE"); } } elsif (/^\$ttl\b/ig) { if (/\G\s+($pat_ttl)$pat_skip$/) { my $v = $1; $ttl = $default_ttl = ttl_fromtext($v); if ($default_ttl < 0 || $default_ttl > $MAXIMUM_TTL) { error("bad TTL value `$v'"); } else { debug("\$TTL < $default_ttl\n") if $debug; } } else { error("wrong \$TTL"); } return; } elsif (/^$pat_skip$/g) { # skip return; } elsif (/^[ \t]+/g) { # fall through } elsif (/^\.[ \t]+/g) { $domain = "."; } elsif (/^\@[ \t]+/g) { $domain = $origin; $domain =~ s/^.// unless $domain eq "."; } elsif (/^$/g) { # skip return; } elsif (/^($pat_name\.)[ \t]+/g) { $domain = $1; } elsif (/^($pat_name)[ \t]+/g) { $domain = "$1$origin"; } else { error("syntax error"); } if (/\G($pat_ttl)[ \t]+/gc) { my $v = $1; $ttl = ttl_fromtext($v); if ($ttl == 0) { $ttl = $default_ttl; } else { if ($ttl < 0 || $ttl > $MAXIMUM_TTL) { error("bad TTL value `$v'"); } } } else { $ttl = $default_ttl; } if (/\G(in)[ \t]+/igc) { # skip; we only support IN class } if (/\G(a)[ \t]+/igc) { if (/\G(\d+)\.(\d+)\.(\d+)\.(\d+)$pat_skip$/ && $1 < 256 && $2 < 256 && $3 < 256 && $4 < 256) { push @zone, { Line => $ln, name => $domain, type => "A", ttl => $ttl, class => "IN", address => "$1.$2.$3.$4", }; } else { error("bad IP address"); } } elsif (/\G(ptr)[ \t]+/igc) { if (/\G($pat_maybefullname)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; chop $name; push @zone, { Line => $ln, name => $domain, type => "PTR", ttl => $ttl, class => "IN", ptrdname => $name, }; } elsif (/\G\@$pat_skip$/gc) { my $name = $origin; $name =~ s/^.// unless $name eq "."; chop $name; push @zone, { Line => $ln, name => $domain, type => "PTR", ttl => $ttl, class => "IN", ptrdname => $name, }; } else { error("bad name in PTR"); } } elsif (/\G(afsdb)[ \t]+/igc) { my $subtype; if (/\G(\d+)[ \t]+/gc) { $subtype = $1; } else { error("bad subtype in AFSDB"); } if (/\G($pat_maybefullname)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; chop $name; push @zone, { Line => $ln, name => $domain, type => "AFSDB", ttl => $ttl, class => "IN", subtype => $subtype, hostname => $name, }; } } elsif (/\G(cname)[ \t]+/igc) { if (/\G($pat_maybefullname)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; chop $name; push @zone, { Line => $ln, name => $domain, type => "CNAME", ttl => $ttl, class => "IN", cname => $name, }; } elsif (/\G\@$pat_skip$/gc) { my $name = $origin; $name =~ s/^.// unless $name eq "."; chop $name; push @zone, { Line => $ln, name => $domain, type => "CNAME", ttl => $ttl, class => "IN", cname => $name, }; } else { error("bad cname in CNAME"); } } elsif (/\G(dname)[ \t]+/igc) { if (/\G($pat_maybefullname)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; chop $name; push @zone, { Line => $ln, name => $domain, type => "DNAME", ttl => $ttl, class => "IN", dname => $name, }; } elsif (/\G\@$pat_skip$/gc) { my $name = $origin; $name =~ s/^.// unless $name eq "."; chop $name; push @zone, { Line => $ln, name => $domain, type => "DNAME", ttl => $ttl, class => "IN", dname => $name, }; } else { error("bad dname in DNAME"); } } elsif (/\G(mx)[ \t]+/igc) { my $prio; if (/\G(\d+)[ \t]+/gc) { $prio = $1; } else { error("bad priority in MX"); } if (/\G($pat_maybefullnameorroot)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; chop $name; push @zone, { Line => $ln, name => $domain, type => "MX", ttl => $ttl, class => "IN", preference => $prio, exchange => $name, }; } elsif (/\G\@$pat_skip$/gc) { my $name = $origin; $name =~ s/^.// unless $name eq "."; chop $name; push @zone, { Line => $ln, name => $domain, type => "MX", ttl => $ttl, class => "IN", preference => $prio, exchange => $name, }; } else { error("bad exchange in MX"); } } elsif (/\G(aaaa)[ \t]+/igc) { if (/\G([\da-fA-F:.]+)$pat_skip$/) { # parsing stolen from Net::DNS::RR::AAAA my $string = $1; if ($string =~ /^(.*):(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { my ($front, $a, $b, $c, $d) = ($1, $2, $3, $4, $5); $string = $front . sprintf(":%x:%x", ($a << 8 | $b), ($c << 8 | $d)); } my @addr; if ($string =~ /^(.*)::(.*)$/) { my ($front, $back) = ($1, $2); my @front = split(/:/, $front); my @back = split(/:/, $back); my $fill = 8 - (@front ? $#front + 1 : 0) - (@back ? $#back + 1 : 0); my @middle = (0) x $fill; @addr = (@front, @middle, @back); } else { @addr = split(/:/, $string); if (@addr < 8) { @addr = ((0) x (8 - @addr), @addr); } } push @zone, { Line => $ln, name => $domain, type => "AAAA", ttl => $ttl, class => "IN", address => sprintf("%x:%x:%x:%x:%x:%x:%x:%x", map { hex $_ } @addr), }; } else { error("bad IPv6 address"); } } elsif (/\G(ns)[ \t]+/igc) { if (/\G($pat_maybefullname)$pat_skip$/gc) { my $name = $1; $name = "$name$origin" unless $name =~ /\.$/; chop $name; push @zone, { Line => $ln, name => $domain, type => "NS", ttl => $ttl, class => "IN", nsdname => lc($name), }; } elsif (/\G\@$pat_skip$/gc) { my $name = $origin; $name =~ s/^.// unless $name eq "."; chop $name; push @zone, { Line => $ln, name => $domain, type => "NS", ttl => $ttl, class => "IN", nsdname => lc($name), }; } else { error("bad name in NS"); } } elsif (/\G(soa)\b/igc) { $parse = \&parse_soa_name; $soa = { Line => $ln, name => $domain, type => "SOA", ttl => $ttl, class => "IN", breakable => 0, nextkey => "mname", }; $parse->(); return; } elsif (/\G(txt|spf)[ \t]+/igc) { my $type = uc($1); if (/\G('[^']+')$pat_skip$/gc) { push @zone, { Line => $ln, name => $domain, type => $type, ttl => $ttl, class => "IN", txtdata => $1, }; } elsif (/\G("[^"]+")$pat_skip$/gc) { push @zone, { Line => $ln, name => $domain, type => $type, ttl => $ttl, class => "IN", txtdata => $1, }; } elsif (/\G(["']?.*?["']?)$pat_skip$/gc) { push @zone, { Line => $ln, name => $domain, type => $type, ttl => $ttl, class => "IN", txtdata => $1, }; } else { error("bad txtdata in $type"); } } elsif (/\G(type[0-9]+)[ \t]+/igc) { my $type = $1; if (/\G\\#\s+(\d+)\s+\(\s(.*)$/gc) { # multi-line $sshfp = { Line => $ln, name => $domain, type => uc $type, ttl => $ttl, class => "IN", fptype => $1, fingerprint => $2, }; $parse = \&parse_sshfp; } elsif (/\G\\#\s+(\d+)\s+(.*)$pat_skip$/gc) { push @zone, { Line => $ln, name => $domain, type => uc $type, ttl => $ttl, class => "IN", fptype => $1, fingerprint => $2, }; } else { error("bad data in in $type"); } } elsif (/\G(sshfp)[ \t]+/igc) { if (/\G(\d+)\s+(\d+)\s+\(\s*$/gc) { # multi-line $sshfp = { Line => $ln, name => $domain, type => "SSHFP", ttl => $ttl, class => "IN", algorithm => $1, fptype => $2, }; $parse = \&parse_sshfp; } elsif (/\G(\d+)\s+(\d+)\s+([a-zA-Z0-9]+)$pat_skip$/gc) { push @zone, { Line => $ln, name => $domain, type => "SSHFP", ttl => $ttl, class => "IN", algorithm => $1, fptype => $2, fingerprint => $3, }; } else { error("bad data in in SSHFP"); } } elsif (/\G(loc)[ \t]+/igc) { # parsing stolen from Net::DNS::RR::LOC if (/\G (\d+) \s+ # deg lat ((\d+) \s+)? # min lat (([\d.]+) \s+)? # sec lat (N|S) \s+ # hem lat (\d+) \s+ # deg lon ((\d+) \s+)? # min lon (([\d.]+) \s+)? # sec lon (E|W) \s+ # hem lon (-?[\d.]+) m? # altitude (\s+ ([\d.]+) m?)? # size (\s+ ([\d.]+) m?)? # horiz precision (\s+ ([\d.]+) m?)? # vert precision $pat_skip $/ixgc) { # Defaults (from RFC 1876, Section 3). my $default_min = 0; my $default_sec = 0; my $default_size = 1; my $default_horiz_pre = 10_000; my $default_vert_pre = 10; # Reference altitude in centimeters (see RFC 1876). my $reference_alt = 100_000 * 100; my $version = 0; my ($latdeg, $latmin, $latsec, $lathem) = ($1, $3, $5, $6); my ($londeg, $lonmin, $lonsec, $lonhem) = ($7, $9, $11, $12); my ($alt, $size, $horiz_pre, $vert_pre) = ($13, $15, $17, $19); $latmin = $default_min unless $latmin; $latsec = $default_sec unless $latsec; $lathem = uc($lathem); $lonmin = $default_min unless $lonmin; $lonsec = $default_sec unless $lonsec; $lonhem = uc($lonhem); $size = $default_size unless $size; $horiz_pre = $default_horiz_pre unless $horiz_pre; $vert_pre = $default_vert_pre unless $vert_pre; push @zone, { Line => $ln, name => $domain, type => "LOC", ttl => $ttl, class => "IN", version => $version, size => $size * 100, horiz_pre => $horiz_pre * 100, vert_pre => $vert_pre * 100, latitude => dms2latlon($latdeg, $latmin, $latsec, $lathem), longitude => dms2latlon($londeg, $lonmin, $lonsec, $lonhem), altitude => $alt * 100 + $reference_alt, }; } else { error("bad LOC data"); } } elsif (/\G(hinfo)[ \t]+/igc) { if (/\G(["'].*?["']|\S+)\s+(["'].*?["']|\S+)$pat_skip$/gc) { my $result = { Line => $ln, name => $domain, type => "HINFO", ttl => $ttl, class => "IN", cpu => $1, os => $2, }; $result->{'cpu'} =~ s/^["']//; $result->{'cpu'} =~ s/["']$//; $result->{'os'} =~ s/^["']//; $result->{'os'} =~ s/["']$//; push @zone, $result; } else { error("bad HINFO data"); } } elsif (/\G(srv)[ \t]+/igc) { # parsing stolen from Net::DNS::RR::SRV if (/\G(\d+)\s+(\d+)\s+(\d+)\s+(\S+)$pat_skip$/gc) { push @zone, { Line => $ln, name => $domain, type => "SRV", ttl => $ttl, class => "IN", priority => $1, weight => $2, port => $3, target => $4, }; $zone[-1]->{target} =~ s/\.+$//; } else { error("bad SRV data"); } } elsif (/\G(key)[ \t]+/igc) { if (!/\G(\d+)\s+(\d+)\s+(\d+)\s+/gc) { error("bad KEY data 1"); } $dnskey = { first => 1, Line => $ln, name => $domain, ttl => $ttl, class => "IN", type => "KEY", flags => $1, protocol => $2, algorithm => $3 }; if (/\G\(\s*$/gc) { # multi-line $parse = \&parse_dnskey; } elsif (/\G(.*\S)\s*$/) { # single-line $dnskey->{'key'} .= $1; $dnskey->{'key'} =~ s/\s//g; $dnskey->{'keybin'} = decode_base64($dnskey->{'key'}); push @zone, $dnskey; $dnskey = undef; } else { error("bad KEY data 2"); } } elsif (/\G(rrsig)[ \t]+/igc) { if (/\G(\w+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/gc) { # some versions of bind (>=10) put the sig-expir on the first line $rrsig = { first => 1, Line => $ln, name => $domain, type => "RRSIG", class => "IN", ttl => $ttl, typecovered => $1, algorithm => $2, labels => $3, orgttl => $4, }; } else { error("bad RRSIG data 1"); } if (/\G(\d+)\s+/gc) { # some versions of bind (<10) put the sig-expir on the first line # and newer ones put it on the next. $rrsig->{'sigexpiration'} = $1; } else { $rrsig->{'needsigexp'} = $1; } if (/\G\(\s*$/gc) { # multi-line $parse = \&parse_rrsig; } elsif (/\G(\d+)\s+(\d+)\s+($pat_maybefullnameorroot)\s+([^=]+=)\s*/gc) { # single-line $rrsig->{'siginception'} = $1; $rrsig->{'keytag'} = $2; $rrsig->{'signame'} = $3; $rrsig->{'sig'} = $4; $rrsig->{'sigbin'} = decode_base64($rrsig->{'sig'}); push @zone, $rrsig; $rrsig = undef; } else { error("bad RRSIG data 2"); } } elsif (/\G(dnskey)[ \t]+/igc) { if (!/\G(\d+)\s+(\d+)\s+(\d+)\s+/gc) { error("bad DNSKEY data 1"); } $dnskey = { first => 1, Line => $ln, name => $domain, ttl => $ttl, class => "IN", type => "DNSKEY", flags => $1, protocol => $2, algorithm => $3 }; if (/\G\(\s*$/gc) { # multi-line $parse = \&parse_dnskey; } elsif (/\G([\sA-Za-z0-9\+\/=]+).*$/) { # single-line $dnskey->{'key'} .= $1; $dnskey->{'key'} =~ s/\s//g; $dnskey->{'keybin'} = decode_base64($dnskey->{'key'}); push @zone, $dnskey; $dnskey = undef; } else { error("bad DNSKEY data 2"); } } elsif (/\G(ds)[ \t]+/igc) { if (!/\G(\d+)\s+(\d+)\s+(\d+)\s+/gc) { error("bad DS data 1"); } $ds = { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "DS", keytag => $1, algorithm => $2, digtype => $3, }; if (/\G\(\s*$/gc) { # multi-line $parse = \&parse_ds; } elsif (/\G(.*\S)\s*$/) { # single line $ds->{'digest'} .= $1; $ds->{'digest'} = lc($ds->{'digest'}); $ds->{'digest'} =~ s/\s//g; # remove any surrounding single line ()s $ds->{'digest'} =~ s/^\(//; $ds->{'digest'} =~ s/\)$//; $ds->{'digestbin'} = pack("H*", $ds->{'digest'}); push @zone, $ds; $ds = undef; } else { error("bad DS data"); } } elsif (/\G(tlsa)[ \t]+/igc) { if (!/\G(\d+)\s+(\d+)\s+(\d+)\s+/gc) { error("bad TLSA data 1"); } $tlsa = { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "TLSA", usage => $1, selector => $2, matchingtype => $3, }; if (/\G\(\s*$/gc) { # multi-line $parse = \&parse_tlsa; } elsif (/\G(.*\S)\s*$/) { # single line $tlsa->{'cert'} .= $1; $tlsa->{'cert'} = lc($tlsa->{'cert'}); $tlsa->{'cert'} =~ s/\s//g; # remove any surrounding single line ()s $tlsa->{'cert'} =~ s/^\(//; $tlsa->{'cert'} =~ s/\)$//; $tlsa->{'certbin'} = pack("H*", $tlsa->{'cert'}); push @zone, $tlsa; $tlsa = undef; } else { error("bad TLSA data"); } } elsif (/\G(nsec)[ \t]+/igc) { if (/\G\s*($pat_maybefullnameorroot)\s+(.*?)$pat_skip$/gc) { # XXX: set the typebm field ourselves? my ($nxtdname, $typelist) = ($1, $2); $typelist = join(" ",sort split(/\s+/,$typelist)); push @zone, { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "NSEC", nxtdname => $nxtdname, typelist => $typelist, typebm => Net::DNS::RR::NSEC::_typearray2typebm(split(/\s+/,$typelist)), }; } else { error("bad NSEC data"); } } elsif (/\G(nsec3)[ \t]+/igc) { error ("You are missing required modules for NSEC3 support") if (!$nsec3capable); if (/\G\s*(\d+)\s+(\d+)\s+(\d+)\s+([-0-9A-Fa-f]+)\s+($pat_maybefullname)\s*(.*?)$pat_skip$/gc) { # XXX: set the typebm field ourselves? my ($alg, $flags, $iters, $salt, $nxthash, $typelist) = ($1, $2, $3, $4, $5, $6); $typelist = join(" ",sort split(/\s+/,$typelist)); my $binhash = MIME::Base32::decode(uc($nxthash)); push @zone, { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "NSEC3", hashalgo => $alg, flags => $flags, iterations => $iters, hnxtname => $nxthash, hnxtnamebin => $binhash, hashlength => length($binhash), salt => $salt, saltbin => pack("H*",$salt), saltlength => int(length($salt)/2), typelist => $typelist, typebm => Net::DNS::RR::NSEC::_typearray2typebm(split(/\s+/,$typelist)), }; # multi-line } elsif (/\G\s*(\d+)\s+(\d+)\s+(\d+)\s+([-0-9A-Fa-f]+)\s+\(/gc) { # XXX: set the typebm field ourselves? my ($alg, $flags, $iters, $salt) = ($1, $2, $3, $4); $nsec3 = { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "NSEC3", hashalgo => $alg, flags => $flags, iterations => $iters, salt => $salt, saltbin => pack("H*",$salt), saltlength => int(length($salt)/2), }; $parse = \&parse_nsec3; } else { error("bad NSEC data"); } } elsif (/\G(nsec3param)[ \t]+/igc) { if (/\G\s*(\d+)\s+(\d+)\s+(\d+)\s+([-0-9A-Fa-f]+)$pat_skip$/gc) { # XXX: set the typebm field ourselves? my ($alg, $flags, $iters, $salt) = ($1, $2, $3, $4); push @zone, { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "NSEC3PARAM", hashalgo => $alg, flags => $flags, iterations => $iters, salt => $salt, saltbin => pack("H*",$salt), saltlength => int(length($salt)/2), }; } else { error("bad NSEC data"); } } elsif (/\G(rp)[ \t]+/igc) { my $mbox; if (/\G($pat_maybefullname)[ \t]+/gc) { $mbox = $1; $mbox = "$mbox$origin" unless $mbox =~ /\.$/; chop $mbox; } elsif (/\G\@[ \t]+/gc) { $mbox = $origin; $mbox =~ s/^.// unless $mbox eq "."; chop $mbox; } else { error("bad mbox in PTR"); } my $txtdname; if (/\G($pat_maybefullname)$pat_skip$/gc) { $txtdname = $1; $txtdname = "$txtdname$origin" unless $txtdname =~ /\.$/; chop $txtdname; } elsif (/\G\@$pat_skip$/gc) { $txtdname = $origin; $txtdname =~ s/^.// unless $txtdname eq "."; chop $txtdname; } else { error("bad txtdname in PTR"); } push @zone, { Line => $ln, name => $domain, type => "RP", ttl => $ttl, class => "IN", mbox => $mbox, txtdname => $txtdname, }; } elsif (/\G(naptr)[ \t]+/igc) { # Parsing taken from Net::DNS::RR::NAPTR if (!/\G(\d+) \s+ (\d+) \s+ ['"] (.*?) ['"] \s+ ['"] (.*?) ['"] \s+ ['"] (.*?) ['"] \s+ (\S+)$/xgc) { error("bad NAPTR data"); } push @zone, { Line => $ln, name => $domain, class => "IN", ttl => $ttl, type => "NAPTR", order => $1, preference => $2, flags => $3, service => $4, regexp => $5, replacement => $6, }; $zone[ $#zone ]{replacement} =~ s/\.+$//; } elsif (/\Gany\s+tsig.*$/igc) { # XXX ignore tsigs } else { error("unrecognized type for $domain\n$_\n"); } } # Reference lat/lon (see RFC 1876). my $reference_latlon = 2**31; # Conversions to/from thousandths of a degree. my $conv_sec = 1000; my $conv_min = 60 * $conv_sec; my $conv_deg = 60 * $conv_min; sub dms2latlon { my ($deg, $min, $sec, $hem) = @_; my ($retval); $retval = ($deg * $conv_deg) + ($min * $conv_min) + ($sec * $conv_sec); $retval = -$retval if ($hem eq "S") || ($hem eq "W"); $retval += $reference_latlon; return $retval; } sub parse_soa_name { error("parse_soa_name: internal error, no \$soa") unless $soa; if ($soa->{breakable}) { if (/\G[ \t]*($pat_maybefullname)$pat_skip$/igc) { $soa->{$soa->{nextkey}} = $1; } elsif (/\G$pat_skip$/gc) { return; } elsif (/\G[ \t]*(\@)[ \t]/igc) { $soa->{$soa->{nextkey}} = $origin; } elsif (/\G[ \t]*($pat_name\.)[ \t]/igc) { $soa->{$soa->{nextkey}} = $1; } else { error("expected valid $soa->{nextkey}"); } } else { if (/\G[ \t]*($pat_maybefullname)/igc) { $soa->{$soa->{nextkey}} = $1; } elsif (/\G[ \t]*\($pat_skip$/igc) { $soa->{breakable} = 1; return; } elsif (/\G[ \t]*(\@)[ \t]/igc) { $soa->{$soa->{nextkey}} = $origin; } elsif (/\G[ \t]*\(/igc) { $soa->{breakable} = 1; $parse->(); return; } else { error("expected valid $soa->{nextkey}"); } } if ($soa->{nextkey} eq "mname") { $soa->{mname} = lc($soa->{mname}); $soa->{nextkey} = "rname"; } elsif ($soa->{nextkey} eq "rname") { $soa->{rname} = lc($soa->{rname}); $soa->{nextkey} = "serial"; $parse = \&parse_soa_number; } else { error("parse_soa_name: internal error, bad {nextkey}") unless $soa; } $parse->(); } sub ttl_or_serial { my ($v) = @_; if ($soa->{nextkey} eq "serial") { error("bad serial number") unless $v =~ /^\d+$/; } else { $v = ttl_fromtext($v); error("bad $soa->{nextkey}") unless $v; } return $v; } sub parse_rrsig { # got more data if ($rrsig->{'first'}) { delete $rrsig->{'first'}; if (exists($rrsig->{'needsigexp'}) && /\G\s*(\d+)\s+(\d+)\s+(\d+)\s+($pat_maybefullnameorroot)/gc) { delete $rrsig->{'needsigexp'}; $rrsig->{'sigexpiration'} = $1; $rrsig->{'siginception'} = $2; $rrsig->{'keytag'} = $3; $rrsig->{'signame'} = $4; } elsif (!exists($rrsig->{'needsigexp'}) && /\G\s*(\d+)\s+(\d+)\s+($pat_maybefullnameorroot)/gc) { $rrsig->{'siginception'} = $1; $rrsig->{'keytag'} = $2; $rrsig->{'signame'} = $3; } else { error("bad rrsig second line"); } } else { if (/\)\s*$/) { if (/\G\s*(\S+)\s*\)\s*$/gc) { $rrsig->{'sig'} .= $1; $rrsig->{'sigbin'} = decode_base64($rrsig->{'sig'}); # we're done $parse = \&parse_line; push @zone, $rrsig; $rrsig = undef; } else { error("bad rrsig last line"); } } else { if (/\G\s*(\S+)\s*$/gc) { $rrsig->{'sig'} .= $1; } else { error("bad rrsig remaining lines"); } } } } sub parse_sshfp { # got more data if (/\)\s*$/) { # last line if (/\G\s*(\S+)\s*\)\s*$/gc) { $sshfp->{'fingerprint'} .= $1; # we're done $parse = \&parse_line; push @zone, $sshfp; $sshfp = undef; } else { error("bad sshfp last line"); } } else { if (/\G\s*(\S+)\s*$/gc) { $sshfp->{'fingerprint'} .= $1; } else { error("bad sshfp remaining lines"); } } } sub parse_dnskey { # got more data? if (/\)\s*;.*$/) { if (/\G\s*(\S*)\s*\)\s*;.*$/gc) { $dnskey->{'key'} .= $1; # we're done $parse = \&parse_line; $dnskey->{'keybin'} = decode_base64($dnskey->{'key'}); push @zone, $dnskey; $dnskey = undef; } else { error("bad dnskey last line"); } } else { if (/\G\s*(\S+)\s*$/gc) { $dnskey->{'key'} .= $1; } else { error("bad dnskey remaining lines"); } } } sub parse_ds { # got more data if (/\)\s*$/) { if (/\G\s*(\S*)\s*\)\s*$/gc) { $ds->{'digest'} .= $1; $ds->{'digest'} = lc($ds->{'digest'}); # we're done $parse = \&parse_line; $ds->{'digestbin'} = pack("H*",$ds->{'digest'}); push @zone, $ds; $ds = undef; } else { error("bad ds last line"); } } else { if (/\G\s*(\S+)\s*$/gc) { $ds->{'digest'} .= $1; } else { error("bad ds remaining lines"); } } } sub parse_tlsa { # got more data if (/\)\s*$/) { while (/\G\s*([0-9A-Za-z]+)\s*/gc) { $tlsa->{'cert'} .= $1; } if (/\G\s*\)$/gc) { $tlsa->{'cert'} = lc($tlsa->{'cert'}); # we're done $parse = \&parse_line; $tlsa->{'certbin'} = pack("H*",$tlsa->{'cert'}); push @zone, $tlsa; $tlsa = undef; } else { error("bad tlsa last line: $_"); } } else { if (/\G\s*(\S+)\s*$/gc) { $tlsa->{'cert'} .= $1; } else { error("bad tlsa remaining lines"); } } } sub parse_nsec3 { #got more data if ( /\G\s*([A-Z0-9]{32})\s*(\))?/gc) { my $nxthash = $1; my $binhash = MIME::Base32::decode(uc($nxthash)); $nsec3->{ 'hnxtname' } = $nxthash; $nsec3->{ 'hnxtnamebin' } = $binhash; $nsec3->{ 'hashlength' } = length( $binhash ); if ( defined($2) && $2 eq ')' ) { # Was RR terminated ? push @zone, $nsec3; # we're done $parse = \&parse_line; $nsec3 = undef; } } elsif ( /\G\s+$/gc ) { # Empty line } elsif ( /\G\s*((\w+\s+)*)\)\s*$/) { my $typelist = $1; $typelist = join(" ",sort split(/\s+/,$typelist)); $nsec3->{ 'typelist' } = $typelist; $nsec3->{ 'typebm' } = Net::DNS::RR::NSEC::_typearray2typebm(split(/\s+/,$typelist)); push @zone, $nsec3; # we're done $parse = \&parse_line; $nsec3 = undef; } else { error( "bad NSEC3 continuation lines ($_)" ); } } sub parse_soa_number { error("parse_soa_number: internal error, no \$soa") unless $soa; if ($soa->{breakable}) { if (/\G[ \t]*($pat_ttl)$pat_skip$/igc) { $soa->{$soa->{nextkey}} = ttl_or_serial($1); } elsif (/\G$pat_skip$/gc) { return; } elsif (/\G[ \t]*($pat_ttl)\b/igc) { $soa->{$soa->{nextkey}} = ttl_or_serial($1); } else { error("expected valid $soa->{nextkey}"); } } else { if (/\G[ \t]+($pat_ttl)/igc) { $soa->{$soa->{nextkey}} = ttl_or_serial($1); } elsif (/\G[ \t]*\($pat_skip$/igc) { $soa->{breakable} = 1; return; } elsif (/\G[ \t]*\(/igc) { $soa->{breakable} = 1; $parse->(); return; } else { error("expected valid $soa->{nextkey}"); } } if ($soa->{nextkey} eq "serial") { $soa->{nextkey} = "refresh"; } elsif ($soa->{nextkey} eq "refresh") { $soa->{nextkey} = "retry"; } elsif ($soa->{nextkey} eq "retry") { $soa->{nextkey} = "expire"; } elsif ($soa->{nextkey} eq "expire") { $soa->{nextkey} = "minimum"; } elsif ($soa->{nextkey} eq "minimum") { $minimum = $soa->{minimum}; $default_ttl = $minimum if $default_ttl <= 0; $parse = $soa->{breakable} ? \&parse_close : \&parse_line; if (!$soa->{breakable} && !/\G$pat_skip$/gc) { error("unexpected trailing garbage after Minimum"); } delete $soa->{nextkey}; delete $soa->{breakable}; $soa->{mname} .= $origin unless ($soa->{mname} =~ /\.$/); $soa->{rname} .= $origin unless ($soa->{rname} =~ /\.$/); $soa->{mname} =~ s/\.$//; $soa->{rname} =~ s/\.$//; $soa->{Lines} = $ln - $soa->{Line} + 1; push @zone, $soa; $soa = undef; return if $parse == \&parse_line; } else { error("parse_soa_number: internal error, bad {nextkey}") unless $soa; } $parse->(); } sub parse_close { if (/\G[ \t]*\)$pat_skip$/igc) { $zone[-1]->{Lines} = $ln - $zone[-1]->{Line} + 1; $parse = \&parse_line; return; } elsif (/\G$pat_skip$/gc) { return; } else { error("expected closing block \")\""); } } sub debug { print STDERR @_; } sub ttl_fromtext # zero == invalid value { my ($t) = @_; my $ttl = 0; if ($t =~ /^\d+$/) { $ttl = $t; } elsif ($t =~ /^(?:\d+[WDHMS])+$/i) { my %ttl; $ttl{W} ||= 0; $ttl{D} ||= 0; $ttl{H} ||= 0; $ttl{M} ||= 0; $ttl{S} ||= 0; while ($t =~ /(\d+)([WDHMS])/gi) { $ttl{uc($2)} += $1; } $ttl = $ttl{S} + 60*($ttl{M} + 60*($ttl{H} + 24*($ttl{D} + 7*$ttl{W}))); } return $ttl; } 1; __END__ =head1 NAME Net::DNS::ZoneFile::Fast -- parse BIND8/9 zone files =head1 SYNOPSIS use Net::DNS::ZoneFile::Fast; my $rr = Net::DNS::ZoneFile::Fast::parse($zone_text); =head1 DESCRIPTION The Net::DNS::ZoneFile::Fast module provides an ability to parse zone files that BIND8 and BIND9 use, fast. Currently it provides a single function, I, which returns a reference to an array of traditional I objects, so that no new API has to be learned in order to manipulate zone records. Great care was taken to ensure that I does its job as fast as possible, so it is interesting to use this module to parse huge zones. As an example datapoint, it takes less than 5 seconds to parse a 2.2 MB zone with about 72000 records on an Athlon XP 2600+ box. On the other hand, it is likely that I objects that I returns are going to be further processed. To make it easier to link any record back to the zone file (say, to report a logical error like infamous `CNAME and other data' back to the user, or to do a zone file modification), I inserts line numbering information into I objects. The module currently understands: =over 4 =item B<$GENERATE> directive =item B<$ORIGIN> directive =item B<$TTL> directive =item B<$INCLUDE> directive (only while processing files or filehandles) =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =item B records =back =head2 Non-standard third-party modules I. =head2 Exports None. =head2 Subroutines =over 4 =item I Parses zone data and returns a reference to an array of I objects if successful. Takes the following named (no pun intended) parameters: =over 4 =item B A semi-mandatory parameter, textual contents of the zone to be parsed. =item B A semi-mandatory parameter, a file handle from which zone contents can be read for parsing. =item B A semi-mandatory parameter, a file name with the zone to parse. =item B An optional parameter specifying zone origin. The default is ".". A trailing "." is appended if necessary. =item B An optional parameter, user-defined error handler. If specified, it must be a subroutine reference, which will be called on any error. This subroutine will be passed two parameters: a line number in the zone, where the error occurred, and the error description. =item B By default, I throws an exception on any error. Set this optional parameter to a true value to avoid this. The default is false, unless B is also specified, in which case it is true. =item B An optional parameter. By default, any $INCLUDE directives encountered will be tested for existence and readability. If the base path of the included filename is not your current working directory, this test will fail. Set the B to the same as your named.conf file to avoid this failure. =item B An optional parameter. By default, on any error, the error description is printed via warn(). Set B to a true value if you don't want this. The default is false, unless B is also specified, in which case it is true. =item B An optional parameter. If set to true, will produce some debug printing. You probably don't want to use that. =back One of B, B, B must be specified. If more than one is specified at the same time, B takes precedence over B, which takes precedence over B. As a special case, if I is called with a single, unnamed parameter, it is assumed to be a zone text. If I is unsuccessful, and does not throw an exception (because either B or B was specified), I returns undef. The returned I are normal in every respect, except that each of them has two extra keys, Line and Lines, which correspondingly are the line number in the zone text where the record starts, and the number of lines the record spans. This information can be accessed either via hash lookup (C<$rr-E{Line}>), or via an accessor method (C<$rr-ELine>). =back =head1 BUGS The I subroutine is not re-entrant, and it probably will never be. There is also no guarantee that I will successfully parse every zone parsable by BIND, and no guarantee that BIND will parse every zone parsable by I. That said, I appears to do the right thing on around 50000 real life zones I tested it with. SOA serial numbers with a decimal point are not supported (they're not a legal zonefile contstruct, although bind8 supported them. Even bind is dropping support for them in future releases). =head1 COPYRIGHT AND LICENSE Copyright 2003 by Anton Berezin and catpipe Systems ApS "THE BEER-WARE LICENSE" (Revision 42) wrote this module. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. Anton Berezin Copyright (c) 2004-2011 SPARTA, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of SPARTA, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright (c) 2013-2013 PARSONS, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of SPARTA, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =head1 CREDITS Anton Berezin created the versions up until 0.5. Wes Hardaker at Sparta implemented the DNSSEC patches and took over maintenance of the module from 0.6 onward. Anton's original CREDITS section: This module was largely inspired by the I module by Luis E. Munoz. Many thanks to Phil Regnauld and Luis E. Munoz for discussions. =head1 SEE ALSO http://www.dnssec-tools.org/, Net::DNS(3), Net::DNS::RR(3), Net::DNS::SEC(3) =cut Net-DNS-ZoneFile-Fast-1.24/META.yml0000664000237200023720000000065112402370347016621 0ustar hardakerhardaker--- abstract: unknown author: - unknown build_requires: {} dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.64, CPAN::Meta::Converter version 2.120921' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Net-DNS-ZoneFile-Fast no_index: directory: - t - inc resources: repository: https://dnssec-tools.org/svn/dnssec-tools/ version: 1.24 Net-DNS-ZoneFile-Fast-1.24/META.json0000664000237200023720000000132112402370347016764 0ustar hardakerhardaker{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.64, CPAN::Meta::Converter version 2.120921", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Net-DNS-ZoneFile-Fast", "no_index" : { "directory" : [ "t", "inc" ] }, "release_status" : "stable", "resources" : { "repository" : { "type" : "svn", "url" : "https://dnssec-tools.org/svn/dnssec-tools/", "web" : "https://dnssec-tools.org/svn/dnssec-tools/" } }, "version" : "1.24" } Net-DNS-ZoneFile-Fast-1.24/Changes0000644000237200000120000000261311176220213016142 0ustar hardakerwheelRevision history for Perl extension Net::DNS::ZoneFile::Fast. 1.11 Wed Apr 29 2009 - Patch from Chris Adams to allow for negative offsets in $GENERATE 1.1 Tue Jan 27 2009 - Expanded SSHFP formatting support - NSEC3 and NSEC3PARAM support - fix issues with recent changes in Net::DNS 1.01 Tue Oct 7 2008 - Many many fixes, improvements, and even releases since the last Changes listing here. whoops. 0.6.1 Sat Apr 8 21:19:21 PDT 2006 - bug fix to match current Net::DNS::SEC APIs we shouldn't be using. 0.6 Mon Nov 14 16:35:00 PST 2005 - added support for DNSSEC records (RRSIG, DNSKEY, DS, NSEC, RRSIG) - added support for AFSDB records - added support for parsing included files (commonly used in DNSSEC) 0.5 Thu Apr 15 23:17:56 CEST 2004 - add support for RP "Responsible Person" records 0.4 Fri Mar 26 11:06:21 CET 2004 - add dependency from Net::DNS 0.3 Wed Mar 24 20:01:18 CET 2004 - rename the module from Net::DNS::Zone to Net::DNS::ZoneFile::Fast (after consulting with Net::DNS::ZoneFile's author) - fix a small bug with TXT records parsing 0.2 Mon Nov 17 14:21:33 CET 2003 - rename the module from Net::DNS::ZoneParse to Net::DNS::Zone - add documentation - add soft_errors parameter and fix die/fatal/warn/quiet logic - small fixes 0.01 Mon Oct 13 20:38:04 CET 2003 - original version Net-DNS-ZoneFile-Fast-1.24/README0000644000237200023720000000271611677177353016251 0ustar hardakerhardakerUsage: use Net::DNS::ZoneFile::Fast; my $rr = Net::DNS::ZoneFile::Fast::parse($zone_text); Description: The Net::DNS::ZoneFile::Fast module provides an ability to parse zone files that BIND8 and BIND9 use, fast. Currently it provides a single function, parse(), which returns a reference to an array of traditional Net::DNS::RR objects, so that no new API has to be learned in order to manipulate zone records. Great care was taken to ensure that parse() does its job as fast as possible, so it is interesting to use this module to parse huge zones. As an example datapoint, it takes less than 5 seconds to parse a 2.2 MB zone with about 72000 records on an Athlon XP 2600+ box. On the other hand, it is likely that Net::DNS::RR objects that parse() returns are going to be further processed. To make it easier to link any record back to the zone file (say, to report a logical error like infamous `CNAME and other data' back to the user, or to do a zone file modification), parse() inserts line numbering information into Net::DNS::RR objects. Important Note about DNSSEC: If you try to load a ZoneFile with DNSSEC records in it *and* you haven't installed all the other necessary perl modules require to support the Net::DNS::SEC module set, you will get bad results. And, worse they'll be cryptic. A common problem is failing to install the MIME::Base32 and MIME::Base64 perl modules in addition to the Net::DNS::SEC modules. Net-DNS-ZoneFile-Fast-1.24/Makefile.PL0000644000237200023720000000136012402367562017323 0ustar hardakerhardakeruse ExtUtils::MakeMaker; WriteMakefile( NAME => 'Net::DNS::ZoneFile::Fast', VERSION_FROM => 'Fast.pm', # Net::DNS{,::SEC} keeps changing internals by adding and removing # dots in certain fields, etc... That means we need to require # certain versions explicitly. PREREQ_PM => { 'Net::DNS' => 0.65, 'Net::DNS::SEC' => 0.15, 'MIME::Base64' => 0, 'IO::File' => 0, }, META_MERGE => { 'meta-spec' => { version => 2 }, resources => { repository => { type => 'svn', url => 'https://dnssec-tools.org/svn/dnssec-tools/', web => 'https://dnssec-tools.org/svn/dnssec-tools/', }, }, }, );