source: trunk/bind2hosts@ 799

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

/trunk

Add saved version of BIND-to-hosts-file script that was intended to be
"just a quick hack". 1 of probably 7?

  • Property svn:executable set to *
File size: 5.1 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 if (my ($macro,$mdetail) = (/^\s*\$(TTL|ORIGIN|INCLUDE)\s+(.+)/) ) {
71 # macro sort of thing; $TTL and $ORIGIN most common. $INCLUDE is a thing, expect it to be rare in live use tho
72 if ($macro eq 'TTL') {
73 if ($mdetail =~ /^\d+$/) {
74 $defttl = $mdetail;
75 } else {
76 warn "invalid \$TTL: $_\n";
77 }
78 } elsif ($macro eq 'ORIGIN') {
79##fixme: going to skip the stupid case of "$ORIGIN com." and the like that lie
80# between . and the root domain we were told we're importing; anyone using such
81# a mess outside the root servers is clearly insane
82# handled cases:
83# $ORIGIN .
84# $ORIGIN [zonedomain].
85# $ORIGIN [subdomain.zonedomain].
86 if ($mdetail eq '.' || $mdetail =~ /$zname\.$/ || $zname =~ /$mdetail\.$/) {
87 $recbase = $mdetail;
88 } else {
89 # if we continue, we either use an $ORIGIN that's out of zone, or ignore it and potentially publish incorrect records.
90 die "bad \$ORIGIN: $_\n";
91 }
92 }
93 next;
94 }
95 # skip stale records that have no value
96# next if /^ip-\d+-\d+-\d+/;
97# next if /^ip.pre.fix.\d+.static.colo/;
98 my ($name) = /([\w_.-]+)\s/;
99 # append zone name to record name if missing AND not dot-terminated;
100 # this happens automagically for forward zones, but not reverse because Reasons. (fixme?)
101 # suck up and deal with the error if the dot-termiated name is out of zone; should be
102 # impossible with valid BIND zone file but...
103 $name .= ".$zname" if $name !~ /$zname$/ && $zname !~ /\.$/;
104$name = $zname if /^\s*IN/;
105 s/([\w_.-]+)\s+//;
106 my ($class) = /(IN|CS|CH|HS)\s/;
107 if ($class) {
108 if ($class ne 'IN') {
109 print "Non-Internet class records not supported, you weirdo\n";
110 next;
111 }
112 s/(IN|CS|CH|HS)\s+//;
113 } else {
114 $class = 'IN' if !$class;
115 }
116 my ($ttl) = /(\d+)?\s/;
117 if (defined $ttl) {
118 # TTL may be zero
119 s/(\d+)?\s+//;
120 } else {
121 # Fall back to zone default TTL
122 $ttl = $zonettl;
123 }
124 my ($type) = /([A-Z-]+)\s/;
125 if (!$reverse_typemap{$type}) {
126 print "Unknown type $type, skipping\n";
127 next;
128 }
129 my $itype = $reverse_typemap{$type};
130 s/([A-Z-]+)\s+//;
131 chomp;
132 my $rdata = $_;
133
134 # Quotes may arguably be syntactically required, but they're not actually part of the record data
135 if ($itype == 16) {
136 $rdata =~ s/^"//;
137 $rdata =~ s/"$//;
138 }
139
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}
148
149no warnings qw(uninitialized);
150#print "parsed: '$name' '$class' '$ttl' '$type'->'$itype' '$rdata'\n";
151#print;
152#;imap IN 900 CNAME deepnet.cx.
153##fixme: not sure how to handle the case where someone leaves off the class.
154 if ($doimport) {
155 my ($code, $msg);
156 if ($rev eq 'n') {
157 ($code,$msg) = $dnsdb->addRec('n', $rev, $zid, \$name, \$itype, \$rdata, $ttl);
158 } else {
159 ($code,$msg) = $dnsdb->addRec('n', $rev, $zid, \$rdata, \$itype, \$name, $ttl);
160 }
161 print "$code: $msg\n";
162 }
163}
164
165
166#print Dumper \%amap;
167foreach my $n (keys %amap) {
168 foreach my $ip (@{$amap{$n}}) {
169#print "$ip\t$n\n";
170 push @{$namemap{$ip}}, $n unless grep $n, @{$namemap{$ip}};
171 }
172}
173
174#print Dumper \%namemap;
175foreach my $ip (sort keys %namemap) {
176 print "$ip\t".join(' ', @{$namemap{$ip}})."\n";
177}
Note: See TracBrowser for help on using the repository browser.