#!/usr/bin/perl
BEGIN {
	my $x = $0; $x =~ s/\/[^\/]+$//;
	if ($x eq $0 || $x eq '') { $x = `pwd`;chomp $x; };
	require "$x/config.pl";
};

use Net::DNS;

sub usage {
	print "usage: transfer_zone ns zone ...\n"; exit 1;
}

my $ns1ip = $ENV{IP};
my @local_ns = ();

warn "IP not set; won't force creation of working nameserver records"
	if (!defined($ns1ip) && $ARGV[1] ne '.');	# 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 if (!defined($ns));

my @zone = shift @ARGV;
&usage if (scalar(@zone) < 1);

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) {
	print $zonename . ' ' . ('=' x (79 - length($zonename))) . "\n\n";
	my @records = $res->axfr($zonename);
	while (!@records) {
		if ($res->errorstring eq "couldn't connect") {
			sleep(10);
			@records = $res->axfr($zonename);
		} else {
			next read_zone;
		}
	}

	foreach my $rr (@records) {
		$rr->print;
		if ($rr->type eq 'SOA') {
			die "Invalid SOA(1) record for ", $rr->name, "  "
				unless ($rr->string =~ /^(\*?[0-9a-zA-Z_.+-]+)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+([0-9a-zA-Z_.+-]+)\s+([0-9a-zA-Z_.+-]+)\s+\((.*)\)/s);
			die "Corrupt SOA record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type);

			my $zn = $1;
			my $soa = $7;
			die "Invalid SOA fields for ", $zonename, "  "
				unless ($soa =~ /\s*(\d+)\D*(\d+)\D*(\d+)\D*(\d+)\D*(\d+)\s*/s);

			my $soarecord = "$1 $2 $3 $4 $5";

			set_record($ldap, &fix_domain($zonename, $zn), [
				dc => &dc_domain($zn),
				objectClass => 'dnsDomain',
				objectClass => 'dcObject',
				objectClass => 'domain',
				sOARecord => $soarecord,
			], { sOARecord => $soarecord });
		} elsif ($rr->type eq "A") {
			die "Invalid A record for ", $rr->name, "  "
				unless ($rr->string =~ /^(\*?[0-9a-zA-Z_.+-]+)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+([0-9.]+)/);
			die "Corrupt A record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type && $5 eq $rr->address);

			next if lc($1) eq lc("localhost.$zonename");
			add_record($ldap, &fix_domain($zonename, $1), [
				dc => &dc_domain($1),
				objectClass => 'dnsDomain',
				objectClass => 'dcObject',
				objectClass => 'domain',
				aRecord => $5,
			], { aRecord => $5 });
		} elsif ($rr->type eq "MX") {
			die "Invalid MX record for ", $rr->name, "  "
				unless ($rr->string =~ /^(\*?[0-9a-zA-Z_.+-]+)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+(\d+)\s+([0-9a-zA-Z_.+-]+)/);
			die "Corrupt MX record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type);

			my $zn = $1;
			my $pref = $5;
			my $name = lc $6;
			$name =~ s/\.$//; $name .= '.';
			add_record($ldap, &fix_domain($zonename, $zn), [
				dc => &dc_domain($zn),
				objectClass => 'dnsDomain',
				objectClass => 'dcObject',
				objectClass => 'domain',
				mXRecord => "$pref $name",
			], { mXRecord => "$pref $name" });
		} elsif ($rr->type eq "NS") {
			die "Invalid NS record for ", $rr->name, "  "
				unless ($rr->string =~ /^(\*?[0-9a-zA-Z_.+-]*)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+([0-9a-zA-Z_.+-]+)/);
			die "Corrupt NS record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type);

			my $zn = $1;
			$zn = '*' if ($zn eq '');

			my $name = lc $5;
			$name =~ s/\.$//; $name .= '.';

			# nameserver records for PTR's?
			next if ($zn =~ /\.in-addr\.arpa\.?$/ && $ns1ip && $ns1ip ne '');

			if ($ns1ip && $ns1ip ne '') {
				# we want $name to be fudged to be
				# ns.$zn

				$name = 'ns.' . $zn;
			}

			add_record($ldap, &fix_domain($zonename, $zn), [
				dc => &dc_domain($zn),
				objectClass => 'dnsDomain',
				objectClass => 'dcObject',
				objectClass => 'domain',
				nSRecord => $name,
			], { nSRecord => $name });

			if ($ns1ip && $ns1ip ne '') {
				set_record($ldap, &fix_domain($zonename, $name), [
					dc => &dc_domain($name),
					objectClass => 'dnsDomain',
					objectClass => 'dcObject',
					objectClass => 'domain',
					aRecord => $ns1ip,
				], { nSRecord => $name });
			}
		} elsif ($rr->type eq "CNAME") {
			die "Invalid ", $rr->type, " record for ", $rr->name, "  "
				unless ($rr->string =~ /^(\*?[0-9a-zA-Z_.+-]+)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+([0-9a-zA-Z_.+-]+)/);
			die "Corrupt ", $rr->type, " record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type);

			my $zn = $1;
			my $name = lc $5;
			$name =~ s/\.$//; $name .= '.';
			add_record($ldap, &fix_domain($zonename, $zn), [
				dc => &dc_domain($zn),
				objectClass => 'dnsDomain',
				objectClass => 'dcObject',
				objectClass => 'domain',
				cNAMERecord => $name,
			], { cNAMERecord => $name });
		} elsif ($rr->type eq "TXT") {
			die "Invalid ", $rr->type, " record for ", $rr->name, "  "
				unless ($rr->string =~ /^(\*?[0-9a-zA-Z_.+-]+)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+(.*)/);
			die "Corrupt ", $rr->type, " record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type);

			# displays as "doo" "doo" ""
			my $arg = join('|', (split /\" \"/, substr($5, 1, -1)));
			$arg =~ s/\|$//;

			my $zn = $1;
			add_record($ldap, &fix_domain($zonename, $zn), [
				dc => &dc_domain($zn),
				objectClass => 'dnsDomain',
				objectClass => 'dcObject',
				objectClass => 'domain',
				description => $arg,
			], { description => $arg });
		} elsif ($rr->type eq "PTR") {
			die "Invalid PTR record for ", $rr->name, "  "
				unless ($rr->string =~ /^([0-9.]+\.in-addr\.arpa)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+([0-9a-zA-Z_.+-]+)/i
				|| $rr->string =~ /^([\w\/.+-]+\.$zonename)\.\s+(\d+)\s+(\w+)\s+(\w+)\s+([\w\/.+-]+)/i);
			die "Corrupt PTR record for ", $rr->name, "  "
				unless ($1 eq $rr->name && $2 eq $rr->ttl && $3 eq $rr->class && $4 eq $rr->type);

			my $zn = $1;
			my $name = lc $5;
			$name =~ s/\.$//; $name .= '.';

			if ($ns1ip && $ns1ip ne '') {
				set_record($ldap, &fix_domain($zonename, $zn), [
					dc => &dc_domain($zn),
					objectClass => 'dnsDomain',
					objectClass => 'dcObject',
					objectClass => 'domain',
					cNAMERecord => $name,
					nSRecord => \@local_ns,
				], { cNAMERecord => $name });
			} else {
				# let the "old" record keep it's NS...
				set_record($ldap, &fix_domain($zonename, $zn), [
					dc => &dc_domain($zn),
					objectClass => 'dnsDomain',
					objectClass => 'dcObject',
					objectClass => 'domain',
					cNAMERecord => $name,
				], { cNAMERecord => $name });
			}
		}
	}

}

sub fix_domain
{
	my ($base, $zn)=(@_);
	return $base if ($zn eq '');
	return $zn;
}
