line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package GlbDNS::Zone; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
4
|
7
|
|
|
7
|
|
30061
|
use strict; |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
447
|
|
5
|
7
|
|
|
7
|
|
348
|
use warnings; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
199
|
|
6
|
7
|
|
|
7
|
|
40
|
use Data::Dumper; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
395
|
|
7
|
7
|
|
|
7
|
|
13203
|
use Net::DNS::RR::A; |
|
7
|
|
|
|
|
4616
|
|
|
7
|
|
|
|
|
11571
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 GlbDNS::Zone |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Parsing zone files with LOC data |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head2 load_configs |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
GlbDNS->load_configs($glbdns, $path); |
17
|
|
|
|
|
|
|
GlbDNS->load_configs($glbdns, $file); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=cut |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub load_configs { |
22
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
23
|
0
|
|
|
|
|
|
my $glbdns = shift; |
24
|
0
|
|
|
|
|
|
my $path = shift; |
25
|
0
|
0
|
|
|
|
|
if (-d $path) { |
|
|
0
|
|
|
|
|
|
26
|
0
|
0
|
|
|
|
|
opendir(DIR, $path) || die "Cannot open directory '$path': $!\n"; |
27
|
0
|
|
|
|
|
|
for my $file (readdir(DIR)) { |
28
|
0
|
0
|
|
|
|
|
next if (-d $file); |
29
|
0
|
0
|
|
|
|
|
next if ($file =~/^(\.|#)/); |
30
|
0
|
0
|
|
|
|
|
next if ($file =~/~$/); |
31
|
0
|
|
|
|
|
|
$class->parse($glbdns, "$path/$file"); |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
} elsif (-f $path) { |
34
|
0
|
|
|
|
|
|
$class->parse($glbdns, $path); |
35
|
|
|
|
|
|
|
} else { |
36
|
0
|
|
|
|
|
|
die "Cannot find zone '$path'\n"; |
37
|
|
|
|
|
|
|
} |
38
|
0
|
|
|
|
|
|
$class->geo_fix($glbdns); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head2 parse |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
GlbDNS->parse($glbdns, $file); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=cut |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub parse { |
48
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
49
|
0
|
|
|
|
|
|
my $glbdns = shift; |
50
|
0
|
|
|
|
|
|
my $file = shift; |
51
|
|
|
|
|
|
|
|
52
|
0
|
0
|
|
|
|
|
open(my $fh, "<", "$file") || die "Cannot open file '$file': $!\n"; |
53
|
0
|
|
|
|
|
|
my $mtime = @{[stat("$file")]}[9]; |
|
0
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my $error = sub { |
56
|
0
|
|
|
0
|
|
|
die "$_[0] at $file:$.\n"; |
57
|
0
|
|
|
|
|
|
}; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
|
my $base_fqdn; |
62
|
|
|
|
|
|
|
my $base; |
63
|
0
|
|
0
|
|
|
|
my $hosts = $glbdns->{hosts} ||= {}; |
64
|
0
|
|
|
|
|
|
while(my $line = <$fh>) { |
65
|
0
|
|
|
|
|
|
chomp($line); |
66
|
0
|
0
|
|
|
|
|
next unless($line); |
67
|
0
|
0
|
|
|
|
|
next if($line =~ /^\s+$/); |
68
|
0
|
0
|
|
|
|
|
next if($line =~ /^;/); |
69
|
|
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
|
if($line =~/\$ORIGIN\s+([a-zA-Z.\-]+)/) { |
|
|
0
|
|
|
|
|
|
71
|
0
|
|
|
|
|
|
$base_fqdn = $1; |
72
|
0
|
|
|
|
|
|
($base) = $base_fqdn =~/(.*)\.$/; |
73
|
0
|
0
|
|
|
|
|
$error->("'$base_fqdn' needs to be terminated with a . to be a FQDN") unless ($base); |
74
|
0
|
|
|
|
|
|
next; |
75
|
|
|
|
|
|
|
} elsif (!$base) { |
76
|
0
|
|
|
|
|
|
$error->("No \$ORIGIN domain has been specified, don't know what domain we are working on"); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
my @record = split /\s+/, $line; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# if the first record is a DNS entry |
82
|
|
|
|
|
|
|
# then check if it is a FQDN and complete it |
83
|
|
|
|
|
|
|
# or use the default one |
84
|
0
|
0
|
|
|
|
|
if ($record[0] !~ /^\d+$/) { |
85
|
0
|
0
|
|
|
|
|
$record[0] = "$record[0].$base" if($record[0] !~ /\.$/); |
86
|
|
|
|
|
|
|
} else { |
87
|
0
|
|
|
|
|
|
unshift @record, $base; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# fully qualify CNAMEs |
91
|
0
|
0
|
0
|
|
|
|
if($record[3] eq 'CNAME' && $record[4] !~/\.$/) { |
92
|
0
|
|
|
|
|
|
$record[4] .= ".$base"; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
# fully qualify MX |
95
|
0
|
0
|
0
|
|
|
|
if($record[3] eq 'MX' && $record[5] !~/\.$/) { |
96
|
0
|
|
|
|
|
|
$record[5] .= ".$base"; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $add_host = sub { |
101
|
0
|
|
|
0
|
|
|
my $record = shift; |
102
|
0
|
|
0
|
|
|
|
my $host = $hosts->{$record->name} ||= {}; |
103
|
0
|
|
0
|
|
|
|
my $records = $host->{$record->type} ||= []; |
104
|
0
|
|
|
|
|
|
$host->{__RECORD__} = $record->name; |
105
|
0
|
|
|
|
|
|
$host->{domain} = $host->{__DOMAIN__} = $base; |
106
|
0
|
|
|
|
|
|
push @$records, $record; |
107
|
0
|
|
|
|
|
|
}; |
108
|
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
my $rr = Net::DNS::RR->new(join " ", @record); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# autocreate PTR records for A records |
112
|
|
|
|
|
|
|
# there can be more than one |
113
|
0
|
0
|
|
|
|
|
if ($rr->type eq 'A') { |
114
|
0
|
|
|
|
|
|
my $address = join(".", reverse( split(/\./, $rr->address)) ) ; |
115
|
0
|
|
|
|
|
|
my $reverse = Net::DNS::RR->new("$address.in-addr.arpa. " . $rr->ttl . " IN PTR " . $rr->name); |
116
|
0
|
|
|
|
|
|
$add_host->($reverse); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
|
$add_host->($rr); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
} |
127
|
0
|
|
|
|
|
|
close($fh); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head2 geo_fix |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
GlbDNS::Zone->geo_fix($glbdns); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=cut |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub geo_fix { |
137
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
138
|
0
|
|
|
|
|
|
my $glbdns = shift; |
139
|
0
|
|
|
|
|
|
my $hosts = $glbdns->{hosts}; |
140
|
|
|
|
|
|
|
# now go through and fix up the geolocation ones |
141
|
0
|
|
|
|
|
|
foreach my $host (values %{$hosts}) { |
|
0
|
|
|
|
|
|
|
142
|
0
|
0
|
0
|
|
|
|
if ($host->{CNAME} && @{$host->{CNAME}} > 1) { |
|
0
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# more than one cname is not allowed |
144
|
|
|
|
|
|
|
# so they have to point to geo tagged records |
145
|
|
|
|
|
|
|
# or we abort |
146
|
0
|
|
|
|
|
|
foreach my $cname (@{$host->{CNAME}}) { |
|
0
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
|
my $target = $hosts->{$cname->cname}; |
148
|
0
|
0
|
|
|
|
|
die "Need record for " . $cname->cname . "\n" unless $target; |
149
|
0
|
0
|
|
|
|
|
die "Record " . $cname->name . " needs LOC data\n" unless $target->{LOC}; |
150
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
|
my ($lat, $lon) = $target->{LOC}[0]->latlon; |
152
|
0
|
|
0
|
|
|
|
my $geo = $host->{__GEO__} ||= {}; |
153
|
|
|
|
|
|
|
|
154
|
0
|
0
|
|
|
|
|
die "Trying to overwrite geo target $target->{__RECORD__}\n" if($geo->{$target->{__RECORD__}}); |
155
|
0
|
|
|
|
|
|
my $geo_entry = $geo->{$target->{__RECORD__}} = {}; |
156
|
|
|
|
|
|
|
|
157
|
0
|
|
|
|
|
|
$geo_entry->{lat} = $lat; |
158
|
0
|
|
|
|
|
|
$geo_entry->{lon} = $lon; |
159
|
0
|
|
0
|
|
|
|
$geo_entry->{hosts} = $target->{A} || $target->{CNAME} || die "Need A or CNAME for $target->{__RECORD__}\n"; |
160
|
0
|
0
|
|
|
|
|
if ($target->{TXT}) { |
161
|
0
|
|
|
|
|
|
foreach my $txt (@{$target->{TXT}}) { |
|
0
|
|
|
|
|
|
|
162
|
0
|
|
|
|
|
|
my @txt = $txt->char_str_list; |
163
|
0
|
0
|
|
|
|
|
if($txt[0] eq 'GlbDNS::RADIUS') { |
|
|
0
|
|
|
|
|
|
164
|
0
|
|
|
|
|
|
$geo_entry->{radius} = $txt[1]; |
165
|
|
|
|
|
|
|
} elsif($txt[0] eq 'GlbDNS::CHECK') { |
166
|
0
|
|
|
|
|
|
foreach my $check_host (@{$geo_entry->{hosts}}) { |
|
0
|
|
|
|
|
|
|
167
|
0
|
|
|
|
|
|
$glbdns->{checks}->{$check_host->address} = { |
168
|
|
|
|
|
|
|
ip => $check_host->address, |
169
|
|
|
|
|
|
|
url => $txt[1], |
170
|
|
|
|
|
|
|
expect => $txt[2], |
171
|
|
|
|
|
|
|
interval => 5, |
172
|
|
|
|
|
|
|
}; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
0
|
|
|
|
|
|
$geo_entry->{source}->{$host->{__RECORD__}} = $cname; |
178
|
|
|
|
|
|
|
} |
179
|
0
|
|
|
|
|
|
delete($host->{CNAME}); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
1; |