1#!/usr/bin/perl
2#
3# $Id: Connectivity.pm 885 2010-06-18 14:26:03Z calle $
4#
5# Copyright (c) 2007 .SE (The Internet Infrastructure Foundation).
6#                    All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
29######################################################################
30
31package DNSCheck::Test::Connectivity;
32
33require 5.008;
34use warnings;
35use strict;
36use Net::IP;
37
38our $SVN_VERSION = '$Revision: 885 $';
39
40use base 'DNSCheck::Test::Common';
41
42######################################################################
43
44sub test {
45    my $self = shift;
46    my $zone = shift;
47
48    my $parent = $self->parent;
49    my $qclass = $self->qclass;
50    my $logger = $self->logger;
51
52    return unless $parent->config->should_run;
53
54    $logger->module_stack_push();
55    $logger->auto("CONNECTIVITY:BEGIN", $zone);
56
57    my $errors = $self->test_v4($zone) + $self->test_v6($zone);
58
59    $logger->auto("CONNECTIVITY:END", $zone);
60    $logger->module_stack_pop();
61
62    return $errors;
63}
64
65sub test_v4 {
66    my $self = shift;
67    my $zone = shift;
68
69    my $parent = $self->parent;
70    my $qclass = $self->qclass;
71    my $logger = $self->logger;
72    my $errors = 0;
73
74    return 0 unless $parent->config->should_run;
75
76    my %as_set;
77    my @nameservers = ();
78
79    # Fetch IPv4 nameservers
80    my $ipv4 = $parent->dns->get_nameservers_ipv4($zone, $qclass);
81    push @nameservers, @{$ipv4} if ($ipv4);
82
83    foreach my $address (@nameservers) {
84        my $as_lookup = $parent->asn->lookup($address);
85        my @as_list   = ();
86        @as_list = @{$as_lookup} if $as_lookup;
87
88        foreach my $asn (@as_list) {
89            $as_set{$asn} = $asn;
90        }
91
92        $logger->auto("CONNECTIVITY:ANNOUNCED_BY_ASN",
93            $address, join(",", @as_list));
94
95        # REQUIRE: A name server must be announced
96        if (scalar @as_list < 1) {
97            $errors += $logger->auto("CONNECTIVITY:NOT_ANNOUNCED", $address);
98        }
99    }
100
101    $logger->auto("CONNECTIVITY:ASN_LIST", join(",", keys(%as_set)));
102
103    # REQUIRE: Domain name servers should live in more than one AS
104    my $as_count = scalar keys %as_set;
105    if ($as_count <= 1) {
106        $errors += $logger->auto("CONNECTIVITY:TOO_FEW_ASN", $as_count);
107    } else {
108        $logger->auto("CONNECTIVITY:ASN_COUNT_OK", $as_count);
109    }
110
111  DONE:
112
113    return $errors;
114}
115
116sub test_v6 {
117    my $self = shift;
118    my $zone = shift;
119
120    my $parent = $self->parent;
121    my $qclass = $self->qclass;
122    my $logger = $self->logger;
123    my $errors = 0;
124
125    return 0 unless $parent->config->should_run;
126
127    my %as_set;
128    my @nameservers = ();
129
130    # Fetch IPv6 nameservers.
131    my $ipv6 = $parent->dns->get_nameservers_ipv6($zone, $qclass);
132    push @nameservers, @{$ipv6} if ($ipv6);
133
134    foreach my $address (map { Net::IP->new($_)->ip } @nameservers) {
135        my $as_lookup = $parent->asn->lookup($address);
136        my @as_list   = ();
137        @as_list = @{$as_lookup} if $as_lookup;
138
139        foreach my $asn (@as_list) {
140            $as_set{$asn} = $asn;
141        }
142
143        $logger->auto("CONNECTIVITY:V6_ANNOUNCED_BY_ASN",
144            $address, join(",", @as_list));
145
146        # REQUIRE: A name server must be announced
147        if (scalar @as_list < 1) {
148            $errors += $logger->auto("CONNECTIVITY:V6_NOT_ANNOUNCED", $address);
149        }
150    }
151
152    $logger->auto("CONNECTIVITY:V6_ASN_LIST", join(",", keys(%as_set)));
153
154    # REQUIRE: Domain name servers should live in more than one AS
155    my $as_count = scalar keys %as_set;
156    if ($as_count <= 1) {
157        $errors += $logger->auto("CONNECTIVITY:V6_TOO_FEW_ASN", $as_count);
158    } else {
159        $logger->auto("CONNECTIVITY:V6_ASN_COUNT_OK", $as_count);
160    }
161
162  DONE:
163    return $errors;
164}
165
1661;
167
168__END__
169
170
171=head1 NAME
172
173DNSCheck::Test::Connectivity - Test zone connectivity
174
175=head1 DESCRIPTION
176
177Test connectivity for a zone's nameservers. The following tests are made:
178
179=over 4
180
181=item *
182A name server should not be announced by more than one AS.
183
184=item *
185A name server must be announced.
186
187=item *
188Domain name servers should live in more than one AS.
189
190=back
191
192=head1 METHODS
193
194=over
195
196=item ->test($zonename);
197
198=back
199
200=head1 EXAMPLES
201
202=head1 SEE ALSO
203
204L<DNSCheck>, L<DNSCheck::Logger>
205
206=cut
207