source: trunk/bind2hosts@ 800

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

/trunk

Commit 2nd archived iteration of bind2hosts in development

  • Property svn:executable set to *
File size: 5.5 KB
Line 
1#!/usr/bin/perl
2# Convert a BIND zone file to a hosts 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
24# push "the directory the script is in" into @INC
25use FindBin;
26use lib "$FindBin::RealBin/";
27
28use DNSDB;
29
30my $dnsdb = new DNSDB;
31my $doimport = 0;
32
33#print Dumper(\%reverse_typemap);
34
35my $zname = shift @ARGV;
36my $rev = 'n';
37my $zid;
38
39my %amap;
40my %namemap;
41
42if ($zname =~ /\.arpa\.?$/ || $zname =~ m,^[\d./]+$,) {
43 $rev = 'y';
44 $zname = _zone2cidr($zname) if $zname =~ /\.arpa\.?$/;
45 $zid = $dnsdb->revID($zname,':ANY:');
46 if ($zid) {
47 $zname = new NetAddr::IP $zname;
48 $zname = DNSDB::_ZONE($zname, 'ZONE', 'r', '.').($zname->{isv6} ? '.ip6.arpa' : '.in-addr.arpa');
49 }
50} else {
51 $zid = $dnsdb->domainID($zname,':ANY:');
52}
53
54die "zone $zname not on file\n" if !$zid;
55
56# still no sane way to expose a human-friendly view tag on the command line.
57my $view = shift @ARGV;
58$view = '' if !$view;
59
60##fixme: retrieve defttl from SOA record
61my $zonettl = 900;
62my $defttl = $zonettl;
63my $recbase = $zname; # to append to unqualified names
64
65# need to spin up a full state machine-ish thing, because BIND zone files are all about context
66while (<>) {
67 chomp;
68 next if /^\s*$/;
69 next if /^\s*;/;
70 next if /^\s*\)/; # SOA closing (possibly other records too?)
71 # arguably should do some more targeted voodoo when parsing the SOA details
72
73 if (my ($macro,$mdetail) = (/^\s*\$(TTL|ORIGIN|INCLUDE)\s+(.+)/) ) {
74 # macro sort of thing; $TTL and $ORIGIN most common. $INCLUDE is a thing, expect it to be rare in live use tho
75 if ($macro eq 'TTL') {
76 # irrelevant for a hosts file
77 } elsif ($macro eq 'ORIGIN') {
78 # $ORIGIN supports cascading/nesting, by watching for fully-qualified names vs partial names.
79 if ($mdetail =~ /\.$/) {
80 $origin = $mdetail;
81 } else {
82 # append current origin to unqualified origin
83 $origin = "$mdetail.$origin";
84 }
85 }
86##fixme: should arguably handle $INCLUDE
87# probably NOT going to handle $GENERATE, since that type of record set is best handled in DNS
88 next;
89 }
90
91 my $origrec = $_;
92
93##fixme: convert to optional skipfile?
94# skip stale records that have no value
95#next if /^ip-\d+-\d+-\d+/;
96#next if /^ip.pre.fix.\d+.static.colo/;
97
98 # leading whitespace indicates "same label as last record"
99 if (/^\s/) {
100 $curlabel = $prevlabel;
101 } else {
102 ($curlabel) = /^([\w\@_.-]+)\s/;
103 }
104
105 # magic name!
106 $curlabel = "$zname." if $curlabel eq '@';
107
108 # append $ORIGIN if name is not fully qualified.
109 if ($curlabel !~ /\.$/) {
110 $curlabel .= $origin;
111 }
112
113 # check for zone scope. skip bad records.
114 if ($curlabel !~ /$zname.$/) {
115 warn "bad record $origrec, maybe bad \$ORIGIN?\n";
116 next;
117 }
118
119 my ($name) = /([\w_.-]+)\s/;
120 # append zone name to record name if missing AND not dot-terminated;
121 # this happens automagically for forward zones, but not reverse because Reasons. (fixme?)
122 # suck up and deal with the error if the dot-termiated name is out of zone; should be
123 # impossible with valid BIND zone file but...
124 $name .= ".$zname" if $name !~ /$zname$/ && $zname !~ /\.$/;
125$name = $zname if /^\s*IN/;
126 s/([\w_.-]+)\s+//;
127 my ($class) = /(IN|CS|CH|HS)\s/;
128 if ($class) {
129 if ($class ne 'IN') {
130 print "Non-Internet class records not supported, you weirdo\n";
131 next;
132 }
133 s/(IN|CS|CH|HS)\s+//;
134 } else {
135 $class = 'IN' if !$class;
136 }
137 my ($ttl) = /(\d+)?\s/;
138 if (defined $ttl) {
139 # TTL may be zero
140 s/(\d+)?\s+//;
141 } else {
142 # Fall back to zone default TTL
143 $ttl = $zonettl;
144 }
145 my ($type) = /([A-Z-]+)\s/;
146 if (!$reverse_typemap{$type}) {
147 print "Unknown type $type, skipping\n";
148 next;
149 }
150 my $itype = $reverse_typemap{$type};
151 s/([A-Z-]+)\s+//;
152 chomp;
153 my $rdata = $_;
154
155 # Quotes may arguably be syntactically required, but they're not actually part of the record data
156 if ($itype == 16) {
157 $rdata =~ s/^"//;
158 $rdata =~ s/"$//;
159 }
160
161if ($type eq 'A') {
162# if ($amap{$name}) {
163# print "urp: dupe name $name $rdata\n";
164# } else {
165 push @{$amap{$name}}, $rdata;
166# }
167 push @{$namemap{$rdata}}, $name;
168}
169
170no warnings qw(uninitialized);
171#print "parsed: '$name' '$class' '$ttl' '$type'->'$itype' '$rdata'\n";
172#print;
173#;imap IN 900 CNAME deepnet.cx.
174##fixme: not sure how to handle the case where someone leaves off the class.
175# if ($doimport) {
176# my ($code, $msg);
177# if ($rev eq 'n') {
178# ($code,$msg) = $dnsdb->addRec('n', $rev, $zid, \$name, \$itype, \$rdata, $ttl);
179# } else {
180# ($code,$msg) = $dnsdb->addRec('n', $rev, $zid, \$rdata, \$itype, \$name, $ttl);
181# }
182# print "$code: $msg\n";
183# }
184
185}
186
187
188#print Dumper \%amap;
189foreach my $n (keys %amap) {
190 foreach my $ip (@{$amap{$n}}) {
191#print "$ip\t$n\n";
192 push @{$namemap{$ip}}, $n unless grep $n, @{$namemap{$ip}};
193 }
194}
195
196#print Dumper \%namemap;
197foreach my $ip (sort keys %namemap) {
198 print "$ip\t".join(' ', @{$namemap{$ip}})."\n";
199}
Note: See TracBrowser for help on using the repository browser.