File Coverage

lib/App/AllKnowingDNS/Handler.pm
Criterion Covered Total %
statement 63 75 84.0
branch 9 16 56.2
condition 4 9 44.4
subroutine 12 12 100.0
pod 1 3 33.3
total 89 115 77.3


line stmt bran cond sub pod time code
1             # vim:ts=4:sw=4:expandtab
2             package App::AllKnowingDNS::Handler;
3              
4 2     2   6450 use strict;
  2         6  
  2         97  
5 2     2   15 use warnings;
  2         52  
  2         115  
6 2     2   13 use base 'Exporter';
  2         4  
  2         312  
7 2     2   2578 use Net::DNS;
  2         186051  
  2         281  
8 2     2   22 use NetAddr::IP::Util qw(ipv6_aton);
  2         4  
  2         24  
9 2     2   274 use App::AllKnowingDNS::Config;
  2         7  
  2         47  
10 2     2   13 use App::AllKnowingDNS::Zone;
  2         4  
  2         48  
11 2     2   1906 use POSIX qw(strftime);
  2         14368  
  2         15  
12 2     2   2448 use v5.10;
  2         7  
  2         1863  
13              
14             =head1 NAME
15              
16             App::AllKnowingDNS::Handler - main code of AllKnowingDNS
17              
18             =head1 DESCRIPTION
19              
20             Note: User documentation is in L(1).
21              
22             This module contains the C handler function.
23              
24             =head1 FUNCTIONS
25              
26             =cut
27              
28             our @EXPORT = qw(reply_handler);
29              
30             sub handle_ptr_query {
31 2     2 0 7 my ($querylog, $zone, $qname, $qclass, $qtype) = @_;
32              
33             # Forward this query to our upstream DNS first, if any.
34 2 50 33     16 if (defined($zone->upstream_dns) &&
35             $zone->upstream_dns ne '') {
36 0         0 my $resolver = Net::DNS::Resolver->new(
37             nameservers => [ $zone->upstream_dns ],
38             recurse => 0,
39             );
40 0         0 my $result = $resolver->query($qname . '.upstream', 'PTR');
41              
42             # If the upstream query was successful, relay the response, otherwise
43             # generate a reply.
44 0 0 0     0 if (defined($result) && $result->header->rcode eq 'NOERROR') {
45 0 0       0 if ($querylog) {
46 0         0 say strftime('%x %X %z', localtime) . " - Relaying upstream answer for $qname";
47             }
48 0         0 my @answer = $result->answer;
49 0         0 for my $answer (@answer) {
50 0         0 my $name = $answer->name;
51 0         0 $name =~ s/\.upstream$//;
52 0         0 $answer->name($name);
53             }
54 0         0 return ('NOERROR', [ $result->answer ], [], [], { aa => 1 });
55             }
56             }
57              
58 2         3 my $ttl = 3600;
59 2         5 my $fullname = $qname;
60 2         9 substr($fullname, -1 * length($zone->ptrzone)) = '';
61 2         13 my $hostpart = join '', reverse split /\./, $fullname;
62 2         10 my $rdata = $zone->resolves_to;
63 2         10 $rdata =~ s/%DIGITS%/$hostpart/i;
64 2         29 my $rr = Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata");
65 2         3990 return ('NOERROR', [ $rr ], [], [], { aa => 1 });
66             }
67              
68             sub handle_aaaa_query {
69 4     4 0 8 my ($zone, $qname, $qclass, $qtype) = @_;
70              
71 4         11 my $ttl = 3600;
72 4         7 my $block = '([a-z0-9]{4})';
73 4         18 my $regexp = quotemeta($zone->resolves_to);
74 4         32 my ($address, $mask) = ($zone->network =~ m,^([^/]+)/([0-9]+),);
75 4         141 my @components = unpack("n8", ipv6_aton($address));
76              
77 4         256 my $numdigits = (128 - $mask) / 4;
78 4         58 $regexp =~ s/\\%DIGITS\\%/([a-z0-9]{$numdigits})/i;
79 4         84 my ($digits) = ($qname =~ /$regexp/);
80 4 50       16 return ('NXDOMAIN', undef, undef, undef) unless defined($digits);
81              
82 4 100       12 if ($qtype ne 'AAAA') {
83 1         9 return ('NOERROR', [ ], [], [], { aa => 1 });
84             }
85              
86             # Pad with zeros so that we can match 4 digits each.
87 3         13 $digits = "0$digits" while (length($digits) % 4) != 0;
88              
89             # Collect blocks with 4 digits each
90 3         8 my $numblocks = length($digits) / 4;
91 3         13 for (my $c = 0; $c < $numblocks; $c++) {
92 9         32 $components[8 - $numblocks + $c] |= hex(substr($digits, $c * 4, 4));
93             }
94              
95 3         18 my $rdata = sprintf("%04x:" x 7 . "%04x", @components);
96 3         35 my $rr = Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata");
97 3         4346 return ('NOERROR', [ $rr ], [], [], { aa => 1 });
98             }
99              
100             =head2 reply_handler($config, $qname, $qclass, $qtype, $peerhost)
101              
102             Handler to be used for Net::DNS::Nameserver.
103              
104             Returns DNS RRs for PTR and AAAA queries of zones which are configured in
105             C<$config>.
106              
107             =cut
108              
109             sub reply_handler {
110 8     8 1 6861 my ($config, $querylog, $qname, $qclass, $qtype, $peerhost) = @_;
111              
112 8 50       30 if ($querylog) {
113 0         0 say strftime('%x %X %z', localtime) . " - $peerhost - query for $qname ($qtype)";
114             }
115              
116 8 100 100     41 if ($qtype eq 'PTR' &&
117             defined(my $zone = $config->zone_for_ptr($qname))) {
118 2         8 return handle_ptr_query($querylog, $zone, $qname, $qclass, $qtype);
119             }
120              
121 6 100       29 if (defined(my $zone = $config->zone_for_aaaa($qname))) {
122 4         31 return handle_aaaa_query($zone, $qname, $qclass, $qtype);
123             }
124              
125 2         10 return ('NXDOMAIN', undef, undef, undef);
126             }
127              
128             1
129              
130             __END__