|
Packit Service |
ae04f2 |
#!/usr/bin/perl -w
|
|
Packit Service |
ae04f2 |
use strict;
|
|
Packit Service |
ae04f2 |
use BerkeleyDB;
|
|
Packit Service |
ae04f2 |
use Getopt::Long;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $opt = {};
|
|
Packit Service |
ae04f2 |
if (!GetOptions($opt, qw/bdb|b:s input|i:s zones|z:s help|h/)) {
|
|
Packit Service |
ae04f2 |
usage('GetOptions processing failed.');
|
|
Packit Service |
ae04f2 |
exit 1;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if ($opt->{help}) {
|
|
Packit Service |
ae04f2 |
usage();
|
|
Packit Service |
ae04f2 |
exit 0;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $db_file = $opt->{bdb};
|
|
Packit Service |
ae04f2 |
if (!defined $db_file || $db_file eq '') {
|
|
Packit Service |
ae04f2 |
usage('Please specify an output BerkeleyDB filename.');
|
|
Packit Service |
ae04f2 |
exit 1;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $input_file = $opt->{input};
|
|
Packit Service |
ae04f2 |
if (!defined $input_file || $input_file eq '') {
|
|
Packit Service |
ae04f2 |
usage('Please specify an input records file.');
|
|
Packit Service |
ae04f2 |
exit 1;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $zone_list = $opt->{zones};
|
|
Packit Service |
ae04f2 |
if (!defined $zone_list || $zone_list eq '') {
|
|
Packit Service |
ae04f2 |
usage('Please specify a space separated list of zones');
|
|
Packit Service |
ae04f2 |
exit 1;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $records = [];
|
|
Packit Service |
ae04f2 |
my $unique_names = [];
|
|
Packit Service |
ae04f2 |
populate_records(records=>$records, input_file=>$input_file, unique_names=>$unique_names);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $flags = DB_CREATE;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $dns_data = new BerkeleyDB::Hash
|
|
Packit Service |
ae04f2 |
-Filename => $db_file,
|
|
Packit Service |
ae04f2 |
-Flags => $flags,
|
|
Packit Service |
ae04f2 |
-Property => DB_DUP | DB_DUPSORT,
|
|
Packit Service |
ae04f2 |
-Subname => "dns_data"
|
|
Packit Service |
ae04f2 |
|| die "Cannot create dns_data: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $replId = 0;
|
|
Packit Service |
ae04f2 |
my @zones = split(/\s+/, $zone_list);
|
|
Packit Service |
ae04f2 |
foreach my $zone (@zones) {
|
|
Packit Service |
ae04f2 |
foreach my $r (@$records) {
|
|
Packit Service |
ae04f2 |
my $name = $r->{name};
|
|
Packit Service |
ae04f2 |
my $ttl = $r->{ttl};
|
|
Packit Service |
ae04f2 |
my $type = $r->{type};
|
|
Packit Service |
ae04f2 |
my $data = $r->{data};
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
$data =~ s/\%zone\%/$zone/g;
|
|
Packit Service |
ae04f2 |
$data =~ s/\%driver\%/bdbhpt-dynamic/g;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $row_name = "$zone $name";
|
|
Packit Service |
ae04f2 |
my $row_value = "$replId $name $ttl $type $data";
|
|
Packit Service |
ae04f2 |
if ($dns_data->db_put($row_name, $row_value) != 0) {
|
|
Packit Service |
ae04f2 |
die "Cannot add record '$row_name' -> '$row_value' to dns_data: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
$replId++;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
$dns_data->db_close();
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $dns_xfr = new BerkeleyDB::Hash
|
|
Packit Service |
ae04f2 |
-Filename => $db_file,
|
|
Packit Service |
ae04f2 |
-Flags => $flags,
|
|
Packit Service |
ae04f2 |
-Property => DB_DUP | DB_DUPSORT,
|
|
Packit Service |
ae04f2 |
-Subname => "dns_xfr"
|
|
Packit Service |
ae04f2 |
or die "Cannot create dns_xfr: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
foreach my $zone (@zones) {
|
|
Packit Service |
ae04f2 |
foreach my $name (@$unique_names) {
|
|
Packit Service |
ae04f2 |
if ($dns_xfr->db_put($zone, $name) != 0) {
|
|
Packit Service |
ae04f2 |
die "Cannot add record '$zone' -> '$name' to dns_xfr: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
$dns_xfr->db_close();
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $dns_client = new BerkeleyDB::Hash
|
|
Packit Service |
ae04f2 |
-Filename => $db_file,
|
|
Packit Service |
ae04f2 |
-Flags => $flags,
|
|
Packit Service |
ae04f2 |
-Property => DB_DUP | DB_DUPSORT,
|
|
Packit Service |
ae04f2 |
-Subname => "dns_client"
|
|
Packit Service |
ae04f2 |
or die "Cannot create dns_client: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
foreach my $zone (@zones) {
|
|
Packit Service |
ae04f2 |
my $ip = '127.0.0.1';
|
|
Packit Service |
ae04f2 |
if ($dns_client->db_put($zone, $ip) != 0) {
|
|
Packit Service |
ae04f2 |
die "Cannot add record '$zone' -> '$ip' to dns_client: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
$dns_client->db_close();
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my $dns_zone = new BerkeleyDB::Btree
|
|
Packit Service |
ae04f2 |
-Filename => $db_file,
|
|
Packit Service |
ae04f2 |
-Flags => $flags,
|
|
Packit Service |
ae04f2 |
-Property => 0,
|
|
Packit Service |
ae04f2 |
-Subname => "dns_zone"
|
|
Packit Service |
ae04f2 |
or die "Cannot create dns_zone: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
foreach my $zone (@zones) {
|
|
Packit Service |
ae04f2 |
my $reversed_zone = reverse($zone);
|
|
Packit Service |
ae04f2 |
if ($dns_zone->db_put($reversed_zone, "1") != 0) {
|
|
Packit Service |
ae04f2 |
die "Cannot add record '$reversed_zone' -> '1' to dns_zone: $BerkeleyDB::Error";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
};
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
$dns_zone->db_close();
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
exit 0;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
sub usage {
|
|
Packit Service |
ae04f2 |
my ($message) = @_;
|
|
Packit Service |
ae04f2 |
if (defined $message && $message ne '') {
|
|
Packit Service |
ae04f2 |
print STDERR $message . "\n\n";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
print STDERR "usage: $0 --bdb=<bdb-file> --input=<input-file> --zones=<zone-list>\n\n";
|
|
Packit Service |
ae04f2 |
print STDERR "\tbdb-file: The output BerkeleyDB file you wish to create and use with bdbhpt-dynamic\n\n";
|
|
Packit Service |
ae04f2 |
print STDERR "\tinput-file: The input text-file containing records to populate within your zones\n\n";
|
|
Packit Service |
ae04f2 |
print STDERR "\tzone-list: The space-separated list of zones you wish to create\n\n";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
sub populate_records {
|
|
Packit Service |
ae04f2 |
my (%args) = @_;
|
|
Packit Service |
ae04f2 |
my $records = $args{records};
|
|
Packit Service |
ae04f2 |
my $input_file = $args{input_file};
|
|
Packit Service |
ae04f2 |
my $unique_names = $args{unique_names};
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
my %unique;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
open(RECORDS, $input_file) || die "unable to open $input_file: $!";
|
|
Packit Service |
ae04f2 |
while (<RECORDS>) {
|
|
Packit Service |
ae04f2 |
chomp;
|
|
Packit Service |
ae04f2 |
s/\#.*$//;
|
|
Packit Service |
ae04f2 |
s/^\s+//;
|
|
Packit Service |
ae04f2 |
if ($_ eq '') {
|
|
Packit Service |
ae04f2 |
next;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
my ($name, $ttl, $type, $data) = split(/\s+/, $_, 4);
|
|
Packit Service |
ae04f2 |
my $record = { name=>$name, ttl=>$ttl, type=>$type, data=>$data };
|
|
Packit Service |
ae04f2 |
if (validate_record($record)) {
|
|
Packit Service |
ae04f2 |
push @$records, $record;
|
|
Packit Service |
ae04f2 |
$unique{$name} = 1;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
close(RECORDS);
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
foreach my $name (sort keys %unique) {
|
|
Packit Service |
ae04f2 |
push @$unique_names, $name;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
# This could probably do more in-depth tests, but these tests are better than nothing!
|
|
Packit Service |
ae04f2 |
sub validate_record {
|
|
Packit Service |
ae04f2 |
my ($r) = @_;
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
# http://en.wikipedia.org/wiki/List_of_DNS_record_types
|
|
Packit Service |
ae04f2 |
my @TYPES = qw/A AAAA AFSDB APL CERT CNAME DHCID DLV DNAME DNSKEY DS HIP IPSECKEY KEY KX LOC MX NAPTR NS NSEC NSEC3 NSEC3PARAM PTR RRSIG RP SIG SOA SPF SRV SSHFP TA TKEY TLSA TSIG TXT/;
|
|
Packit Service |
ae04f2 |
my $VALID_TYPE = {};
|
|
Packit Service |
ae04f2 |
foreach my $t (@TYPES) {
|
|
Packit Service |
ae04f2 |
$VALID_TYPE->{$t} = 1;
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (!defined $r->{name} || $r->{name} eq '') {
|
|
Packit Service |
ae04f2 |
die "Record name must be set";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (!defined $r->{ttl} || $r->{ttl} eq '') {
|
|
Packit Service |
ae04f2 |
die "Record TTL must be set";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if ($r->{ttl} =~ /\D/ || $r->{ttl} < 0) {
|
|
Packit Service |
ae04f2 |
die "Record TTL must be an integer 0 or greater";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (!defined $r->{type} || $r->{type} eq '') {
|
|
Packit Service |
ae04f2 |
die "Record type must be set";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
if (!$VALID_TYPE->{$r->{type}}) {
|
|
Packit Service |
ae04f2 |
die "Unsupported record type: $r->{type}";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
# Lets do some data validation for the records which will cause bind to crash if they're wrong
|
|
Packit Service |
ae04f2 |
if ($r->{type} eq 'SOA') {
|
|
Packit Service |
ae04f2 |
my $soa_error = "SOA records must take the form: 'server email refresh retry expire negative_cache_ttl'";
|
|
Packit Service |
ae04f2 |
my ($server, $email, $version, $refresh, $retry, $expire, $negative_cache_ttl) = split(/\s+/, $r->{data});
|
|
Packit Service |
ae04f2 |
if (!defined $server || $server eq '') {
|
|
Packit Service |
ae04f2 |
die "$soa_error, missing server";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (!defined $email || $email eq '') {
|
|
Packit Service |
ae04f2 |
die "$soa_error, missing email";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (!defined $refresh || $refresh eq '') {
|
|
Packit Service |
ae04f2 |
die "$soa_error, missing refresh";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if ($refresh =~ /\D/ || $refresh <= 0) {
|
|
Packit Service |
ae04f2 |
die "$soa_error, refresh must be an integer greater than 0";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (!defined $retry || $retry eq '') {
|
|
Packit Service |
ae04f2 |
die "$soa_error, missing retry";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if ($retry =~ /\D/ || $retry <= 0) {
|
|
Packit Service |
ae04f2 |
die "$soa_error, retry must be an integer greater than 0";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (!defined $expire || $expire eq '') {
|
|
Packit Service |
ae04f2 |
die "$soa_error, missing expire";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if ($expire =~ /\D/ || $expire <= 0) {
|
|
Packit Service |
ae04f2 |
die "$soa_error, expire must be an integer greater than 0";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if (!defined $negative_cache_ttl || $negative_cache_ttl eq '') {
|
|
Packit Service |
ae04f2 |
die "$soa_error, missing negative cache ttl";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
if ($negative_cache_ttl =~ /\D/ || $negative_cache_ttl <= 0) {
|
|
Packit Service |
ae04f2 |
die "$soa_error, negative cache ttl must be an integer greater than 0";
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
}
|
|
Packit Service |
ae04f2 |
|
|
Packit Service |
ae04f2 |
return 1;
|
|
Packit Service |
ae04f2 |
}
|