diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-04-01 16:21:40 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-04-01 16:21:40 +0300 |
commit | 2fd19a6808a9d7b86ad294ae20ad9dcf1c4f6325 (patch) | |
tree | b8374035974b329af6dd8244b395833fae2f49b0 | |
parent | 4ccb3de29040a53fa55866c84d846639f8ac532a (diff) | |
download | dnstools-2fd19a6808a9d7b86ad294ae20ad9dcf1c4f6325.tar.gz dnstools-2fd19a6808a9d7b86ad294ae20ad9dcf1c4f6325.tar.bz2 |
nsdbimport: implement import to the database; optionally expand $GENERATE directives
-rwxr-xr-x | nsdbimport/nsdbimport | 203 |
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 @@ -22,2 +22,3 @@ use Pod::Man; use String::Regexp; +use DBI; @@ -36,4 +37,7 @@ 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; @@ -62,3 +66,3 @@ my %config; sub readconfig { - my $file = shift; + my ($file, $kw) = @_; open(my $fd, "<", $file) @@ -66,2 +70,3 @@ sub readconfig { my $line; + my $err; while (<$fd>) { @@ -86,7 +91,7 @@ sub readconfig { - # 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; @@ -94,2 +99,3 @@ sub readconfig { close $fd; + exit(EX_CONFIG) if $err; abend(EX_CONFIG, "rr-query is not defined in $file") @@ -98,2 +104,7 @@ sub readconfig { +sub istrue { + my $s = shift; + return $s =~ /^([1yt])|(yes)|(true)|(enable)$/i; +} + # ################### @@ -124,2 +135,14 @@ sub parse_named_conf { # ################### +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 { @@ -129,3 +152,3 @@ sub replvar { foreach my $x (split /,/, $opt) { - if ($x =~ /([?+\.-@])(.*)/) { + if ($x =~ /([?+\.@-])(.*)/) { $option{$1} = $2; @@ -135,5 +158,5 @@ sub replvar { - my $val = $rec->{$var}; + my $val = $rec->{$var} || ''; - if (!defined($val)) { + if ($val eq '') { if (defined($option{'-'})) { @@ -148,3 +171,3 @@ sub replvar { - if (defined($val)) { + if ($val ne '') { if ($val eq '@' and defined($option{'@'})) { @@ -162,3 +185,3 @@ 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"); @@ -167,2 +190,11 @@ sub query_expand { +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); @@ -170,5 +202,41 @@ sub query_expand { # 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)}); + } + } } @@ -187,4 +255,3 @@ sub insert_soa { $rec->{minimum} = parsetime($7, $rec->{origin}); - my $q = query_expand($tmpl, $rec); - # FIXME: SQL + sql_query($tmpl, $rec); } else { @@ -200,4 +267,3 @@ sub insert_mx { $rec->{mx} = $2; - my $q = query_expand($tmpl, $rec); - # FIXME: SQL + sql_query($tmpl, $rec); } else { @@ -213,4 +279,3 @@ sub insert_rr { my ($rec, $tmpl) = @_; - my $q = query_expand($tmpl || $config{'rr-query'}, $rec); - # FIXME: SQL + sql_query($tmpl || $config{'rr-query'}, $rec); } @@ -258,12 +323,12 @@ sub parsetime { -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}); + } +} @@ -274,3 +339,3 @@ sub zimport { if $file !~ m#^/# and defined($bind_directory); - + debug(1, "processing file $file, origin $origin"); open(my $fd, "<", $file) @@ -295,3 +360,9 @@ sub zimport { } 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 (/^\$/) { @@ -310,20 +381,12 @@ sub zimport { } - - 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 '@'; } @@ -352,4 +415,7 @@ GetOptions("h" => sub { "debug|d+" => \$debug, + "dry-run|n" => \$dry_run, "config|c=s" => \$conffile) or exit(EX_USAGE); - + +++$debug if $dry_run; + unless (defined($conffile)) { @@ -364,3 +430,41 @@ abend(EX_USAGE, "no configuration file; please use the --config option") -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); @@ -379,2 +483,5 @@ if (defined($bind_config)) { - +error('set generate=yes to enable processing of $GENERATE directives') + if ($generate_seen); + +$dbd->disconnect; |