ACC SHELL
#!/usr/bin/perl -w
#
# script for dumping LDAP data into zone files with
# keeping dhcpd added hosts.
#
# LDAP zones that are not existing in /etc/named.conf
# are created as static zones there. The zone files are
# created too under $namedDir/master/$zonename
#
# Zones that are existing as file but not in LDAP are
# dropped from /etc/named.conf and the zone files are
# deleted
#
# The nameserver will not be reloaded. You have to do this
# on your own.
#
# if you have dynamic zones, you need the nsupdate binary from
# the bind-utils package.
#
# Uwe Gansert ug@suse.de
# Copyright 2004, Novell, Inc. All rights reserved.
#
use strict;
eval("use Net::LDAP;");
if( $@ ) {
print STDERR "\n Loading the perl module Net::LDAP failed\n";
print STDERR " please install the perl-ldap RPM.\n\n";
print STDERR "$@\n";
exit(101);
}
# $zones{$zonename} = { allow-update => keyname, type => master, file => master/bal.de }
my %zones;
my $DEBUG = 0;
my $DONSUPDATE = 1;
my $DOFILES = 1;
my $dryrun = 0;
my $server = "127.0.0.1";
my $ldapServer = undef;
my $ldapPort = 389;
my $ldapBaseDN = undef;
my $ldapBindDN = undef;
my $ldapPassword = undef;
my $namedDir = getNamedDir();
my $help = undef;
my $nsupdate = "/usr/bin/nsupdate";
my $dig = "/usr/bin/dig";
my $base = undef;
my $namedRunning = statusNamed();
my $version = 1.2;
my $keydir = '/etc/named.d/';
die("no namedDir found in /etc/named.conf") unless $namedDir;
use Getopt::Long;
GetOptions (
"z" => \$DEBUG,
"y" => \$dryrun,
"s=s" => \$server,
"l=s" => \$ldapServer,
"p=s" => \$ldapPort,
"b=s" => \$ldapBaseDN,
"D=s" => \$ldapBindDN,
"e=s" => \$base,
"w=s" => \$ldapPassword,
"n=s" => \$nsupdate,
"m=s" => \$dig,
"k=s" => \$keydir,
"h|help" => \$help
);
if( $help ) {
print STDERR "\n";
print STDERR "-s <nameserver> => which nameserver to use [$server]\n";
print STDERR "-l <ldapserver> => which ldap server to use [HOST from /etc/openldap/ldap.conf]\n";
print STDERR "-p <ldapport> => which ldap port to use [PORT from /etc/openldap/ldap.conf]\n";
print STDERR "-b <ldapbasedn> => which ldap basedn to use [BASE from /etc/openldap/ldap.conf]\n";
print STDERR "-w <ldappasswd> => which ldap admin password to use\n";
print STDERR "-D <ldapbinddn> => the ldap bind dn\n";
print STDERR "-e <ldapDNSdn> => the DN where the DNS data is stored [from suseDnsConfiguration object]\n";
print STDERR "-m <dig> => dig binary [$dig]\n";
print STDERR "-k <keydir> => directory that contains the DDNS keys [$keydir]\n";
print STDERR "-n <nsupdate> => nsupdate binary [$nsupdate]\n";
print STDERR "-z => turns on debugging\n";
print STDERR "-y => dry run (nothing will be changed in any files)\n";
print STDERR "-h => this help\n";
print STDERR "\n";
print STDERR "Example:\n";
print STDERR "$0 -s 127.0.0.1 -l localhost -p 389 -D uid=root,dc=suse,dc=de -b dc=suse,dc=de -w MyPaSs -z\n";
print STDERR "$0 -y -z # dry run with debug output\n";
print STDERR "\n";
exit;
}
if( $dryrun ) {
$DONSUPDATE = 0;
$DOFILES = 0;
}
$ldapServer = getLDAPConfigServer() if( not $ldapServer );
$ldapBaseDN = getLDAPConfigBaseDN() if( not $ldapBaseDN );
if( $DEBUG ) {
print STDERR "ldapServer = $ldapServer\n";
print STDERR "ldapPort = $ldapPort\n";
print STDERR "ldapBaseDN = $ldapBaseDN\n";
print STDERR "named dir = $namedDir\n";
print STDERR "named run = $namedRunning\n";
}
my $ldap= Net::LDAP->new($ldapServer, port => $ldapPort, version => 3);
if(!defined $ldap) {
error("can't contact LDAP Server");
exit -1;
}
my $mesg;
if( $ldapBindDN ) {
$mesg=$ldap->bind($ldapBindDN, password=>$ldapPassword);
} else {
$mesg=$ldap->bind();
}
if($mesg->code != 0) {
my $text = "bind failed\n";
error($text);
exit(-1);
}
$base = getLDAPConfigDNSBaseDN() unless( $base );
if( not $base ) {
print STDERR "can't find LDAP DNS configuration base\n";
exit(-100);
}
if( $DEBUG ) {
print STDERR "DNS DN = $base\n\n";
}
# read dynamic and static zoneinfos from named.conf
print STDERR "********** CHECKPOINT : get named.conf zones\n" if($DEBUG);
getZoneTypes();
if( $DEBUG ) {
use Data::Dumper;
print STDERR Data::Dumper->Dump( [ \%zones ] );
}
# drop static zone entries in dynamic zones via nsupdate
# first all forward zones, then all reverse zones
print STDERR "********** CHECKPOINT : drop static zone entries in dynamic zones\n" if($DEBUG);
foreach my $zone ( sortZones() ) {
next unless $namedRunning;
next unless $zones{$zone}->{"allow-update"};
next if( $zones{$zone}->{type} eq 'slave' );
next unless( -f $namedDir."/".$zones{$zone}->{file} );
print STDERR "\tdropStaticZoneEntries($zone)\n" if($DEBUG);
dropStaticZoneEntries($zone);
}
# drop all static zones by "hand"
print STDERR "********** CHECKPOINT : drop all static zones\n" if($DEBUG);
foreach my $zone ( keys(%zones) ) {
next if $zones{$zone}->{"allow-update"};
next if( $zones{$zone}->{type} eq 'slave' );
print STDERR "\tdropZone($zone)\n" if($DEBUG);
dropZone($zone);
delete( $zones{$zone} );
}
# we have just cleaned dynamic zones now.
# it's time to merge the LDAP data now
# drop all dynamic zones that are not in LDAP
print STDERR "********** CHECKPOINT : drop all dynamic zones that are not in LDAP\n" if($DEBUG);
foreach my $zone ( keys(%zones) ) {
next if( $zones{$zone}->{type} eq 'slave' );
$mesg = $ldap->search(
base => $base,
scope => 'sub',
filter => "(&(zoneName=$zone)(objectclass=dNSZone))"
);
if( $mesg->code != 0 ){
error("LDAP: search failed\n");
$ldap->unbind;
exit(-3);
} elsif( $mesg->count == 0) {
# a dynamic zone has been dropped from LDAP but is still
# existing in config. So we remove it now from the config
print STDERR "\tdropZone($zone)\n" if($DEBUG);
dropZone($zone);
delete( $zones{$zone} );
}
}
# create new zones in LDAP as files now
$mesg = $ldap->search(
base => $base,
scope => 'one',
filter => '(objectclass=dNSZone)'
);
if( $mesg->code != 0 ) {
error("LDAP: search failed\n");
$ldap->unbind;
exit(-3);
}
print STDERR "********** CHECKPOINT : create Zones that are new in LDAP\n" if($DEBUG);
foreach my $zone_entry ( $mesg->entries ) {
my $dn = $zone_entry->dn();
my ( $zone ) = $dn =~ /^zoneName=([^,]+)/i;
if( not $namedRunning ) {
createStaticZone( $dn ); # special case. named not running, dyn. zones gets recreated
next;
}
next if( exists($zones{$zone}) and -f $namedDir."/".$zones{$zone}->{file} );
print STDERR "\tcreateStaticZone( $dn )\n" if($DEBUG);
createStaticZone( $dn );
}
# add the new entries for dynamic zones now
print STDERR "********** CHECKPOINT : add the new entries for dynamic zones now\n" if($DEBUG);
foreach my $zone_entry ( $mesg->entries ) {
next unless( $namedRunning );
my $dn = $zone_entry->dn();
my ( $zone ) = $dn =~ /^zoneName=([^,]+)/i;
next if( not exists($zones{$zone}) );
print STDERR "\tupdateDynamicZone( $dn )\n" if($DEBUG);
updateDynamicZone( $dn );
}
# FIXME: not so rock solid parser
# should be more sophisticated
sub getZoneTypes {
open(FILE, "< /etc/named.conf") or die("can't open /etc/named.conf for reading. $!");
while( my $l = <FILE> ) {
next unless $l =~ /^zone\s+\"*([^\s"]+)\"*/;
my $zone = $1;
my $bc = $l =~ tr/{/{/;
$zones{$zone} = { 'allow-update' => 0 };
while( $l = <FILE> ) {
$l =~ s/#.*//g;
next if $l =~ /^\s*$/;
$bc += $l =~ tr/{/{/;
if( $l =~ /^\s*allow-update/ ) {
# catches allow-update { key ...
if( $l =~ /key\s+\"*([^\s;"]+)\"*/ ) {
$zones{$zone}->{'allow-update'} = $1;
my $keyfile;
eval("\$keyfile = <$keydir/K".lc($zones{$zone}->{'allow-update'})."*>");
if( ! $keyfile ) {
die( "unable to read keyfile for ".$zones{$zone}->{'allow-update'}." in $keydir\n" );
}
} else {
# catches allow-update {
# key ....
while( $l = <FILE> ) {
$l =~ s/#.*//g;
next unless $l =~ /key\s+\"*([^\s;"]+)\"*/;
$zones{$zone}->{'allow-update'} = $1;
last;
}
}
} elsif( $l =~ /^\s*file\s+(.*)/ ) {
my $file = $1;
$file =~ s/^"//g;
$file =~ s/\s*;\s*$//;
$file =~ s/"$//g;
$zones{$zone}->{'file'} = $file;
} elsif( $l =~ /^\s*type\s+\"*([^\s";]+)\"*/ ) {
$zones{$zone}->{'type'} = $1;
}
$bc -= $l =~ tr/}/}/;
last if not $bc;
}
if( $zones{$zone}->{type} eq 'hint' ) {
delete($zones{$zone});
} elsif( $zone eq 'localhost' ) {
delete($zones{$zone});
} elsif( $zone eq '0.0.127.in-addr.arpa' ) {
delete($zones{$zone});
}
}
close(FILE);
}
my %keepRRinReverse = ();
sub dropStaticZoneEntries {
my $zone = shift;
print STDERR "\t\tdigging: $dig \@$server -t AXFR $zone\n" if($DEBUG);
my $data = `$dig \@$server -t AXFR $zone`;
my @data = split( "\n", $data );
my %toDel;
foreach my $l ( @data ) {
if( $l =~ /\s+IN\s+(NS|A|MX|PTR|CNAME|SRV|HINFO|MINFO|SIG|KEY|AAAA|LOC|NXT|NAPTR|KX|CERT|A6|DNAME)\s+/ ) {
my $rr = $1;
$l =~ /^(\S+)/;
my $e = $1;
if( $zone !~ /in-addr\.arpa/ and grep( { /^$e\s+.*"[0-9a-f]{34}"$/i } @data ) ) {
# this happens only in forward zones. Dynamic reverse zone RRs have no TXT record
print STDERR "\t\twill not drop entry $e because of TXT record\n" if($DEBUG);
if( $rr eq 'A' ) {
$l =~ /([\d.]+)$/;
my $ip = $1;
$ip = join( '.', reverse( split( /\./, $ip ) ) );
$ip .= ".in-addr.arpa.";
$keepRRinReverse{$ip} = 1;
print STDERR "\t\tremember '$ip' to keep\n";
}
} elsif( $zone =~ /^([\d.]+)in-addr\.arpa/ ) {
if( not exists($keepRRinReverse{$e}) ) {
$toDel{$e} = 1;
print STDERR "\t\twill drop entry $e $rr\n" if($DEBUG);
} else {
# this looks like a dynamic reverse zone entry
print STDERR "\t\twill not drop entry $e in reverse zone because of a TXT record in forward zone\n"
if($DEBUG);
}
} else {
$toDel{$e} = 1;
print STDERR "\t\twill drop entry $e $rr\n" if($DEBUG);
}
} else {
# debug?
}
}
$SIG{PIPE} = 'IGNORE';
if( $DONSUPDATE ) {
my $keyfile;
eval("\$keyfile = <$keydir/K".lc($zones{$zone}->{'allow-update'})."*>");
$keyfile =~ s/\.[^.]+$//g;
print STDERR "\t\tusing key ".lc($zones{$zone}->{'allow-update'})." for zone $zone\n" if( $DEBUG );
print STDERR "\t\topen pipe to: nsupdate -k $keyfile\n" if($DEBUG);
open( PIPE, "|$nsupdate -k $keyfile > /dev/null" ) or do {
print STDERR "unable to open pipe to $nsupdate -k $keyfile\n";
die($!);
};
print PIPE "server $server\n" or die "can’t write to $nsupdate pipe: $!";
}
print STDERR "\t\tupdate add $zone. 1234 NS ldapdump_kill_me\n" if( $DEBUG );
if( $DONSUPDATE ) {
# create dummy NS record
# sadly this one is needed if we want to change the last NS record
print PIPE "update add $zone. 1234 NS ldapdump_kill_me\n" or die "can’t write to $nsupdate pipe: $!";
}
foreach my $e ( @data ) {
next if( $e =~ /^[\s;]/ );
next if( $e =~ /^$/ );
$e =~ /^(\S+).*\s+IN\s+(\w+)\s+(.*)/;
if( exists($toDel{$1}) ) {
print STDERR "\t\tupdate delete $1 $2 $3\n" if( $DEBUG );
if( $DONSUPDATE ) {
print PIPE "update delete $1 $2 $3\n" or die "can’t write to $nsupdate pipe: $!";
}
}
}
if( $DONSUPDATE ) {
print PIPE "\n\n" or die "can’t write to $nsupdate pipe: $!";
close(PIPE) or die "can’t close $nsupdate pipe: status=$?";
}
print STDERR "\n\t\tpipe to $nsupdate closed\n" if($DEBUG);
return 1;
}
# remove zone entry from named.conf and delete zone file
sub dropZone {
my $zone = shift;
open(FILE, "< /etc/named.conf") or die("reading named.conf $!");
my @named_conf = <FILE>;
close(FILE);
for( my $i=0; $i < @named_conf; $i++ ) {
next unless $named_conf[$i] =~ /^zone\s+\"*$zone/;
print STDERR "\t\tfound zone $zone in named.conf. Dropping ...\n" if($DEBUG);
my $bc = $named_conf[$i] =~ tr/{/{/;
$bc -= $named_conf[$i] =~ tr/}/}/;
$named_conf[$i++] = "";
do {
$named_conf[$i] =~ s/#.*//g;
$bc += $named_conf[$i] =~ tr/{/{/;
$bc -= $named_conf[$i] =~ tr/}/}/;
$named_conf[$i++] = "";
} while( $bc and ($i < @named_conf) );
print STDERR " done\n" if($DEBUG);
}
if( $DOFILES ) {
open(FILE, "> /etc/named.conf") or die("writing named.conf $!");
print FILE @named_conf;
close(FILE);
}
print STDERR "\t\tnamed.conf written\n" if($DEBUG);
my $del = $namedDir.'/'.$zones{$zone}->{file}.'*';
if( $DOFILES ) {
eval "unlink <$del>";
}
print STDERR "\t\tunlink: $del\n" if($DEBUG);
}
sub error {
my $t = shift;
print STDERR "$t\n";
}
# this one writes zone files and modifies the
# named.conf
sub createStaticZone {
my $dn = shift;
my @outf = ();
my $zone;
my $mesg = $ldap->search(
base => $dn,
scope => 'base',
filter => '(&(relativeDomainName=@)(objectclass=dNSZone))'
);
if( $mesg->code != 0 ) {
error("LDAP: search failed\n");
$ldap->unbind;
exit(-3);
}
foreach my $zone_entry ( $mesg->entries ) {
$zone = $zone_entry->get_value('zoneName');
print STDERR "\t\tcreating static zone $zone as file\n" if($DEBUG);
my $ttl = $zone_entry->get_value('dnsttl');
my $soa = $zone_entry->get_value('soarecord');
my @soa = ( '', '' );
@soa = split(/ /,$soa) if( $soa );
push @outf, "\$TTL $ttl\n";
push @outf, "\@\tIN\tSOA\t".$soa[0]." ".$soa[1]." (\n";
for( my $i=2; $i < @soa; $i++ ) {
push @outf, "\t\t\t".$soa[$i]."\n";
}
chomp($outf[-1]);
$outf[-1] .= ")\n";
foreach my $rec ( qw( NS A MX PTR CNAME SRV HINFO MINFO SIG KEY AAAA LOC NXT NAPTR KX CERT A6 DNAME ) ) {
my $ref = $zone_entry->get_value($rec.'record', asref => 1);
next unless $ref;
foreach my $rr ( @$ref ) {
print STDERR "\t\tadding RR $rec $rr.\n" if($DEBUG);
my $str = "\tIN\t$rec\t".$rr;
push @outf, "$str\n";
}
}
}
$mesg = $ldap->search(
base => $dn,
scope => 'one',
filter => '(objectclass=dNSZone)'
);
if( $mesg->code != 0 ) {
error("LDAP: search failed\n");
$ldap->unbind;
exit(-3);
}
foreach my $zone_entry ( $mesg->entries ) {
my $ttl = $zone_entry->get_value('dnsttl');
my $rdn = $zone_entry->get_value('relativedomainname');
my $zone = $zone_entry->get_value('zoneName');
foreach my $rec ( qw( NS A PTR MX TXT CNAME SRV HINFO MINFO SIG KEY AAAA LOC NXT NAPTR KX CERT A6 DNAME ) ) {
my $ref = $zone_entry->get_value($rec.'record', asref => 1);
next unless $ref;
foreach my $rr ( @$ref ) {
print STDERR "\t\tadding RR $rec $rr\n" if($DEBUG);
push @outf, "$rdn\tIN\t$rec\t$rr\n";
}
}
}
# my $filename = $namedDir."/";
my $filename = "";
my $doChown = 0;
if( exists($zones{$zone} ) ) {
# this is a not so nice workaround
# if the nameserver is not running, dynamic entries which are
# not in LDAP get lost during this. To avoid this, I have
# to parse existing dynamic zone file and save the TXT records from
# not getting written.
#
$filename .= $zones{$zone}->{file};
push( @outf, parseDynEntries($namedDir."/".$filename) );
delete($zones{$zone});
$doChown = 1;
my $del = $namedDir."/".$filename.'*';
print STDERR "\t\tdeleting file from dynamic zone $del\n" if( $DEBUG );
if( $DOFILES ) {
eval "unlink <$del>";
}
print STDERR "\t\tcreating file for new dynamic zone $zone ($filename)\n" if( $DEBUG );
} else {
$filename .= "master/$zone";
open( FILE, "< /etc/named.conf" ) or die "unable to read /etc/named.conf. $!";
my @file = <FILE>;
close(FILE);
print STDERR "\t\tadding zone \"$zone\" to named.conf\n" if($DEBUG);
push( @file, "zone \"$zone\" in {\n" );
push( @file, "\tfile \"$filename\";\n" );
push( @file, "\ttype master;\n" );
push( @file, "};\n" );
if( $DOFILES ) {
open( FILE, "> /etc/named.conf" ) or die "unable to write /etc/named.conf. $!";
print FILE @file;
close(FILE);
}
}
print STDERR "\t\tcreating file: $filename\n" if($DEBUG);
if( $DOFILES ) {
open( FILE, "> $namedDir/$filename" );
print FILE @outf;
close(FILE);
if( $doChown ) {
my ($login,$pass,$uid,$gid) = getpwnam('named');
print STDERR "\t\tchowning file: $uid, $gid, $filename\n" if( $DEBUG );
chown( $uid, $gid, $filename );
}
}
return;
}
# this one uses nsupdate to add new entries to dynamic zones
#
sub updateDynamicZone {
my $dn = shift;
my $mesg = $ldap->search(
base => $dn,
scope => 'base',
filter => '(&(relativeDomainName=@)(objectclass=dNSZone))'
);
if( $mesg->code != 0 ) {
error("LDAP: search failed\n");
$ldap->unbind;
exit(-3);
}
my $ttl;
my $zone;
foreach my $zone_entry ( $mesg->entries ) {
$zone = $zone_entry->get_value('zoneName');
$ttl = $zone_entry->get_value('dnsttl');
}
$SIG{PIPE} = 'IGNORE';
if( $DONSUPDATE ) {
my $keyfile;
eval("\$keyfile = <$keydir/K".lc($zones{$zone}->{'allow-update'})."*>");
$keyfile =~ s/\.[^.]+$//g;
print STDERR "\t\tusing key ".lc($zones{$zone}->{'allow-update'})." for zone $zone\n" if( $DEBUG );
print STDERR "\t\topen pipe to: $nsupdate -k $keyfile\n" if($DEBUG);
open( PIPE, "|$nsupdate -k $keyfile > /dev/null" ) or do {
print STDERR "unable to open pipe to $nsupdate -k $keyfile\n";
die($!);
};
}
$mesg = $ldap->search(
base => $dn,
scope => 'sub',
filter => '(objectclass=dNSZone)'
);
if( $mesg->code != 0 ) {
error("LDAP: search failed\n");
$ldap->unbind;
exit(-3);
}
print STDERR "\t\tcreating dyn. zone from $dn\n" if($DEBUG);
if( $DONSUPDATE ) {
print PIPE "server $server\n" or die "can’t write to $nsupdate pipe: $!";
}
foreach my $zone_entry ( $mesg->entries ) {
my $ttl = $zone_entry->get_value('dnsttl') || $ttl;
my $rdn = $zone_entry->get_value('relativedomainname');
my $zone = $zone_entry->get_value('zoneName') || $zone;
foreach my $rec ( qw( SOA NS A PTR MX TXT CNAME SRV HINFO MINFO SIG KEY AAAA LOC NXT NAPTR KX CERT A6 DNAME ) ) {
my $ref = $zone_entry->get_value($rec.'record', asref => 1);
next unless $ref;
foreach my $rr ( @$ref ) {
my $where = ($rdn eq '@')?("$zone."):("$rdn.$zone");
my $command = "update add $where $ttl $rec $rr\n";
print STDERR "\t\t$command" if($DEBUG);
if( $DONSUPDATE ) {
print PIPE "$command" or die "can’t write to $nsupdate pipe: $!";
}
}
}
}
print STDERR "\t\tupdate delete $zone. NS ldapdump_kill_me\n" if($DEBUG);
if( $DONSUPDATE ) {
print PIPE "update delete $zone. NS ldapdump_kill_me\n" or die "can’t write to $nsupdate pipe: $!";
print PIPE "\n\n\n" or die "can’t write to $nsupdate pipe: $!";
close(PIPE) or die "can’t close $nsupdate pipe: status=$?";
}
return;
}
sub getNamedDir {
my $ret;
open( FILE, "< /etc/named.conf" ) or die "unable to open named.conf. $!";
while(<FILE>) {
next unless /^options/;
while(<FILE>) {
next unless /^\s*directory\s+\"*([^";]+)/;
close FILE;
return $1;
}
}
}
sub getLDAPConfigServer {
my $data;
open( FILE, "< /etc/openldap/ldap.conf" ) or die "unable to open /etc/openldap/ldap.conf. $!";
while(<FILE>) {
if ( /^host\s+([^\s]*)\s*/i ) { $data = $1; last }
}
close(FILE);
return $data;
}
sub getLDAPConfigBaseDN {
my $data;
open( FILE, "< /etc/openldap/ldap.conf" ) or die "unable to open /etc/openldap/ldap.conf. $!";
while(<FILE>) {
if ( /^base\s+([^\s]*)/i ) { $data = $1; last }
}
close(FILE);
return $data;
}
sub getLDAPConfigDNSBaseDN {
my $ret;
my $mesg = $ldap->search(
base => $ldapBaseDN,
scope => 'sub',
filter => '(objectclass=suseDnsConfiguration)'
);
if( $mesg->code != 0 ) {
error("LDAP: search failed\n");
$ldap->unbind;
} else {
foreach my $entry ( $mesg->entries ) {
$ret = $entry->get_value('suseDefaultBase');
}
}
return $ret;
}
sub statusNamed {
my $ret = 0;
if( -f '/var/run/named/named.pid' ) {
if( open(FILE, "< /var/run/named/named.pid") ) {
my $pid = <FILE>;
chomp($pid);
close(FILE);
if( open(FILE, "< /proc/$pid/cmdline") ) {
my $cmd = <FILE>;
close(FILE);
$ret = 1 if( $cmd =~ /named/ );
}
}
}
return $ret;
}
sub parseDynEntries {
my $file = shift;
my @ret = ();
print STDERR "\t\tsearching dyn entries in $file\n" if( $DEBUG );
open( DUMMY, "< $file" );
my @data = <DUMMY>;
close(DUMMY);
my %entries;
my $entry = "";
foreach( my $i=0; $i<@data; $i++ ) {
if( $data[$i] =~ /^(\S+)\s+(.*)/ ) {
$entry = $1;
$entries{$1} .= "$2\n";
} else {
$data[$i] =~ /^\s+(.*)/;
$entries{$entry} .= "\t$1\n";
}
}
foreach my $k ( keys(%entries) ) {
if( $entries{$k} =~ /TXT\s+\"*[a-f0-9]{34}\"*/i ) {
print STDERR "\t\t$k identified as dyn entry in zone file $file\n" if( $DEBUG );
push( @ret, "$k\t$entries{$k}" );
}
}
return @ret;
}
sub sortZones {
return sort {
if( $a =~ /in-addr\.arpa/ and $b !~ /in-addr\.arpa/ ) {
return 1;
} elsif( $a =~ /in-addr\.arpa/ and $b =~ /in-addr\.arpa/ ) {
return 0;
} else {
return -1;
}
} keys(%zones);
}
ACC SHELL 2018