File Coverage

blib/lib/AnyEvent/DNS/Nameserver.pm
Criterion Covered Total %
statement 12 102 11.7
branch 0 62 0.0
condition 0 15 0.0
subroutine 4 8 50.0
pod 0 3 0.0
total 16 190 8.4


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