source: trunk/bind-import@ 811

Last change on this file since 811 was 811, checked in by Kris Deugau, 4 years ago

/trunk

Fourth sampled iteration of bind-import

File size: 5.3 KB
Line 
1#!/usr/bin/perl
2# Import a BIND zone file
3##
4# Copyright 2020 Kris Deugau <kdeugau@deepnet.cx>
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18##
19
20use strict;
21use warnings;
22use Data::Dumper;
23
24use lib '.';
25use DNSDB;
26
27my $dnsdb = new DNSDB;
28my $doimport = 0;
29
30#print Dumper(\%reverse_typemap);
31
32my $zname = shift @ARGV;
33my $rev = 'n';
34my $zid;
35
36my %amap;
37my %namemap;
38my %cmap;
39
40if ($zname =~ /\.arpa\.?$/ || $zname =~ m,^[\d./]+$,) {
41 $rev = 'y';
42 $zname = _zone2cidr($zname) if $zname =~ /\.arpa\.?$/;
43 $zid = $dnsdb->revID($zname,':ANY:');
44 if ($zid) {
45 $zname = new NetAddr::IP $zname;
46 $zname = DNSDB::_ZONE($zname, 'ZONE', 'r', '.').($zname->{isv6} ? '.ip6.arpa' : '.in-addr.arpa');
47 }
48} else {
49 $zid = $dnsdb->domainID($zname,':ANY:');
50}
51
52die "zone $zname not on file\n" if !$zid;
53
54# still no sane way to expose a human-friendly view tag on the command line.
55my $view = shift @ARGV;
56$view = '' if !$view;
57
58##fixme: retrieve defttl from SOA record
59my $zonettl = 900;
60my $defttl = $zonettl;
61my $recbase = $zname; # to append to unqualified names
62
63# need to spin up a full state machine-ish thing, because BIND zone files are all about context
64while (<>) {
65 chomp;
66 next if /^\s*$/;
67 next if /^\s*;/;
68 if (my ($macro,$mdetail) = (/^\s*\$(TTL|ORIGIN|INCLUDE)\s+(.+)/) ) {
69 # macro sort of thing; $TTL and $ORIGIN most common. $INCLUDE is a thing, expect it to be rare in live use tho
70 if ($macro eq 'TTL') {
71 if ($mdetail =~ /^\d+$/) {
72 $defttl = $mdetail;
73 } else {
74 warn "invalid \$TTL: $_\n";
75 }
76 } elsif ($macro eq 'ORIGIN') {
77##fixme: going to skip the stupid case of "$ORIGIN com." and the like that lie
78# between . and the root domain we were told we're importing; anyone using such
79# a mess outside the root servers is clearly insane
80# handled cases:
81# $ORIGIN .
82# $ORIGIN [zonedomain].
83# $ORIGIN [subdomain.zonedomain].
84 if ($mdetail eq '.' || $mdetail =~ /$zname\.$/ || $zname =~ /$mdetail\.$/) {
85 $recbase = $mdetail;
86 } else {
87 # if we continue, we either use an $ORIGIN that's out of zone, or ignore it and potentially publish incorrect records.
88 die "bad \$ORIGIN: $_\n";
89 }
90 }
91 next;
92 }
93 # skip stale records that have no value
94 next if /^ip-192-168-1(12|20)-\d+/;
95 next if /ip.add.re.\d+\s*$/;
96 my ($name) = /([\w_.-]+)\s/;
97 # append zone name to record name if missing AND not dot-terminated;
98 # this happens automagically for forward zones, but not reverse because Reasons. (fixme?)
99 # suck up and deal with the error if the dot-termiated name is out of zone; should be
100 # impossible with valid BIND zone file but...
101 $name .= ".$zname" if $name !~ /$zname$/ && $zname !~ /\.$/;
102$name = $zname if /^\s*IN/;
103$name = $zname if /^\@/;
104 s/([\w\@_.-]+)\s+//;
105 my ($class) = /(IN|CS|CH|HS)\s/;
106 if ($class) {
107 if ($class ne 'IN') {
108 print "Non-Internet class records not supported, you weirdo\n";
109 next;
110 }
111 s/(IN|CS|CH|HS)\s+//;
112 } else {
113 $class = 'IN' if !$class;
114 }
115 my ($ttl) = /(\d+)?\s/;
116 if (defined $ttl) {
117 # TTL may be zero
118 s/(\d+)?\s+//;
119 } else {
120 # Fall back to zone default TTL
121 $ttl = $zonettl;
122 }
123 my ($type) = /([A-Z-]+)\s/;
124 if (!$reverse_typemap{$type}) {
125 print "Unknown type $type, skipping\n ($_)\n";
126 next;
127 }
128 my $itype = $reverse_typemap{$type};
129 s/([A-Z-]+)\s+//;
130 chomp;
131 my $rdata = $_;
132
133 # Quotes may arguably be syntactically required, but they're not actually part of the record data
134 if ($itype == 16) {
135 $rdata =~ s/^"//;
136 $rdata =~ s/"$//;
137 }
138
139# temp hack for hosts file
140if ($type eq 'A') {
141# if ($amap{$name}) {
142# print "urp: dupe name $name $rdata\n";
143# } else {
144 push @{$amap{$name}}, $rdata;
145# }
146 push @{$namemap{$rdata}}, $name;
147}
148if ($type eq 'CNAME') {
149 push @{$cmap{$rdata}}, $name;
150}
151
152no warnings qw(uninitialized);
153#print "parsed: '$name' '$class' '$ttl' '$type'->'$itype' '$rdata'\n";
154#print;
155#;imap IN 900 CNAME deepnet.cx.
156##fixme: not sure how to handle the case where someone leaves off the class.
157 if ($doimport) {
158 my ($code, $msg);
159 if ($rev eq 'n') {
160 ($code,$msg) = $dnsdb->addRec('n', $rev, $zid, \$name, \$itype, \$rdata, $ttl);
161 } else {
162 ($code,$msg) = $dnsdb->addRec('n', $rev, $zid, \$rdata, \$itype, \$name, $ttl);
163 }
164 print "$code: $msg\n";
165 }
166}
167
168
169#print Dumper \%amap;
170#print Dumper \%namemap;
171#print Dumper \%cmap;
172
173foreach my $n (keys %amap) {
174 foreach my $ip (@{$amap{$n}}) {
175#print "$ip $n\n";
176 push @{$namemap{$ip}}, $n unless grep $n, @{$namemap{$ip}};
177 }
178}
179
180foreach my $c (keys %cmap) {
181 if ($amap{$c}) {
182 print Dumper(\@{$amap{$c}});
183 }
184# print $amap{$c};
185}
186
187# cname targ -> IP
188
189#foreach my $ip (sort keys %namemap) {
190# print "$ip ".join(' ', @{$namemap{$ip}})."\n";
191#}
192
Note: See TracBrowser for help on using the repository browser.