File Coverage

blib/lib/Test/Instance/DNS/Server.pm
Criterion Covered Total %
statement 17 76 22.3
branch 1 18 5.5
condition 0 2 0.0
subroutine 6 21 28.5
pod 0 5 0.0
total 24 122 19.6


line stmt bran cond sub pod time code
1             package Test::Instance::DNS::Server;
2              
3 1     1   487 use MooX::Options::Actions;
  1         1174  
  1         6  
4 1     1   94932 use Net::DNS::Nameserver;
  1         10583  
  1         32  
5 1     1   9 use Net::DNS::ZoneFile;
  1         17  
  1         64  
6 1     1   7 use Net::DNS qw/ rrsort /;
  1         2  
  1         52  
7 1     1   554 use IO::All;
  1         10455  
  1         8  
8              
9             option listen_addr => (
10             is => 'ro',
11             format => 's@',
12             doc => 'Addresses to listen on',
13             default => sub { ['::1', '127.0.0.1' ] },
14             );
15              
16             option listen_port => (
17             is => 'ro',
18             format => 'i',
19             doc => 'Listen Port',
20             required => 1,
21             );
22              
23             option verbose => (
24             is => 'ro',
25             default => 0,
26             doc => 'Turn on Verbose Debugging',
27             );
28              
29             option zone => (
30             is => 'ro',
31             format => 's',
32             required => 1,
33             doc => 'The zone file to use',
34             );
35              
36             option pid => (
37             is => 'ro',
38             format => 's',
39             default => 'dns-server.pid',
40             doc => 'Pidfile for the server',
41             );
42              
43             has ns => (
44             is => 'lazy',
45             builder => sub {
46 0     0   0 my $self = shift;
47             return Net::DNS::Nameserver->new(
48             LocalAddr => $self->listen_addr,
49             LocalPort => $self->listen_port,
50 0     0   0 ReplyHandler => sub { $self->reply_handler( @_ ) },
51 0   0     0 Verbose => $self->verbose,
52             ) || die "Couldn't create nameserver object\n";
53             },
54             );
55              
56             has _zone_file => (
57             is => 'lazy',
58             builder => sub {
59 0     0   0 my $self = shift;
60 0         0 return Net::DNS::ZoneFile->new( $self->zone );
61             },
62             );
63              
64             has _zone_data => (
65             is => 'lazy',
66             builder => sub {
67 0     0   0 my $self = shift;
68 0         0 return [ $self->_zone_file->read ];
69             },
70             );
71              
72             has _zone_lookup => (
73             is => 'lazy',
74             builder => sub {
75 0     0   0 my $self = shift;
76 0         0 my $data = {};
77 0         0 for my $zone ( @{ $self->_zone_data } ) {
  0         0  
78 0         0 my $ref = ref( $zone );
79 0         0 my ( $type ) = $ref =~ /^.*::(.*)$/;
80 0         0 push @{ $data->{$type} }, $zone;
  0         0  
81             }
82 0         0 return $data;
83             },
84             );
85              
86             has _is_running => (
87             is => 'rwp',
88             default => 1,
89             );
90              
91             has _pidfile => (
92             is => 'lazy',
93             builder => sub {
94 0     0   0 my $self = shift;
95 0         0 return io($self->pid);
96             },
97             );
98              
99             sub BUILD {
100 0     0 0 0 my $self = shift;
101 0     0   0 $SIG{'INT'} = sub { $self->sig_handler( @_ ) };
  0         0  
102 0     0   0 $SIG{'TERM'} = sub { $self->sig_handler( @_ ) };
  0         0  
103             }
104              
105             sub _create_pidfile {
106 0     0   0 my $self = shift;
107 0         0 $self->_pidfile->println($$)->autoflush;
108             }
109              
110             sub _cleanup_pidfile {
111 0     0   0 my $self = shift;
112 0         0 $self->_pidfile->unlink;
113             }
114              
115             sub cmd_run {
116 0     0 0 0 my $self = shift;
117 0 0       0 print "Creating Nameserver on port " . $self->listen_port . "\n" if $self->verbose;
118              
119 0         0 $self->_create_pidfile;
120             # same as calling main_loop on the Nameserver, but with a dropout
121 0         0 while ( $self->_is_running ) {
122 0         0 $self->ns->loop_once(10);
123             }
124 0         0 $self->_cleanup_pidfile;
125             }
126              
127             sub sig_handler {
128 0     0 0 0 my $self = shift;
129 0         0 $self->_set__is_running(0);
130 0 0       0 print "Stopping Nameserver on port " . $self->listen_port . "\n" if $self->verbose;
131             }
132              
133             sub lookup_records {
134 0     0 0 0 my $self = shift;
135 0         0 my ( $qtype, $qname ) = @_;
136 0         0 my @ans;
137 0         0 for my $rr ( @{ $self->_zone_lookup->{ $qtype } } ) {
  0         0  
138 0 0       0 push @ans, $rr if $rr->owner eq $qname;
139             }
140 0         0 return @ans;
141             }
142              
143             sub reply_handler {
144 0     0 0 0 my $self = shift;
145              
146 0         0 my ( $qname, $qclass, $qtype, $peerhost, $query, $conn ) = @_;
147 0         0 my ( $rcode, @ans, @auth, @add );
148              
149 0 0       0 print "Received query from $peerhost to " . $conn->{sockhost} . "\n" if $self->verbose;
150 0 0       0 $query->print if $self->verbose;
151              
152 0         0 $rcode = "NOERROR";
153             # use rrsort???
154 0 0       0 if ( grep { $_ eq $qtype } qw/ A AAAA CNAME TXT SRV / ) {
  0         0  
155 0         0 push @ans, $self->lookup_records( $qtype, $qname );
156 0 0       0 $rcode = "NXDOMAIN" unless scalar(@ans);
157             } else {
158 0 0       0 if ( exists $self->_zone_lookup->{ $qtype } ) {
159             }
160 0         0 $rcode = "NXDOMAIN";
161             }
162              
163             # mark the answer as authoritative (by setting the 'aa' flag)
164 0         0 my $headermask = {aa => 1};
165              
166             # specify EDNS options { option => value }
167 0         0 my $optionmask = {};
168              
169 0         0 return ( $rcode, \@ans, \@auth, \@add, $headermask, $optionmask );
170             }
171              
172             sub _run_if_script {
173 1 50   1   5 unless ( caller(1) ) {
174 0         0 Test::Instance::DNS::Server->new_with_actions;
175             }
176 1         34 return 1;
177             }
178              
179             Test::Instance::DNS::Server->_run_if_script;