#!/usr/bin/perl

use strict;
use BerkeleyDB;
use DBI;

my $dsn       = 'DBI:mysql:mydns:127.0.0.1';
my $dbpath    = '/var/spool/myns';
my $dbfile    = 'DLZ.db';

my $dbh = DBI->connect($dsn, "dnsadmin", "hah!")
    or die "Can't connect to $dsn";

my $soaq      = "SELECT id,origin,ns,mbox,serial,refresh,retry,expire,minimum,ttl";
$soaq         .=    " FROM soa ORDER BY origin";
my $sth       = $dbh->prepare($soaq);

my $rrq       = "SELECT name,type,data,aux,ttl FROM rr WHERE zone = ?";
my $rrst      = $dbh->prepare($rrq);

my (%DNSdata, %DNSzone, %DNSaxfr, %DNSclient, $rid);

&initBDB('P');

$sth->execute();

# Find all zones

while (my $soa = $sth->fetchrow_hashref) {
    my $line;
    my $zone = $soa->{origin};

    $zone =~ s/\.$//;   # Remove trailing period

    # Add a zone; the key is a reversed zone name and its value is empty

    $DNSzone{reverse $zone} = "";

    # Add the zone's data. First the SOA ...

    $line = $soa->{ttl} . ' ';
    $line .= 'SOA ';
    $line .= $soa->{ns} . ' ';
    $line .= $soa->{mbox} . ' ';
    $line .= $soa->{serial} . ' ';
    $line .= $soa->{refresh} . ' ';
    $line .= $soa->{retry} . ' ';
    $line .= $soa->{expire} . ' ';
    $line .= $soa->{minimum};

    $DNSdata{"$zone @"} = rec("@ $line");

    # ... and then all the other RR

    $rrst->execute($soa->{id}) or warn;
    while (my $rr = $rrst->fetchrow_hashref) {

        my $host = $rr->{name};

        $host = '@' if (!$rr->{name});

        $line = "$host ";
        $line .= $rr->{ttl} . ' ';
        $line .= $rr->{type} . ' ';
        $line .= $rr->{aux} . ' ' if ($rr->{type} eq 'MX');

        if ($rr->{type} eq 'TXT') {
            $line .= '"' . $rr->{data} . '"';
        } else {
            $line .= $rr->{data};
        }

        $DNSdata{"${zone} ${host}"} = rec($line);

        # Add zone/host pointers to the AXFR data, taking care
        # to ensure key/value uniqueness for this database

        if (!defined($DNSaxfr{$zone}) && ($DNSaxfr{$zone} ne "$host")) {
            $DNSaxfr{$zone} = $host;
        }

    }

    # Allow AXFR for clients? This should use the optional `xfer'
    # column of MyDNS, but we use 3 static addresses.

    $DNSclient{$zone} = '127.0.0.1';
    $DNSclient{$zone} = '192.168.1.20';
    $DNSclient{$zone} = '192.168.1.164';
}

untie %DNSdata;
untie %DNSzone;
untie %DNSaxfr;
untie %DNSclient;

$sth->finish();
$rrst->finish();
$dbh->disconnect();
exit;

# Return a record for BDBHPT containing a unique BDB replication
# id, a single SPACE and the data.
sub rec {
    my ($rr) = @_;

    $rid++;          # give unique record-id
    return "${rid} ${rr}";
}

sub fatal {
    my ($dbname) = @_;
        die "Cannot create $dbfile:$dbname: " .  $BerkeleyDB::Error . "\n";
}

sub initBDB {
    my $mode = shift;

    my %DBflags = (
       C => DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE,
       T => DB_INIT_TXN | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_CREATE,
       P => DB_PRIVATE  | DB_INIT_MPOOL | DB_CREATE,
    );

    my $BDB = new BerkeleyDB::Env
        -Home      => $dbpath,
        -Flags     => $DBflags{$mode},
        -ErrFile   => *STDERR,
        -Verbose   => 1 ;

    tie %DNSdata, 'BerkeleyDB::Hash',
        -Env       => $BDB,
        -Flags     => DB_CREATE,
        -Property  => DB_DUP | DB_DUPSORT,
        -Filename  => "$dbpath/$dbfile",
        -Subname   => "dns_data"
        or fata('dns_data');

    tie %DNSzone, 'BerkeleyDB::Btree',
        -Env       => $BDB,
        -Flags     => DB_CREATE,
        -Filename  => "$dbpath/$dbfile",
        -Subname   => "dns_zone",
        or fata('dns_zone');

    tie %DNSaxfr, 'BerkeleyDB::Hash',
        -Env       => $BDB,
        -Flags     => DB_CREATE,
        -Property  => DB_DUP | DB_DUPSORT,
        -Filename  => "$dbpath/$dbfile",
        -Subname   => "dns_xfr",
        or fata('dns_xfr');

    tie %DNSclient, 'BerkeleyDB::Hash',
        -Env       => $BDB,
        -Flags     => DB_CREATE,
        -Property  => DB_DUP | DB_DUPSORT,
        -Filename  => "$dbpath/$dbfile",
        -Subname   => "dns_client",
        or fata('dns_client');
}

