#!/usr/bin/perl

# Functional style
use lib '/hsphere/shared/lib/perl5'; 
use lib '/hsphere/shared/lib/perl5/site_perl'; 

use Digest::MD5 qw(md5 md5_hex md5_base64);
use Fcntl ':flock'; 
use File::Copy 'move';

use Fatal qw(open close flock move);
use constant ROOT => '/hsphere/local/var/named';

####################################### main ####################################################

if ($ARGV[0] eq "-h") {
	&usage();
	exit(0);
}

my $index_changes_ptr = &suck_zones(STDIN, $ARGV[0]);

while (my ($idx, $zones_ptr) = each %{$index_changes_ptr}) {
	&forward_set($idx, $zones_ptr);
}

exit(0);


####################################### service subroutines #####################################

sub usage {
	print << "EOF";
Usage:
Run this script as:
$0 [default DNS server IP]
Feed zones into script's standard input, one zone per line.
You may optionally specify a custom DNS server IP for each zone, in the same line, e.g.:
zone.com 192.168.1.254
and such custom DNS server IP will have precedence over default DNS server IP, for this zone.
Empty lines are not allowed, zones with undefined DNS server IP are not allowed.
EOF
}

sub suck_zones {
	my ($handle, $default_dns_server_ip) = @_;
	my %index_changes;	#  = { '1' => {	'zone.com' => '10.1.1.1',
				#		'zone.tst' => '10.2.2.2'
				#	      }
				#    }

	while (<$handle>) {
	        chomp;

		my $zone;
		my $dns_server_ip = $default_dns_server_ip;
		if (/^([^ ]+) (.+)$/) {
			$zone = $1;
			$dns_server_ip = $2;
		} elsif (/^([^ ]+)$/) {
			$zone = $1;
		} else {
			die "Zone name must be specified.\n";
		}

		unless ($dns_server_ip) {
			die "DNS server IP must be defined for zone $zone.\n";
		}

		# this is how zone files naming is implemented in H-Sphere
		my ($d1, $d2) = unpack("I2", md5($zone));
		my $idx = $d1 % 23;

		$index_changes{$idx}->{$zone} = $dns_server_ip;
	}

	return \%index_changes;
}

sub forward_set {
	my ($idx, $zones_ptr) = @_;

	my $conf_file = ROOT . "/zones_$idx.conf";
	my $lock_file = "$conf_file.lock";

	my $lock_handle = &lock($lock_file);

	my @out;
	if (-e $conf_file) {
		open (CONF, "< $conf_file");
		@out = <CONF>;
		close (CONF);
	}

	while (my ($zone, $dns_server_ip) = each %{$zones_ptr}) {
		@out = grep(!/^zone\s+\"$zone\"/, @out);

		push @out, "zone \"$zone\" { type forward; forward only; forwarders { $dns_server_ip; }; };\n";
	}

	my $conf_file_tmp = "$conf_file.$$";
	open (TCONF,"> $conf_file_tmp");
	print TCONF @out;
	close TCONF;
  
	move($conf_file_tmp, $conf_file);
  
	system("rndc reconfig");

	&unlock($lock_handle);
}

sub lock {
	my ($lock_file) = @_;

	my $handle;
	open($handle,"> $lock_file");
	flock($handle, LOCK_EX);

	return $handle;
}

sub unlock {
	my ($handle) = @_;
	flock($handle, LOCK_UN);
	close($handle);
}
