AllKnowingDNS

 view release on metacpan or  search on metacpan

script/all-knowing-dns  view on Meta::CPAN

#!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab

use strict;
use warnings;
# These modules are in core:
use FindBin;
use lib "$FindBin::Bin/../lib";
use Getopt::Long;
use Sys::Syslog;
# All these modules are not in core:
use App::AllKnowingDNS::Util;
use App::AllKnowingDNS::Handler;
use Net::DNS::Nameserver;
use Privileges::Drop;
use v5.10;

our $VERSION = '1.7';

my $configfile = '/etc/all-knowing-dns.conf';
my $querylog = 0;

GetOptions(
    'configfile=s' => \$configfile,
    'querylog!' => \$querylog,
    'version' => sub {
        say "AllKnowingDNS v$VERSION " .
            "© 2012 Michael Stapelberg and contributors";
        exit 0;
    },
    'help' => sub {
        say "all-knowing-dns [--configfile <path>] [--querylog]";
        say "";
        say "\t--configfile <path>\tSpecifies an alternate configfile location.";
        say "\t\t\t\tThe default is /etc/all-knowing-dns.conf";
        say "\t--querylog\t\tLogs every query to stdout (for debugging).";
        say "";
        exit 0;
    },
);

openlog('all-knowing-dns', 'pid', 'daemon');
syslog('info', "AllKnowingDNS v$VERSION starting");

my $input;
my $fh;
if (!open($fh, '<', $configfile)) {
    my $errormsg = qq|Could not load configfile ("$configfile"): $!|;
    syslog('err', $errormsg);
    die $errormsg;
}
{
    local $/;
    $input = <$fh>;
}
close($fh);

my $config = App::AllKnowingDNS::Util::parse_config($input);
# TODO: sanity check config

# XXX: port configurable? better error message when running without privileges
my $ns = Net::DNS::Nameserver->new(
    LocalPort => 53,
    LocalAddr => [ $config->all_listen_addresses ],
    ReplyHandler => sub {
        App::AllKnowingDNS::Handler::reply_handler($config, $querylog, @_)
    },
    # For Net::DNS < 0.67 we need this handler, otherwise it exits on notify.
    NotifyHandler => sub { ('SERVFAIL', undef, undef, undef) },
    Verbose => 0);

# Now that we are listening, drop privileges.
drop_privileges('nobody');

$ns->main_loop;

__END__

=head1 NAME

all-knowing-dns - Tiny DNS server for IPv6 Reverse DNS

=head1 SYNOPSIS

    all-knowing-dns [--configfile <path>] [--querylog]

=head1 DESCRIPTION

AllKnowingDNS provides reverse DNS for IPv6 networks which use SLAAC
(autoconf), e.g. for a /64 network.

The problem with IPv6 reverse DNS and traditional nameservers is that the
nameserver requires you to provide a zone file. Assuming you want to provide
RDNS for a /64 network, you have 2**64 = 18446744073709551616 different usable
IP addresses (a little less if you are using SLAAC). Providing a zone file for
that, even in a very terse notation, would consume a huge amount of disk space
and could not possibly be held in the memory of the computers we have nowadays.

AllKnowingDNS instead generates PTR and AAAA records on the fly. You only
configure which network you want to serve and what your entries should look
like.

=head1 OPTIONS

=over 4

=item B<--configfile=I<path>>

Use I<path> instead of /etc/all-knowing-dns.conf as configuration file.

=item B<--querylog>

Enable logging every query to stdout (for debugging).

=back

=head1 CONFIGURATION FILE (/etc/all-knowing-dns.conf)

The configuration file is wonderfully simple:

    # Configuration file for AllKnowingDNS v1.7

    listen 79.140.39.197
    listen 2001:4d88:100e:1::3
    
    # RaumZeitLabor
    network 2001:4d88:100e:ccc0::/64
        resolves to ipv6-%DIGITS%.nutzer.raumzeitlabor.de
        with upstream 2001:4d88:100e:1::2
    
    # Chaostreff
    network 2001:4d88:100e:cd1::/64
        resolves to ipv6-%DIGITS%.treff.noname-ev.de

This example contains all configuration directives. Let's go over them one by
one:

=over 4

=item B<listen I<address>>

Listens on the given I<address> (IPv4 and IPv6 is supported) on port 53.

=item B<network I<network>>

Specifies that queries for PTR records within the given network should be
answered (any query for an unconfigured network will be answered with
NXDOMAIN). You need to specify at least the B<resolves to> directive
afterwards.

=item B<resolves to I<address>>

Specifies the address to which PTR records should resolve. The address needs to
contain %DIGITS% exactly once. When answering AAAA queries, %DIGITS% will be
parsed and converted back to an IPv6 address.

Example:

    network 2001:4d88:100e:ccc0::/64
        resolves to ipv6-%DIGITS%.nutzer.raumzeitlabor.de

Example query:

    The PTR query 2001:4d88:100e:ccc0:216:eaff:fecb:826 will resolve to
    ipv6-0216eafffecb0826.nutzer.raumzeitlabor.de

=item B<with upstream I<address>>

Before answering a PTR query for this network, AllKnowingDNS will ask the DNS
server at I<address> first, appending .upstream to the query.

Example:



( run in 0.386 second using v1.01-cache-2.11-cpan-5511b514fd6 )