#
# Copyright (C) 2009-2012 John Eaglesham
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND JOHN EAGLESHAM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# JOHN EAGLESHAM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
package dlz_perl_example;
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
# Constructor. Everything after the class name can be folded into a hash of
# various options and settings. Right now only log_context and argv are
# available.
sub new {
my ( $class, %config ) = @_;
my $self = {};
bless $self, $class;
$self->{log} = sub {
my ( $level, $msg ) = @_;
DLZ_Perl::log( $config{log_context}, $level, $msg );
};
if ( $config{argv} ) { warn "Got argv: $config{argv}\n"; }
$self->{zones} = {
'example.com' => {
'@' => [
{
type => 'SOA',
ttl => 86400,
data =>
'ns1.example.com. hostmaster.example.com. 12345 172800 900 1209600 3600',
}
],
perlrr => [
{
type => 'A',
ttl => 444,
data => '1.1.1.1',
},
{
type => 'A',
ttl => 444,
data => '1.1.1.2',
}
],
perltime => [
{
code => sub {
return ['TXT', '1', time()];
},
},
],
sourceip => [
{
code => sub {
my ( $opaque ) = @_;
# Passing anything other than the proper opaque value,
# 0, or undef to this function will cause a crash (at
# best!).
my ( $addr, $port ) =
DLZ_Perl::clientinfo::sourceip( $opaque );
if ( !$addr ) { $addr = $port = 'unknown'; }
return ['TXT', '1', $addr], ['TXT', '1', $port];
},
},
],
},
};
$self->{log}->(
DLZ_Perl::LOG_INFO(),
'DLZ Perl Script: Called init. Loaded zone data: '
. Dumper( $self->{zones} )
);
return $self;
}
# Do we have data for this zone? Expects a simple true or false return value.
sub findzone {
my ( $self, $zone ) = @_;
$self->{log}->(
DLZ_Perl::LOG_INFO(),
"DLZ Perl Script: Called findzone, looking for zone $zone"
);
return exists $self->{zones}->{$zone};
}
# Return the data for a given record in a given zone. The final parameter is
# an opaque value that can be passed to DLZ_Perl::clientinfo::sourceip to
# retrieve the client source IP and port. Expected return value is an array
# of array refs, with each array ref representing one record and containing
# the type, ttl, and data in that order. Data is as it appears in a zone file.
sub lookup {
my ( $self, $name, $zone, $client_info ) = @_;
$self->{log}->(
DLZ_Perl::LOG_INFO(),
"DLZ Perl Script: Called lookup, looking for record $name in zone $zone"
);
return unless $self->{zones}->{$zone}->{$name};
my @results;
foreach my $rr ( @{ $self->{zones}->{$zone}->{$name} } ) {
if ( $rr->{'code'} ) {
my @r = $rr->{'code'}->( $client_info );
if ( @r ) {
push @results, @r;
}
} else {
push @results, [$rr->{'type'}, $rr->{'ttl'}, $rr->{'data'}];
}
}
return @results;
}
# Will we allow zone transfer for this client? Expects a simple true or false
# return value.
sub allowzonexfr {
my ( $self, $zone, $client ) = @_;
$self->{log}->(
DLZ_Perl::LOG_INFO(),
"DLZ Perl Script: Called allowzonexfr, looking for zone $zone for " .
"client $client"
);
if ( $client eq '127.0.0.1' ) { return 1; }
return 0;
}
# Note the return AoA for this method differs from lookup in that it must
# return the name of the record as well as the other data.
sub allnodes {
my ( $self, $zone ) = @_;
my @results;
$self->{log}->(
DLZ_Perl::LOG_INFO(),
"DLZ Perl Script: Called allnodes, looking for zone $zone"
);
foreach my $name ( keys %{ $self->{zones}->{$zone} } ) {
foreach my $rr ( @{ $self->{zones}->{$zone}->{$name} } ) {
if ( $rr->{'code'} ) {
my @r = $rr->{'code'}->();
# The code returns an array of array refs without the name.
# This makes things easy for lookup but hard here. We must
# iterate over each array ref and inject the name into it.
foreach my $a ( @r ) {
unshift @{$a}, $name;
}
push @results, @r;
} else {
push @results,
[$name, $rr->{'type'}, $rr->{'ttl'}, $rr->{'data'}];
}
}
}
return @results;
}
1;