File Coverage

blib/lib/AnyEvent/DNS/Nameserver.pm
Criterion Covered Total %
statement 9 98 9.1
branch 0 60 0.0
condition 0 15 0.0
subroutine 3 7 42.8
pod 0 3 0.0
total 12 183 6.5


line stmt bran cond sub pod time code
1             package AnyEvent::DNS::Nameserver;
2             our $VERSION = "1.1";
3 1     1   819 use Net::DNS;
  1         67945  
  1         113  
4 1     1   472 use AnyEvent::Handle::UDP;
  1         72185  
  1         60  
5 1     1   7 use Socket qw(sockaddr_in inet_ntoa);
  1         6  
  1         834  
6             sub new {
7 0     0 0   my $class = shift;
8 0           my %p = @_;
9 0           my $self = {};
10 0   0       $self->{LocalAddr} = $p{LocalAddr} || '0.0.0.0';
11 0   0       $self->{LocalPort} = $p{LocalPort} || 53;
12 0 0         $self->{ReplyHandler} = $p{ReplyHandler} or die "ReplyHandler invalid\n";
13 0   0       $self->{Verbose} = $p{Verbose} || 0;
14 0   0       $self->{Truncate} = $p{Truncate} || 1;
15 0   0       $self->{IdleTimeout} = $p{IdleTimeout} || 120;
16 0           $self->{NotifyHandler} = $p{NotifyHandler};
17              
18 0           $self->{watchers} = [];
19              
20 0 0         my @LocalAddr =ref $self->{LocalAddr} eq 'ARRAY'?@{$self->{LocalAddr}}:($self->{LocalAddr});
  0            
21 0           for my $la (@LocalAddr){
22 0           my $hdl;$hdl = AnyEvent::Handle::UDP->new(
23             bind => [$la,$self->{LocalPort}],
24             on_recv => sub {
25 0     0     my ($data, $ae_handle, $client_addr) = @_;
26 0           my ($peerport, $peerhost) = sockaddr_in($client_addr);
27 0           $peerhost = inet_ntoa($peerhost);
28 0           my $query = new Net::DNS::Packet( \$data );
29 0 0         if ( my $err = $@ ) {
30 0 0         print "Error decoding query packet: $err\n" if $self->{Verbose};
31 0           undef $query;
32             }
33 0           my $conn = {
34             sockhost=>$la,
35             sockport=>$self->{LocalPort},
36             peerhost=>$peerhost,
37             peerport=>$peerport,
38             };
39 0 0         print "UDP connection from $peerhost:$peerport to $conn->{sockhost}:$conn->{sockport}\n" if $self->{Verbose};
40 0   0       my $reply = make_reply($self,$query,$peerhost,$conn) || return;
41 0 0 0       my $max_len = ( $query && $self->{Truncate} ) ? $query->edns->size : undef;
42 0 0         if ( $self->{Verbose} ) {
43 0           local $| = 1;
44 0 0         print "Maximum UDP size advertised by $peerhost:$peerport: $max_len bytes\n" if $max_len;
45 0           print "Sending response to $peerhost:$peerport\n";
46 0           $reply->print ;
47             }
48 0           $ae_handle->push_send($reply->data($max_len), $client_addr);
49             },
50 0           );
51 0           push @{$self->{watchers}},$hdl;
  0            
52             }
53 0           return bless $self,$class;
54             }
55              
56             #copy from Net::DNS::Nameserver
57             sub make_reply {
58 0     0 0   my ( $self, $query, $peerhost, $conn ) = @_;
59            
60 0 0         unless ($query) {
61 0 0         print "ERROR: invalid packet\n" if $self->{Verbose};
62 0           my $empty = new Net::DNS::Packet(); # create empty reply packet
63 0           my $reply = $empty->reply();
64 0           $reply->header->rcode("FORMERR");
65 0           return $reply;
66             }
67            
68 0 0         if ( $query->header->qr() ) {
69 0 0         print "ERROR: invalid packet (qr set), dropping\n" if $self->{Verbose};
70 0           return;
71             }
72            
73 0           my $reply = $query->reply();
74 0           my $header = $reply->header;
75 0           my $headermask;
76            
77 0           my $opcode = $query->header->opcode;
78 0           my $qdcount = $query->header->qdcount;
79            
80 0 0         unless ($qdcount) {
    0          
81 0           $header->rcode("NOERROR");
82            
83             } elsif ( $qdcount > 1 ) {
84 0 0         print "ERROR: qdcount $qdcount unsupported\n" if $self->{Verbose};
85 0           $header->rcode("FORMERR");
86            
87             } else {
88 0           my ($qr) = $query->question;
89 0           my $qname = $qr->qname;
90 0           my $qtype = $qr->qtype;
91 0           my $qclass = $qr->qclass;
92            
93 0           my $id = $query->header->id;
94 0 0         $query->print if $self->{Verbose};
95            
96 0           my ( $rcode, $ans, $auth, $add );
97 0           my @arglist = ( $qname, $qclass, $qtype, $peerhost, $query, $conn );
98            
99 0 0         if ( $opcode eq "QUERY" ) {
    0          
100 0           ( $rcode, $ans, $auth, $add, $headermask ) =
101 0           &{$self->{ReplyHandler}}(@arglist);
102            
103             } elsif ( $opcode eq "NOTIFY" ) { #RFC1996
104 0 0         if ( ref $self->{NotifyHandler} eq "CODE" ) {
105 0           ( $rcode, $ans, $auth, $add, $headermask ) =
106 0           &{$self->{NotifyHandler}}(@arglist);
107             } else {
108 0           $rcode = "NOTIMP";
109             }
110            
111             } else {
112 0 0         print "ERROR: opcode $opcode unsupported\n" if $self->{Verbose};
113 0           $rcode = "FORMERR";
114             }
115            
116 0 0         if ( !defined($rcode) ) {
117 0 0         print "remaining silent\n" if $self->{Verbose};
118 0           return undef;
119             }
120            
121 0           $header->rcode($rcode);
122            
123 0 0         $reply->{answer} = [@$ans] if $ans;
124 0 0         $reply->{authority} = [@$auth] if $auth;
125 0 0         $reply->{additional} = [@$add] if $add;
126             }
127            
128 0 0         if ( !defined($headermask) ) {
129 0           $header->ra(1);
130 0           $header->ad(0);
131             } else {
132 0 0         $header->opcode( $headermask->{opcode} ) if $headermask->{opcode};
133            
134 0 0         $header->aa(1) if $headermask->{aa};
135 0 0         $header->ra(1) if $headermask->{ra};
136 0 0         $header->ad(1) if $headermask->{ad};
137             }
138 0           return $reply;
139             }
140              
141             sub main_loop{
142 0     0 0   my $self = shift;
143 0           AE::cv->recv;
144             }
145             1;
146             =pod
147              
148             =head1 NAME
149              
150             AnyEvent::DNS::Nameserver - DNS server class using AnyEvent
151              
152             =head1 SYNOPSIS
153              
154             use AnyEvent::DNS::Nameserver;
155             my $nameserver = new Net::DNS::Nameserver(
156             LocalAddr => ['192.168.1.1' , '127.0.0.1' ],
157             LocalPort => "53",
158             ReplyHandler => \&reply_handler,
159             Verbose => 1,
160             Truncate => 0
161             );
162             $nameserver->main_loop;
163              
164             =head1 DESCRIPTION
165              
166             Net::DNS::Nameserver doesn't work with AnyEvent so I wrote this module in honor of Net::DNS::Nameserver
167              
168             AnyEvent::DNS::Nameserver try to be compatible with all the methods and features of Net::DNS::Nameserver
169              
170             You can find more information and usage from Net::DNS::Nameserver
171              
172             AnyEvent::DNS::Nameserver only support udp query and answer by now
173              
174             =head1 SEE ALSO
175              
176             L
177              
178             L
179              
180             =head1 AUTHOR
181              
182             Perfi, Esjdy521@163.comE
183              
184             =head1 COPYRIGHT AND LICENSE
185              
186             Copyright (C) 2014 by Perfi
187              
188             This library is free software; you can redistribute it and/or modify
189             it under the same terms as Perl itself, either Perl version 5.8.8 or,
190             at your option, any later version of Perl 5 you may have available.
191              
192              
193             =cut