#!/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 () { 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 ; } } }