#!/usr/bin/perl -w use strict; use Fcntl qw(:DEFAULT :flock); use Symbol qw(gensym); use Getopt::Std; use IO::File; use File::Basename; use Cwd; use vars qw/$opt_a $opt_b $opt_d $opt_e $opt_h $opt_m $opt_o $opt_q/; my $zonefile_suffix_default = "zone"; my $outfile_default = "data_dns"; my $out_data_file = "nsdata.txt"; ## - hashes to store the records at all, but also ## - domains, maildomains and the defined hosts ## - my %doms = (); my %maildoms = (); my %hosts = () ; my %records = () ; ## - filehandle ## - my $fh = new IO::File ; getopts('ab:d:e:hmo:q') || &usage(); &usage if defined $opt_h ; my $i; if (! defined $opt_q) { print "\n command invoked: $0"; foreach $i (@ARGV) { print " $i"; } print "\n\n"; } my $backup = defined($opt_b) ? $opt_b : 1; if ( $backup != 1 && $backup != 0 ) { print "\n wrong value for flag -b : $opt_b"; &usage; } exit ( &main(@ARGV) ); sub main { my $infile = shift || return usage(); -f $infile or die "$infile: $!"; my $base_infile = basename($infile); my ( $line, $filename, $bind_filename); my ( @lines, @data_file_lines, @single_file_lines); my ( @domains, @maildomains, @hostlist, @records ); my ( $bind_filename, $bind_named_conf_local_master_filename, $bind_named_conf_local_slave_filename ); my ( @bind_file_lines , @bind_named_conf_local_master_lines, @bind_named_conf_local_slave_lines); my ( $extension, $outdir, $outfile, $multiple_files, $add_files ); ## - look at the programm parameters ## - $extension = defined($opt_e) ? $opt_e : $zonefile_suffix_default; $outdir = defined($opt_d) ? $opt_d : "./"; ## - exit if $outdir does not exist ## - -d $outdir or die "$outdir: $!"; ## - craete $outdir/bind if mot exists ## - -d "$outdir/bind" or mkdir("$outdir/bind", 0755); -d "$outdir/bind/conf" or mkdir("$outdir/bind/conf", 0755); $bind_named_conf_local_master_filename = "$outdir/bind/conf/named.conf.local.master"; $bind_named_conf_local_slave_filename = "$outdir/bind/conf/named.conf.local.slave"; ## - like to work with the realpath ## - $outdir = Cwd::abs_path($outdir); ## $outdir =~ s#^(.*)/$#$1#; $outfile = defined($opt_o) ? $opt_o : $outfile_default; $outfile = $outdir . "/" . $outfile; $multiple_files = defined($opt_m) ? 1 : 0; $add_files = defined($opt_a) ? 1 : 0; ## - Datei einlesen ## - if ( $fh->open($infile) ) { flock( $fh, LOCK_SH ); chomp (@lines = <$fh>); flock( $fh, LOCK_UN ); $fh->close(); } else { die "can't open $! for read"; } ## oder so.. ## - ## open( $fh ,$infile ) or die "can't open $! for read" ; ## flock( $fh, LOCK_SH ); ## @lines = <$fh>; ## flock( $fh, LOCK_UN ); ## close ( $fh ); ## - sichere original file ## - &write_file($outdir . "/" . $base_infile,@lines); ## - first we push into hashes, so we can sort ## - afterwords ## - foreach (@lines) { ## chomp ; ## - ignore emty lines ## - next if /^\s*$/o ; ## - ignore comments ## - next if /^#/o ; $records{$_} = 1 ; if ( /^([\.|\&|Z])([^:]+)(.*)$/o ) { $doms{$2} = 1 ; } ## - maildomain ? ## - if ( /^(\@)([^:]+)(.*)/o) { $maildoms{$2} = 1; } ## - host definition ? ## - if ( /^(\+)([^:]+)(.*)/o) { $hosts{$2} = 1; } } ## - Now sort and put into the considered arrays ## - @domains = sort ( keys %doms ) ; #@domains = keys %doms ; @maildomains = sort ( keys %maildoms ) ; @hostlist = sort ( keys %hosts ) ; @records = sort ( sort_records keys %records); ## - schreibe domainliste, maildomainliste, hostliste, records ## - if ( $add_files ) { &write_file($outdir . "/domains.lst",@domains); &write_file($outdir . "/maildomains.lst",@maildomains); &write_file($outdir . "/hostlist.lst",@hostlist); &write_file($outdir . "/records.lst",@records); } my $_domain =""; my $_hostname; my ( $_ipv6_tynidns, $_ipv6_bind, @_ipv6 ) ; my $_ipv4 ; my ( $_mx_host , $_mx_prio ); my $_nameserver ; my $_type; #my $_filename = 0; my $_blank_hostname ; my $_no_blank_hostname = 20 ; my $_blank_soa_comment; my $_no_blank_soa_comment = 12; foreach ( @records ) { /(.)([^:]+).*/o ; if ( $_domain eq get_domain($2) ) { if ( $_type ne $1 ) { if ( $multiple_files ) { push ( @single_file_lines, get_record_comment($1) ); } else { push ( @data_file_lines, get_record_comment($1) ); } push (@bind_file_lines, get_record_comment_bind($1) ); } if ( $multiple_files ) { push ( @single_file_lines, $_ ); } else { push ( @data_file_lines, $_ ); } if ( "$1" eq "&" ) { /&([^:]+):([^:]*):([^:]+):([^:]+).*/o ; $_blank_hostname = ""; $_hostname = $1; $_nameserver = $3 ; $_hostname =~ s/\.?$_domain// ; if ( "$_hostname" eq "" ) { for ( my $i=0; $i < ($_no_blank_hostname - 1) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } push (@bind_file_lines, "\@${_blank_hostname}IN NS ${_nameserver}."); } else { if ( length($_hostname) < $_no_blank_hostname ) { for ( my $i=0; $i < ($_no_blank_hostname - length($_hostname)) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } } else { $_blank_hostname = " " ; } push (@bind_file_lines, "$_hostname${_blank_hostname}IN NS ${_nameserver}."); } } elsif ( "$1" eq "\@" ) { /\@([^:]+):([^:]*):([^:]+):([^:]+):([^:]+).*/o ; $_blank_hostname = ""; $_hostname = $1; $_mx_host = $3; $_mx_prio = $4; $_hostname =~ s/\.?$_domain// ; if ( "$_hostname" eq "" ) { for ( my $i=0; $i < ($_no_blank_hostname - 1) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } push (@bind_file_lines, "\@${_blank_hostname}IN MX $_mx_prio $_mx_host."); } else { if ( length($_hostname) < $_no_blank_hostname ) { for ( my $i=0; $i < ($_no_blank_hostname - length($_hostname)) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } } else { $_blank_hostname = " " ; } push (@bind_file_lines, "${_hostname}${_blank_hostname}IN MX $_mx_prio $_mx_host."); } } elsif ( "$1" eq "+" ) { /\+([^:]+):([^:]+):([^:]+).*/o ; $_blank_hostname = ""; if ( "$1" eq "$_domain" ) { for ( my $i=0; $i < ($_no_blank_hostname - 1) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } push (@bind_file_lines, "\@${_blank_hostname}IN A $2"); } else { $_hostname = $1; $_ipv4 = $2 ; $_hostname =~ s/\.$_domain// ; if ( length($_hostname) < $_no_blank_hostname ) { for ( my $i=0; $i < ($_no_blank_hostname - length($_hostname)) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } } else { $_blank_hostname = " " ; } push (@bind_file_lines, "${_hostname}${_blank_hostname}IN A $_ipv4"); } } elsif ( "$1" eq "3" ) { /\ ?3([^:]+):([^:]+):([^:]+).*/o ; $_blank_hostname = ""; $_hostname = $1 ; $_ipv6_tynidns = $2; $_ipv6_bind = ""; for(my $i=0; $i<31; $i = $i +4) { $_ipv6_bind = $_ipv6_bind . substr($_ipv6_tynidns,$i,4); if ( $i < 28 ) { $_ipv6_bind = $_ipv6_bind .":" ; } } if ( "$1" eq "$_domain" ) { $_ipv6_bind =~ s/:0{1,3}/:/g ; $_ipv6_bind = reverse($_ipv6_bind); $_ipv6_bind =~ s/:0:(0:)+/::/ ; $_ipv6_bind = reverse($_ipv6_bind); for ( my $i=0; $i < ($_no_blank_hostname - 1) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } push (@bind_file_lines, "\@${_blank_hostname}IN AAAA $_ipv6_bind"); } else { $_hostname =~ s/\.$_domain// ; $_ipv6_bind =~ s/:0{1,3}/:/g ; $_ipv6_bind = reverse($_ipv6_bind); $_ipv6_bind =~ s/:0:(0:)+/::/ ; $_ipv6_bind = reverse($_ipv6_bind); if ( length($_hostname) < $_no_blank_hostname ) { for ( my $i=0; $i < ($_no_blank_hostname - length($_hostname)) ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } } else { $_blank_hostname = " " ; } push (@bind_file_lines, "${_hostname}${_blank_hostname}IN AAAA $_ipv6_bind"); } } } else { &write_file($filename,@single_file_lines) if ($filename); @single_file_lines = (); $_type = ""; $_domain = get_domain($2); if ( $multiple_files ) { $filename = $outdir . "/" . $_domain . ".file"; push ( @single_file_lines, "## - domain: $_domain"); push ( @single_file_lines, "## - "); push ( @single_file_lines, get_record_comment($1) ); push ( @single_file_lines, $_ ); } else { push ( @data_file_lines, "\n" ); push ( @data_file_lines, "## - domain: $_domain"); push ( @data_file_lines, "## - "); push ( @data_file_lines, get_record_comment($1) ); push ( @data_file_lines, $_ ); } &write_file($bind_filename, @bind_file_lines) if ($bind_filename);; @bind_file_lines =(); $bind_filename = $outdir . "/bind/" . $_domain . ".zone"; push (@bind_file_lines, "; - domain: $_domain"); push (@bind_file_lines, "; -"); push (@bind_file_lines, "\$TTL 43200"); $_blank_hostname = "" ; for ( my $i=0; $i < $_no_blank_hostname ; $i++ ) { $_blank_hostname = $_blank_hostname . " "; } if ( "$1" eq "Z" ) { /Z([^:]+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]+).*/o ; push (@bind_file_lines, "\@ $8 IN SOA $2. $3 ("); $_blank_soa_comment = "" ; for ( my $i=0; $i < ($_no_blank_soa_comment - length($4)) ; $i++ ) { $_blank_soa_comment= $_blank_soa_comment . " "; } push (@bind_file_lines, "${_blank_hostname}$4$_blank_soa_comment; serial"); $_blank_soa_comment = "" ; for ( my $i=0; $i < ($_no_blank_soa_comment - length($5)) ; $i++ ) { $_blank_soa_comment= $_blank_soa_comment . " "; } push (@bind_file_lines, "${_blank_hostname}$5$_blank_soa_comment; refresh"); $_blank_soa_comment = "" ; for ( my $i=0; $i < ($_no_blank_soa_comment - length($6)) ; $i++ ) { $_blank_soa_comment= $_blank_soa_comment . " "; } push (@bind_file_lines, "${_blank_hostname}$6$_blank_soa_comment; retry"); $_blank_soa_comment = "" ; for ( my $i=0; $i < ($_no_blank_soa_comment - length($7)) ; $i++ ) { $_blank_soa_comment= $_blank_soa_comment . " "; } push (@bind_file_lines, "${_blank_hostname}$7$_blank_soa_comment; expire"); $_blank_soa_comment = "" ; for ( my $i=0; $i < ($_no_blank_soa_comment - length($8)) ; $i++ ) { $_blank_soa_comment= $_blank_soa_comment . " "; } push (@bind_file_lines, "${_blank_hostname}$8$_blank_soa_comment; minimum"); push (@bind_file_lines, ")"); } push (@bind_named_conf_local_master_lines, ""); push (@bind_named_conf_local_master_lines, "zone \"$_domain\" {"); push (@bind_named_conf_local_master_lines, " type master;"); push (@bind_named_conf_local_master_lines, " file \"/etc/bind/master/$_domain.zone\";"); push (@bind_named_conf_local_master_lines, "};"); push (@bind_named_conf_local_slave_lines, ""); push (@bind_named_conf_local_slave_lines, "zone \"$_domain\" {"); push (@bind_named_conf_local_slave_lines, " type slave;"); push (@bind_named_conf_local_slave_lines, " file \"/etc/bind/slave/$_domain.zone\";"); push (@bind_named_conf_local_slave_lines, " //allow-query { any; };"); push (@bind_named_conf_local_slave_lines, " //allow-transfer {none;};"); push (@bind_named_conf_local_slave_lines, " masters {"); push (@bind_named_conf_local_slave_lines, " 83.223.90.92;"); push (@bind_named_conf_local_slave_lines, " };"); push (@bind_named_conf_local_slave_lines, "};"); } $_type = $1; } if ( $multiple_files ) { ## - letztes file muss noch geschrieben werdem ## - &write_file($filename, @single_file_lines); } else { ## - schreibe alles in ein file ## - &write_file($outfile, @data_file_lines); } &write_file($bind_filename, @bind_file_lines); &write_file($bind_named_conf_local_master_filename, @bind_named_conf_local_master_lines); &write_file($bind_named_conf_local_slave_filename, @bind_named_conf_local_slave_lines); exit 0 } sub get_domain { my $record = shift ; if ( $doms{$record} ) { return $record ; } else { while ( $record =~ /([^\.]*).(.*)/o ) { if ( $doms{$2} ) { return $2; } $record = $2; } } return 1 ; } sub get_record_comment { my $directive_type = shift; my $comment; SWITCH : { $1 eq "Z" and $comment = "## - SOA" , last SWITCH; $1 eq "." and $comment = "## - SOA, NS, A" , last SWITCH; $1 eq "&" and $comment = "## - NS, A" , last SWITCH; $1 eq "@" and $comment = "## - MX, A" , last SWITCH; $1 eq "=" and $comment = "## - A, PTR" , last SWITCH; $1 eq "+" and $comment = "## - A" , last SWITCH; $1 eq "C" and $comment = "## - CNAME" , last SWITCH; $1 eq "^" and $comment = "## - PTR" , last SWITCH; $1 eq "'" and $comment = "## - TEXT" , last SWITCH; $1 eq "3" and $comment = "## - AAAA" , last SWITCH; $1 eq "6" and $comment = "## - AAAA, PTR" , last SWITCH; $comment = "## - unknown record"; } return $comment; } sub get_record_comment_bind { my $directive_type = shift; my $comment; SWITCH : { $1 eq "&" and $comment = "\n;\n; NS Records\n;\n" , last SWITCH; $1 eq "@" and $comment = "\n;\n; MX Records\n;\n" , last SWITCH; $1 eq "+" and $comment = "\n;\n; IPv4 Hostaddresses\n;\n" , last SWITCH; $1 eq "C" and $comment = "\n;\n; CNAME Records\n;\n" , last SWITCH; $1 eq "3" and $comment = "\n;\n; IPv6 Hostaddresses\n;\n" , last SWITCH; $comment = "\n;\n; unknown record\n;\n"; } return $comment; } # Datei schreiben # sub write_file { my $outfile = shift ; my @lines = @_ ; backup($outfile) if ( -f $outfile ); if (! defined $opt_q) { print "\twrite $outfile ..\n"; } $fh = new IO::File "> $outfile"; if (defined $fh) { foreach (@lines) { print $fh $_ . "\n" ; } $fh->close(); } ## - oder auch so.. ## - ## open ( $fh , "> $outfile") or die "can't open $! for write" ; ## print $fh @lines ; ## close ($fh); } # # - backup # sub backup { my $filename = shift ; # Sicherungskopie erstellen # if ($backup) { OUTER: for ( my $i = 0 ; $i < 10 ; $i++ ) { for ( my $j = 0 ; $j < 10 ; $j++ ) { my $backupfile = $filename . ".$i$j" ; if ( -f $backupfile || -d $backupfile || -l $backupfile || -p $backupfile || -c $backupfile || -b $backupfile || -S $backupfile ) { next ; } else { my $retval = system( "cp -f $filename $backupfile") ; if ($retval != 0) { die "can't store backupfile $backupfile" } if (! defined $opt_q) { print "\n\tbackupfile stored in $backupfile\n"; } last OUTER ; } } } # ende OUTER : for ( ... } # ende if } # end backup() sub sort_records { my ( $rval1, $rval2, $domain_a, $domain_b, $directive_a, $directive_b ); $a =~ /(.)([^:]+).*/; ## - get directive ## - SWITCH : { $1 eq "Z" and $directive_a = 10 , last SWITCH; $1 eq "." and $directive_a = 20 , last SWITCH; $1 eq "&" and $directive_a = 30 , last SWITCH; $1 eq "@" and $directive_a = 40 , last SWITCH; $1 eq "=" and $directive_a = 50 , last SWITCH; $1 eq "+" and $directive_a = 60 , last SWITCH; $1 eq "C" and $directive_a = 70 , last SWITCH; $1 eq "^" and $directive_a = 80 , last SWITCH; $1 eq "'" and $directive_a = 90 , last SWITCH; $1 eq "3" and $directive_a = 100, last SWITCH; $1 eq "6" and $directive_a = 110, last SWITCH; $directive_a = 999; } ## - get domain.. ## - $domain_a = get_domain($2); $b =~ /(.)([^:]+).*/; ## - get directive ## - SWITCH : { $1 eq "Z" and $directive_b = 10 , last SWITCH; $1 eq "." and $directive_b = 20 , last SWITCH; $1 eq "&" and $directive_b = 30 , last SWITCH; $1 eq "@" and $directive_b = 40 , last SWITCH; $1 eq "=" and $directive_b = 50 , last SWITCH; $1 eq "+" and $directive_b = 60 , last SWITCH; $1 eq "C" and $directive_b = 70 , last SWITCH; $1 eq "^" and $directive_b = 80 , last SWITCH; $1 eq "'" and $directive_b = 90 , last SWITCH; $1 eq "3" and $directive_b = 100, last SWITCH; $1 eq "6" and $directive_b = 110, last SWITCH; $directive_b = 999; } ## - get domain.. ## - $domain_b = get_domain($2); $rval1 = $domain_a cmp $domain_b ; if ( $rval1 == 0 ) { $rval2 = $directive_a <=> $directive_b; if ( $rval2 == 0 ) { return $a cmp $b; } else { return $rval2; } } else { return $rval1; } } # # - usage # sub usage { my $file = basename($0); $file =~ s#.*/([^/]+)$#$1# ; print < This script concatinates all zonefiles with a certain fileextension of a given directory. This script takes all directives from a given djb tinydns-data-file, sorts them and put the sortet list back into one data-file or, if option "m" is set into one file for each domain, named by the concerning domain. A copy of the (original) inputfile will be written out in . Control Options: -a The following addition files be written: /domains.lst.......: alphabetical list of domains /maildomains.lst...: alphabetical list of domains with MX entry /hostlist.lst......: alphabetical list of all hostentries (A - Record) /records.lst.......: all records sorted by domain but without any comment or emty line -b <0|1> a value of "1" means: befor writing a file backup existing one; dont backup if "0". default value is "1" -d directory where the outfile(s) are written. defaults to working directory -e write data-files with extension . defaults to "$zonefile_suffix_default". only affected if option -m is set -h prints this helpcontents -m write one file for each domain -o name of outputfile. defaults to "$outfile_default". only affect if not -m is set -q silent output ENDE exit 1 ; }