#!/usr/bin/perl -w
BEGIN {
my $x = $0; $x =~ s/\/[^\/]+$//;
if ($x eq $0 || $x eq '') { $x = `pwd`;chomp $x; };
require "$x/config.pl";
};
use strict ;
use Net::DNS;
sub usage {
print "usage: secondary_zone ns zone ...\n"; exit 1;
}
sub fix_domain ($$) {
my ($base, $zn)=(@_);
return $base if $zn eq '';
return $zn;
}
sub relative_name ($$) {
my ($nm, $zn) = @_;
if ($ENV{RELATIVE_NAMES}) {
return lc $nm unless $nm =~ /\./;
return $nm if $nm =~ /^\d+\.\d+\.\d+\.\d+$/;
return lc $1 if $nm =~ /^(.*)\.$zn\.?$/;
return lc $nm if $nm =~ /\.$/;
return lc "$nm." ;
} else {
if ($nm =~ s/\.$//) {
return lc $nm;
} else {
my $x = lc($nm . $zn);
$x =~ s/\.$//;
return $x;
}
}
}
sub given_serial ($$) {
my ($ldap, $dn) = @_ ;
my $attr = 'sOARecord' ;
my $mesg = $ldap->search
(base => dn_domain ($dn),
attrs => $attr,
scope => 'base',
filter => '(objectClass=dnsDomain)');
return 0 if $mesg->code ;
my @entry = $mesg->entries ;
my $entry = $entry[0] ;
my $value = $entry->get_value ($attr) ;
return 0 unless defined $value ;
return $1 if $value =~ /^(\d+)/ ;
return 0 ;
}
sub delete_zone ($$) {
my ($ldap, $rdn) = @_ ;
my $dn = dn_domain ($rdn) ;
my $mesg = $ldap->search
(base => $dn,
scope => 'one',
filter => '(objectClass=dnsDomain)');
return 0 if $mesg->code ;
foreach my $entry ($mesg->all_entries) {
$ldap->delete ($entry) ;
next unless $mesg->code ;
print "Cannot delete \"", $entry->dn, "\" (", $mesg->error, ")\n";
}
$ldap->delete ($dn) ;
return 1 unless $mesg->code ;
print "Cannot delete \"$dn\" (", $mesg->error, ")\n";
return 1 ;
}
my $ns1ip = $ENV{IP};
my @local_ns = ();
warn "IP not set; won't force creation of working nameserver records"
unless defined ($ns1ip) || $ARGV[1] eq '.' ; # root zones...
if ($ns1ip && $ns1ip ne '') {
my $x = $ENV{LOCAL_NS};
if ($x && $x ne '') {
@local_ns = split /\s*,\s*/, $x;
}
if ($ns1ip =~ /,/) {
# tada
my @x = split /\s*\,\s*/, $ns1ip;
$ns1ip = \@x;
}
}
my $ns = shift @ARGV; usage() unless defined $ns;
my @zone = @ARGV; usage() unless $#zone >= 0;
if ($zone[0] eq '-') {
@zone = ();
while (<STDIN>) { chomp; push (@zone, $_); }
}
my $ldap = &get_ldap_conn;
my $res = new Net::DNS::Resolver;
$res->nameservers ($ns);
read_zone:
foreach my $zonename (@zone) {
my @records = $res->axfr ($zonename);
while (!@records) {
if ($res->errorstring ne "couldn't connect") {
print "Skipping zone $zonename (could not connect to name server).\n" ;
next read_zone ;
}
sleep (10);
@records = $res->axfr ($zonename);
}
foreach my $rr (@records) {
### $rr->print;
die "Invalid ", $rr->type, " record for ", $rr->name, " "
unless ($rr->name =~ /^(\*\.)?[\w.\/+-]+$/ &&
$rr->ttl =~ /^\d+$/ &&
$rr->class =~ /^in$/i &&
$rr->type =~ /^[a-z]+$/i );
my $zname = fix_domain ($zonename, $rr->name) ;
my $dcdom = dc_domain ($rr->name) ;
if (lc $rr->type eq 'soa') {
die "Invalid SOA fields for ", $zonename, " "
unless ($rr->serial =~ /^\d+$/ &&
$rr->refresh =~ /^\d+$/ &&
$rr->retry =~ /^\d+$/ &&
$rr->expire =~ /^\d+$/ &&
$rr->minimum =~ /^\d+$/ &&
$rr->rname =~ /^[\w.+-]+$/);
# check whether something has changed, at all
if (given_serial ($ldap, $zname) >= $rr->serial) {
print "Skipping zone $zonename (serial has not changed).\n" ;
next read_zone;
}
if (delete_zone ($ldap, $zname)) {
print "Replacing zone $zonename.\n" ;
} else {
print "Adding zone $zonename.\n" ;
}
my $soarecord = $rr->serial
. " "
. $rr->refresh
. " "
. $rr->retry
. " "
. $rr->expire
. " "
. $rr->minimum
;
set_record ($ldap, $zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'inetOrgPerson',
objectClass => 'domain',
nSRecord => relative_name ($rr->mname, $zonename),
mail => relative_name ($rr->rname, $zonename),
sOARecord => $soarecord],
{sOARecord => $soarecord});
next ;
}
if (lc $rr->type eq "a") {
die "Invalid ", $rr->type, " record for ", $rr->name, " "
unless ($rr->address =~ /^\d+\.\d+\.\d+\.\d+$/);
next if lc ($rr->name) eq lc ("localhost.$zonename");
add_record ($ldap, $zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'domain',
aRecord => $rr->address],
{aRecord => $rr->address});
next ;
}
if (lc $rr->type eq "mx") {
die "Invalid ", $rr->type, " record for ", $rr->name, " "
unless ($rr->preference =~ /^\d+$/ &&
$rr->exchange =~ /^[\w.+-]+$/);
my $name = $rr->preference . " " .
relative_name ($rr->exchange, $zonename) ;
add_record ($ldap, $zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'domain',
mXRecord => $name],
{mXRecord => $name});
next ;
}
if (lc $rr->type eq "ns") {
die "Invalid ", $rr->type, " record for ", $rr->name, " "
unless $rr->nsdname =~ /^[\w.+-]+$/;
my $name = relative_name ($rr->nsdname, $zonename);
add_record ($ldap, $zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'domain',
nSRecord => $name],
{nSRecord => $name });
next ;
}
if (lc $rr->type eq "cname") {
die "Invalid ", $rr->type, " record for ", $rr->name, " "
unless ($rr->cname =~ /^[\w.+-]+$/);
my $name = relative_name ($rr->cname, $zonename);
add_record ($ldap, $zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'domain',
cNAMERecord => $name],
{cNAMERecord => $name});
next;
}
if (lc $rr->type eq "txt") {
# displays as "doo" "doo" ""
my $arg = join('|', (split /\" \"/, substr ($rr->txtdata, 1, -1)));
$arg =~ s/\|$//;
set_record ($ldap,$zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'domain',
description => $arg],
{description => $arg});
next ;
}
if (lc $rr->type eq "ptr") {
die "Invalid PTR record for ", $rr->name, " "
unless $rr->ptrdname =~ /^[\w.\/+-]+$/;
my $name = relative_name ($rr->ptrdname, $zonename);
add_record ($ldap, $zname,
[dc => $dcdom,
objectClass => 'dnsDomain',
objectClass => 'dcObject',
objectClass => 'domain',
cNAMERecord => $name],
{cNAMERecord => $name});
next ;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1