#!/usr/bin/perl # # zcat.pl, a tinydns zone file concatenator # based loosely on axfr by Russ Nelson # version 1.0 - 20010228 - Dan Peterson # # You can do whatever you want with this file. I'd appreciate feedback, # though. # # Some differences from axfr: # --------------------------- # # zcat.pl does not support zone transfers # zcat.pl supports extra fields in "." lines it rewrites # for instance: # .foo.dom:1.2.3.4:a:3600:4000000012345678:in # produces: # Zfoo.dom:hostmaster.foo.dom::::::3600:4000000012345678:in # &foo.dom:1.2.3.4:a:3600:4000000012345678:in # ...but only if $fixserial is 1. I personally don't care about my serial # numbers. # zcat.pl uses filename.z instead of primary.filename; this is much more # tab-completion friendly # zcat.pl keeps track of files it's read data from in the past; if it sees a # new file, it builds. if it notices that a file went away, it builds. # zcat.pl builds if data's mtime is greater than data.cdb's mtime # zcat.pl outputs records to the data file in the same order it reads them # from the zone files. axfr sorts records alphabetically before outputting # them to the data file, sometimes causing SOA records to have the # incorrect nameserver listed as the master server (if the true master # server is alphabetically greater than any of the secondaries) # # Here's the Makefile I use with zcat.pl: # # it: # @echo "===> Combine z files" # @perl zcat.pl # # data.cdb: data # @echo "===> Rebuild data.cdb" # /usr/local/bin/tinydns-data $verbose = 1; $fixserial = 0; @paths = ("./zones"); $ntime = $dtime = (stat("data"))[9]; $ctime = (stat("data.cdb"))[9]; $build = 0; $rec = 0; foreach $path (@paths) { opendir(DIR,$path) || die; while ($f = readdir(DIR)) { next unless $f =~ /\.z$/; next unless -f "$path/$f"; $ttime = (stat("$path/$f"))[9]; if ($ttime > $ntime) { print "$0: $path/$f: $ttime > $ntime\n" if $verbose; $ntime = $ttime; } open(F,"<$path/$f") || die; while () { chomp; next if /^(\s+)?$/; next if /^#/; if (/^\.(.*?):/ && $fixserial) { $zone = $1; @d = split /:/; if ($d[2] !~ /\./) { $d[2] = "$d[2].ns.$zone"; } @z = ("Z$zone",$d[2],"hostmaster.$zone",$ttime,undef,undef,undef,undef,$d[3],$d[4],$d[5]); $z = join(":",@z); $z =~ s/:+$//; if (!defined($data{$z})) { $data{$z} = $rec++; } @n = ("&$zone",$d[1],$d[2],$d[3],$d[4],$d[5]); $n = join(":",@n); $n =~ s/:+$//; if (!defined($data{$n})) { $data{$n} = $rec++; } } else { if (!defined($data{$_})) { $data{$_} = $rec++; } } } close(F) || die; $files{"$path/$f"} = 1; } closedir(DIR) || die; } if (!open(ZLIST,") { chomp; $zlist{$_} = 1; } close(ZLIST) || die; foreach (sort keys %zlist) { if (!defined($files{$_})) { print "$0: $_ not in files\n" if $verbose; $build = 1; } } foreach (sort keys %files) { if (!defined($zlist{$_})) { print "$0: $_ not in zlist\n" if $verbose; $build = 1; } } } if ($ntime == $dtime && $ctime >= $dtime && !$build) { print "$0: nothing to do\n" if $verbose; exit; } open(ZLIST,">zlist.tmp") || die; foreach (sort keys %files) { print ZLIST "$_\n"; } close(ZLIST) || die; rename("zlist.tmp","zlist") || die; open(DATA,">data") || die; foreach (sort {$data{$a} <=> $data{$b}} keys %data) { print DATA "$_\n"; } close(DATA) || die; system("make data.cdb");