summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2015-04-01 13:21:40 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2015-04-01 13:21:40 (GMT)
commit2fd19a6808a9d7b86ad294ae20ad9dcf1c4f6325 (patch) (side-by-side diff)
treeb8374035974b329af6dd8244b395833fae2f49b0
parent4ccb3de29040a53fa55866c84d846639f8ac532a (diff)
downloaddnstools-2fd19a6808a9d7b86ad294ae20ad9dcf1c4f6325.tar.gz
dnstools-2fd19a6808a9d7b86ad294ae20ad9dcf1c4f6325.tar.bz2
nsdbimport: implement import to the database; optionally expand $GENERATE directives
Diffstat (more/less context) (ignore whitespace changes)
-rwxr-xr-xnsdbimport/nsdbimport203
1 files changed, 155 insertions, 48 deletions
diff --git a/nsdbimport/nsdbimport b/nsdbimport/nsdbimport
index a1db672..da6b501 100755
--- a/nsdbimport/nsdbimport
+++ b/nsdbimport/nsdbimport
@@ -20,6 +20,7 @@ use File::Basename;
use Pod::Usage;
use Pod::Man;
use String::Regexp;
+use DBI;
use constant EX_OK => 0;
use constant EX_USAGE => 64; # command line usage error
@@ -34,8 +35,11 @@ use constant EX_CONFIG => 78; # configuration error
my $progname = basename($0); # This script name;
my $progdescr = "converts BIND zone files to SQL database";
my $debug;
+my $dry_run;
my $bind_directory;
my $bind_config;
+my $dbd;
+my $generate_seen;
sub error {
my $msg = shift;
@@ -60,10 +64,11 @@ sub abend {
my %config;
sub readconfig {
- my $file = shift;
+ my ($file, $kw) = @_;
open(my $fd, "<", $file)
or abend(EX_UNAVAILABLE, "can't open configuration file $file: $!");
my $line;
+ my $err;
while (<$fd>) {
++$line;
chomp;
@@ -84,18 +89,24 @@ sub readconfig {
}
my ($k,$v) = ($1, $2);
- # unless (exists($config{$k})) {
- # error("$file:$line: unknown keyword $k");
- # next;
- # }
-
+ unless (exists($kw->{$k})) {
+ error("$file:$line: unknown keyword $k");
+ ++$err;
+ next;
+ }
$config{$k} = $v;
}
close $fd;
+ exit(EX_CONFIG) if $err;
abend(EX_CONFIG, "rr-query is not defined in $file")
unless defined($config{'rr-query'});
}
+sub istrue {
+ my $s = shift;
+ return $s =~ /^([1yt])|(yes)|(true)|(enable)$/i;
+}
+
# ###################
sub parse_named_conf {
my %zone;
@@ -122,20 +133,32 @@ sub parse_named_conf {
return %files;
}
# ###################
+my $rx_class = array_to_regexp('IN', 'HS', 'CH');
+my @rrtypes = ('A', 'AAAA', 'A6', 'AFSDB',
+ 'CNAME', 'DNAME', 'DNSKEY', 'DS',
+ 'EUI48', 'EUI64', 'HINFO', 'ISDN',
+ 'KEY', 'LOC', 'MX', 'NAPTR',
+ 'NS', 'NSEC', 'NXT', 'NXT',
+ 'PTR', 'RP', 'RRSIG', 'RT',
+ 'SIG', 'SOA', 'SPF', 'SRV',
+ 'TXT', 'WKS', 'X25');
+my $rx_rr = array_to_regexp(@rrtypes);
+
+# ###################
sub replvar {
my ($rec, $var, $opt) = @_;
my %option;
if (defined($opt)) {
foreach my $x (split /,/, $opt) {
- if ($x =~ /([?+\.-@])(.*)/) {
+ if ($x =~ /([?+\.@-])(.*)/) {
$option{$1} = $2;
}
}
}
- my $val = $rec->{$var};
+ my $val = $rec->{$var} || '';
- if (!defined($val)) {
+ if ($val eq '') {
if (defined($option{'-'})) {
$val = $option{'-'};
} elsif (defined($option{'?'})) {
@@ -146,7 +169,7 @@ sub replvar {
$val = $option{'+'};
}
- if (defined($val)) {
+ if ($val ne '') {
if ($val eq '@' and defined($option{'@'})) {
$val = $option{'@'} || $rec->{origin};
$val .= '.' unless $val =~ /\.$/;
@@ -160,17 +183,62 @@ sub replvar {
sub query_expand {
my ($q, $rec) = @_;
- $q =~ s/\$\{([\w_-]+)(?::(.+))?\}/replvar($rec, $1, $2)/gex;
+ $q =~ s/\$\{([\w_-]+)(?::(.+?))?\}/replvar($rec, $1, $2)/gex;
debug(2, "$rec->{locus}: $q");
return $q;
}
+sub sql_query {
+ my ($tmpl, $rec) = @_;
+ my $q = query_expand($tmpl, $rec);
+ unless ($dry_run) {
+ $dbd->do($q)
+ or abend(EX_UNAVAILABLE, $dbd->errstr."\nFailed query: $q");
+ }
+}
+
# generate($expr, $origin, $file, $line);
# $GENERATE start-stop[/step] lhs[{offset[,width[,type]]}] rr-type rhs[{offset[,width[,type]]}]
# http://www.zytrax.com/books/dns/ch8/generate.html
+
+sub format_part {
+ my ($i, $xhs, $xwid, $xtyp, $loc) = @_;
+ my $ctr;
+
+ if ($xtyp =~ /^[doxX]$/) {
+ $ctr = sprintf($xwid > 0 ? "%0${xwid}$xtyp" : "%$xtyp", $i);
+ } elsif ($xtyp =~ /[nN]/) {
+ my $f = $xtyp eq 'n' ? '%x' : '%X';
+ my @a = reverse(split(//, sprintf("%x", $i)));
+ unshift @a, '0' while (($#a+1) < $xwid);
+ $ctr = join('.', @a);
+ } else {
+ error("invalid type $xtyp in \$GENERATE", prefix => $loc);
+ }
+ $xhs =~ s/\$/$ctr/;
+ return $xhs;
+}
+
sub generate {
- my ($expr, $origin, $file, $line) = @_;
- # FIXME
+ my ($expr, $origin, $ttl, $file, $line) = @_;
+ my $p = '([^\s\{]+)(?:\{(\d+)(?:(?:,(\d+))(?:,([doxXnN])))\})?';
+ if ($expr =~ /(\d+)-(\d+)(?:\/(\d+))?\s+$p\s+($rx_rr)\s+$p/) {
+ my ($start,$stop,$step,
+ $lhs,$loff,$lwid,$ltyp,
+ $rr,
+ $rhs,$roff,$rwid,$rtyp) = ($1, $2, $3 || 1,
+ $4, $5 || 0, $6 || 0, $7 || 'd',
+ $8,
+ $9, $10 || 0, $11 || 0, $12 || 'd');
+ for (my $i = $start; $i <= $stop; $i += $step) {
+ import_record({origin => $origin,
+ locus => "$file:$line",
+ ttl => $ttl,
+ rr => $rr,
+ label => format_part($i + $loff,$lhs,$lwid,$ltyp),
+ data => format_part($i + $roff,$rhs,$rwid,$rtyp)});
+ }
+ }
}
# ###################
@@ -185,8 +253,7 @@ sub insert_soa {
$rec->{retry} = parsetime($5, $rec->{origin});
$rec->{expire} = parsetime($6, $rec->{origin});
$rec->{minimum} = parsetime($7, $rec->{origin});
- my $q = query_expand($tmpl, $rec);
- # FIXME: SQL
+ sql_query($tmpl, $rec);
} else {
error("malformed SOA: '$rec->{data}'",
prefix => "warning: $rec->{locus}");
@@ -198,8 +265,7 @@ sub insert_mx {
if ($rec->{data} =~ /^(\d+)\s+(.+)$/) {
$rec->{mx_priority} = $1;
$rec->{mx} = $2;
- my $q = query_expand($tmpl, $rec);
- # FIXME: SQL
+ sql_query($tmpl, $rec);
} else {
error("malformed MX: '$rec->{data}'",
prefix => "warning: $rec->{locus}");
@@ -211,8 +277,7 @@ my %rrfun = (SOA => \&insert_soa,
sub insert_rr {
my ($rec, $tmpl) = @_;
- my $q = query_expand($tmpl || $config{'rr-query'}, $rec);
- # FIXME: SQL
+ sql_query($tmpl || $config{'rr-query'}, $rec);
}
# ###################
@@ -256,23 +321,23 @@ sub parsetime {
return $t;
}
-my $rx_class = array_to_regexp('IN', 'HS', 'CH');
+sub import_record {
+ my $rec = shift;
+ my $kw = lc($rec->{rr}).'-query';
-my $rx_rr = array_to_regexp('A', 'AAAA', 'A6', 'AFSDB',
- 'CNAME', 'DNAME', 'DNSKEY', 'DS',
- 'EUI48', 'EUI64', 'HINFO', 'ISDN',
- 'KEY', 'LOC', 'MX', 'NAPTR',
- 'NS', 'NSEC', 'NXT', 'NXT',
- 'PTR', 'RP', 'RRSIG', 'RT',
- 'SIG', 'SOA', 'SPF', 'SRV',
- 'TXT', 'WKS', 'X25');
+ if (defined($rrfun{$rec->{rr}}) and defined($config{$kw})) {
+ &{$rrfun{$rec->{rr}}}($rec, $config{$kw});
+ } else {
+ insert_rr($rec, $config{$kw});
+ }
+}
sub zimport {
my ($file, $origin) = @_;
$file = "$bind_directory/$file"
if $file !~ m#^/# and defined($bind_directory);
-
+ debug(1, "processing file $file, origin $origin");
open(my $fd, "<", $file)
or abend(EX_NOINPUT, "can't open input file $file: $!");
my $line;
@@ -293,7 +358,13 @@ sub zimport {
} elsif (/^\$INCLUDE\s+(.+)/) {
zimport($1, $origin);
} elsif (/^\$GENERATE\s+(.+)/) {
- generate($1, $origin, $file, $line);
+ if (istrue($config{generate})) {
+ generate($1, $origin, $ttl, $file, $line);
+ } else {
+ error('$GENERATE ignored',
+ prefix => "$file:$line: warning");
+ $generate_seen = 1;
+ }
} elsif (/^\$/) {
error("ignoring unrecognized directive",
prefix => "$file:$line: warning");
@@ -308,24 +379,16 @@ sub zimport {
redo;
}
}
-
- my %record = (origin => $origin,
- label => $1,
- ttl => parsetime($2, "$file:$line") || $ttl,
- rr => $rr,
- data => $4,
- locus => "$file:$line");
- my $kw = lc($rr).'-query';
-
- if (defined($rrfun{$rr}) and defined($config{$kw})) {
- &{$rrfun{$rr}}(\%record, $config{$kw});
- } else {
- insert_rr(\%record, $config{$kw});
- }
+ import_record({origin => $origin,
+ label => $1,
+ ttl => parsetime($2, "$file:$line") || $ttl,
+ rr => $rr,
+ data => $4,
+ locus => "$file:$line"});
if ($rr eq 'SOA') {
- $origin = $record{label} unless $record{label} eq '@';
+ $origin = $1 unless $1 eq '@';
}
}
}
@@ -350,8 +413,11 @@ GetOptions("h" => sub {
"from-config=s" => \$bind_config,
"origin=s" => \$origin,
"debug|d+" => \$debug,
+ "dry-run|n" => \$dry_run,
"config|c=s" => \$conffile) or exit(EX_USAGE);
-
+
+++$debug if $dry_run;
+
unless (defined($conffile)) {
($conffile) = grep { -e $_ } (
'.nsdbimport.conf',
@@ -362,7 +428,45 @@ unless (defined($conffile)) {
abend(EX_USAGE, "no configuration file; please use the --config option")
unless defined $conffile;
-readconfig $conffile;
+my %kw = ('database' => 1,
+ 'host' => 1,
+ 'port' => 1,
+ 'db-params' => 1,
+ 'user' => 1,
+ 'password' => 1,
+ 'db-default-file' => 1,
+ 'generate' => 1,
+ 'rr-query' => 1);
+
+foreach my $rr (@rrtypes) {
+ $kw{lc($rr).'-query'} = 1;
+}
+
+readconfig($conffile, \%kw);
+
+debug(1, "connecting to the database");
+
+my $arg = "";
+$arg .= ":database=$config{database}" if defined $config{database};
+$arg .= ":host=$config{host}" if defined $config{host};
+$arg .= ":port=$config{port}" if defined $config{port};
+$arg .= ":$config{'db-params'}" if defined $config{'db-params'};
+$arg .= ":;mysql_read_default_file=$config{'db-default-file'}"
+ if defined $config{'db-default-file'};
+
+if (!$arg
+ or (!defined($config{user}) and !defined($config{'db-default-file'}))) {
+ my $my_cnf = "$ENV{HOME}/.my.cnf";
+ if (-r $my_cnf) {
+ debug(1, "using mysql option file $my_cnf");
+ $arg .= ";mysql_read_default_file=$my_cnf";
+ }
+}
+$arg = 'DBI:mysql'.$arg;
+
+$dbd = DBI->connect($arg, $config{user}, $config{password},
+ { PrintError => 0, AutoCommit => 1})
+ or exit(EX_UNAVAILABLE);
if (defined($bind_config)) {
abend(EX_USAGE, "extra input files") if $#ARGV >= 0;
@@ -377,4 +481,7 @@ if (defined($bind_config)) {
}
}
-
+error('set generate=yes to enable processing of $GENERATE directives')
+ if ($generate_seen);
+
+$dbd->disconnect;

Return to:

Send suggestions and report system problems to the System administrator.