#!/usr/bin/perl

$debug = 0;

require Time::Local;

# read in servers file 
$path = "/etc/bind/reverse";

$server_file = shift || "$path/hosts";

increment_serial();

open(SERVERS,"<$server_file");

while($in = <SERVERS>) {
	chop($in);

	($ip, $host) = split(/\s+/,$in);
	if($host && $ip) {
		$reverse{$ip} = $host;
		print "Preloading $host: [$ip]\n" if($debug);
	}

}

close(SERVERS);

# read in DHCP leases

open(LEASES,"</var/lib/dhcp/dhcpd.leases");

while($in=<LEASES>) {
	chop($in);
	
	# is this a start of lease line?

	if(($trim) = $in =~ /^lease(.*){$/) {
		($ip) = $trim =~ /.*?(\d+\.\d+\.\d+\.\d+).*/;

		$host = "";
		print "Found lease: ($trim)[$ip]\n" if($debug);
	}

	# is this a hostname line? If so, and we have a valid IP, store

	if($ip && (($host) = $in =~ /\s*client-hostname.*\"(.*)\".*/)) {
		$reverse{$ip} = $host;
		if((defined $dhcp_date{$host}) && ($dhcp_date{$host} > $dhcp_date)) {
			# no op
		} else {
			$dhcp{$host} = $ip;
			$dhcp_date{$host} = $dhcp_date;
		}
		print "Found host for lease: $ip -> $host\n" if($debug);
	}
	
	# is this the end of a stanza? If so, discard ip and host

	if($ip && (($year,$month,$day,$hour,$minute,$second) = /\s*\d+\s*(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+);/)) {
		$unix_date = timelocal($second,$minute,$hour,$day,$month,$year);

		$dhcp_date = $unix_date;
	}
	if($in =~ /.*}.*/) {
		$ip = undef;
		$host = undef;
		$dhcp_date = undef;
		print "end stanza\n" if($debug);
	}
}
	

close(LEASES);

$networks_file = "$path/networks";

open(NETWORKS,"<$networks_file");

while($in = <NETWORKS>) {
	chop($in);
	($net, $ext,$pre) = split(/\s+/,$in);
	$ext{$net} = $ext;
	$pre{$net} = $pre;
	print "Preloading $net: e[$ext] p[$pre]\n" if($debug);
	push(@networks,$net);
}

# reverse the network name

foreach $ip (keys %reverse) {
	$host = $reverse{$ip};
	print "$ip -> $host\n" if($debug);
} 

foreach $net (@networks) {
	build_reverse($net);
}

ping_hosts();
build_dynamic();


sub increment_serial {
	$serial_file = "$path/serial";
	open(SERIAL,"<$serial_file") || die "No serial file";
	$in = <SERIAL>;
	chop($in);
	if($in < 1997022700) {
		$in = "2007000777";
	} else {
		$in++;
	}

	$official_serial = $in;

	open(SERIAL,">$serial_file");
	print SERIAL $in . "\n";
	print "Official serial is $official_serial" if($debug);
	close(SERIAL);
}

sub build_reverse {
	my $net = shift;

	@r = split(/\./,$net);

	# we could recurse here, but we dont

	die "I only do class C" if(@r < 3);
	
	$myaddr = "";

	while(defined($a = pop(@r))) {
		$myaddr .= "." if($myaddr ne "");
		$myaddr .= $a;
	}

	print "Building reverse for $net / $myaddr\n" if($debug);

	$bigstring = "";
	for($i=1;$i<256;$i++) {
		$thisip = "$net\.$i";
		if(defined $reverse{$thisip}) {
			$host = $reverse{$thisip};
			print "Looking up $thisip: $host\n" if($debug);
		} else {
			$host = $pre{$net} . $i . $ext{$net};
		}


		$bigstring .= "$i\tIN\tPTR\t$host\.\n";
	}
	

	$zone_file = "$path/rev.$net";
	$template_file = "$path/template";

	open(TEMPLATE,"<$path/template");
	open(OUT,">$zone_file");
	while($in = <TEMPLATE>) {
		$in=~ s/\%HOSTS\%/$bigstring/;	
		$in=~ s/\%SERIAL\%/$official_serial/;	
		print OUT $in;
	}
	close(TEMPLATE);
	close(OUT);
}

sub build_dynamic {
	$zone_file = "$path/d.zone";
	$template_file = "$path/template";

	$bigstring = "";
	foreach $host (keys %dhcp) {
		$ip = $dhcp{$host};	
		$bigstring .= "$host\t600\tIN\tA\t$ip\n";
	}

	open(TEMPLATE,"<$path/template");
	open(OUT,">$zone_file");
	while($in = <TEMPLATE>) {
		$in=~ s/\%HOSTS\%/$bigstring/;	
		$in=~ s/\%SERIAL\%/$official_serial/;	
		print OUT $in;
	}
	close(TEMPLATE);
	close(OUT);

}

sub ping_hosts {
	ping_host("10.101.1.23","ivy");
	ping_host("10.101.0.23","ivy");
	ping_host("10.101.1.42","jam");
	ping_host("10.101.0.42","jam");
}

sub ping_host {
	my $ip = shift;
	my $name = shift;

	my $ret = _ping($ip);
	print "ping_host $ip -> $name -> $ret\n" if($debug);
	if($ret) {
		$dhcp_date{$name} = time();
		$dhcp{$name} = $ip;
	}
}

sub _ping {
	my $ip = shift;
	my $pin;
	$prx = 0;
	open(PING,"ping -c 1 $ip|");

	while($pin = <PING>) {
		chop($pin);
		if((($prx) = $pin =~ /1 packets transmitted, (\d+) received, .*/)) {
			close(PING);
			return($prx);
		}
	}
	close(PING);
	return(0);
}


