#!/usr/bin/perl -w
#----------------------------------------------------------------------
# copyright (C) 1999-2003 Mitel Networks Corporation
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 		
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 		
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
# 
# Technical support for this program is available from Mitel Networks 
# Please visit our web site www.mitel.com/sme/ for details.
#----------------------------------------------------------------------
package esmith;

use strict;
use Errno;
use esmith::ConfigDB;
use esmith::DomainsDB;
use esmith::NetworksDB;
use esmith::util;

sub allow_networks_2access_cache;
sub delegate_domains_2localDNS;

my $config = esmith::ConfigDB->open or die "Could not open config db.";
my $dnscache = $config->get('dnscache');
unless ($dnscache)
{
    warn "dnscache not configured in configuration db\n";
    exit 0;
}

my $domains = esmith::DomainsDB->open or die "Could not open Domains db.";
my $nets = esmith::NetworksDB->open or die "Could not open Networks db.";

#------------------------------------------------------------
# Configure DNS cache.
#------------------------------------------------------------

unless (-f "/var/service/dnscache/seed")
{
    system(qw(/bin/dd
	if=/dev/random
	of=/var/service/dnscache/seed
	bs=128 count=1)) == 0 or warn("Could not create seed file");
}

my $event = shift || "Unknown";

foreach my $file (qw(CACHESIZE DATALIMIT IP IPSEND FORWARDONLY))
{
    esmith::util::processTemplate ({
                        TEMPLATE_PATH => "/var/service/dnscache/env/$file",
                        PERMS => 0644,
                        });
}

esmith::util::processTemplate ({
                        TEMPLATE_PATH => "/var/service/dnscache/root/servers/@",
                        PERMS => 0644,
                        });

# allow my networks to access the nameserver cache
my @localnetworks = ();
my %reversenets = ();

foreach my $net ($nets->get_all_by_prop('type', 'network'))
{
    my $mask = $net->prop('Mask');
    my $key = $net->key;
    my $nameserver = $net->prop('NameServer') || '127.0.0.1';
    push @localnetworks,
	esmith::util::computeAllLocalNetworkPrefixes ($key, $mask);

    my $reverse = esmith::util::computeLocalNetworkReversed ($key, $mask);
    # Remove the trailing period provided by computeLocalNetworkReversed
    $reverse =~ s/\.$//;
    $reversenets{$reverse} = $nameserver;
}

allow_networks_2access_cache(@localnetworks);

if ($dnscache->prop('Forwarder'))
{
    # We handle no domains locally
    delegate_domains_2DNS();
}
else
{
    delegate_domains_2DNS(
		%reversenets,
    		map { $_->key => $_->prop('NameServer') || '127.0.0.1' } 
		    (	$domains->get_all_by_prop('type', 'domain'), 
			$domains->get_all_by_prop('type', 'domain-remote')
		    )
		);
}

reload_dnscache( $event );

exit (0);

sub allow_networks_2access_cache
{
    my %access = map { $_ => 1 } @_;

    my $dir = '/var/service/dnscache/root/ip';
    chdir $dir
      || die "Cannot chdir to $dir: $!\n";
    unless (-f "127.0.0.1")
    {
	open F,">127.0.0.1"
	  || die "Cannot add access file for loopback network: $!\n";
	close F;
    }

    opendir(ACCESS, '.') or
	die "Cannot read dnscache access directory: $!";

    foreach my $aclfile (readdir (ACCESS))
    {
	next if "$aclfile" eq "127.0.0.1";
	next if -d "$aclfile";
	if (exists $access{$aclfile})
	{
	    # Cross this one off the list so that we don't bother creating it
	    delete $access{$aclfile};
	}
	else
	{
	    # We no longer need this entry
	    unlink "$aclfile" or
		warn "Could not delete dnscache access file $dir/$aclfile: $!\n";
	}
    }
    closedir(ACCESS);

    foreach my $aclfile (keys %access)
    {
	link "127.0.0.1", $aclfile or 
	  die "Cannot add network access for $aclfile: $!\n";
    }
}

sub delegate_domains_2DNS
{
    my %delegations = @_;
    my $serversdir = '/var/service/dnscache/root/servers';
    chdir $serversdir
      || die "Cannot chdir to $serversdir: $!\n";
    unless (-f "127.in-addr.arpa")
    {
	open F,">127.in-addr.arpa"
	  || die "Cannot add delegation for loopback network: $!\n";
	print F "127.0.0.1\n";
	close F;
    }

    opendir(SERVERS, '.') or
	die "Cannot read dnscache servers directory: $!";

    foreach my $delegatefile (readdir (SERVERS))
    {
	next if "$delegatefile" eq '@';

	next if "$delegatefile" eq "127.in-addr.arpa";

	next if -d "$delegatefile";

	unless (exists $delegations{$delegatefile})
	{
	    # We no longer need this entry
	    unlink "$delegatefile" or
		warn "Could not delete dnscache domain file $delegatefile: $!\n";
	}
    }
    closedir(SERVERS);

    foreach my $delegatefile (keys %delegations)
    {
	if (-l $delegatefile)
	{
	    # Legacy symlink - we use files now
	    unlink "$delegatefile" or
		warn "Could not delete dnscache domain link $delegatefile: $!\n";
	}
	my $nameserver = $delegations{$delegatefile};

	open DELEGATE, ">$delegatefile" or
	    die "Couldn't create $delegatefile with value $nameserver\n";
	print DELEGATE "$nameserver\n";
	close DELEGATE;
    }
}

sub reload_dnscache
{
    my ($event) = @_;
    
    return if ($event eq "bootstrap-console-save");

    return if ($event =~ /post-(install|upgrade)/);

    # kill dnscache. Supervise will start a replacement, which
    # will have the new configuration. In particular, we might have
    # changed from forwarding to not forwarding DNS requests
    system("/usr/local/bin/svc", "-t", "/service/dnscache");
}

