File Coverage

blib/lib/Net/DNS/Multicast.pm
Criterion Covered Total %
statement 102 102 100.0
branch 14 16 100.0
condition 4 4 100.0
subroutine 18 18 100.0
pod 4 6 100.0
total 142 146 100.0


line stmt bran cond sub pod time code
1             package Net::DNS::Multicast;
2              
3 4     4   732460 use strict;
  4         9  
  4         156  
4 4     4   19 use warnings;
  4         8  
  4         356  
5              
6             our $VERSION;
7             $VERSION = '1.00';
8              
9 4     4   2189 use Net::DNS qw(:DEFAULT);
  4         551309  
  4         1316  
10 4     4   47 use base qw(Exporter Net::DNS);
  4         9  
  4         844  
11              
12             our @EXPORT = @Net::DNS::EXPORT;
13              
14 4     4   36 use IO::Select;
  4         11  
  4         268  
15 4     4   28 use IO::Socket;
  4         8  
  4         46  
16 4         481 use Socket qw( pack_ipv6_mreq inet_pton
17 4     4   2828 IPPROTO_IPV6 IPV6_JOIN_GROUP IPV6_MULTICAST_LOOP);
  4         9  
18              
19             =head1 NAME
20              
21             Net::DNS::Multicast - Multicast extension to Net::DNS
22              
23             =head1 SYNOPSIS
24              
25             use Net::DNS::Multicast;
26             my $resolver = Net::DNS::Resolver->new();
27             my $response = $resolver->send( 'host.local.', 'AAAA' );
28              
29             my $handle = $resolver->bgsend( '_ipp._tcp.local.', 'PTR' );
30             while ( my $response = $resolver->bgread($handle) ) {
31             $response->print;
32             }
33              
34             =head1 DESCRIPTION
35              
36             Net::DNS::Multicast is installed as an extension to an existing Net::DNS
37             installation providing packages to support simple IP multicast queries
38             as described in RFC6762(5.1).
39              
40             The multicast feature is activated by substituting Net::DNS::Multicast
41             for Net::DNS in the use declaration.
42              
43             The use of IP Multicast is confined to the link-local domain names
44             listed in RFC6762. Queries for other names in the global DNS are
45             directed to the configured nameservers.
46              
47             =cut
48              
49              
50             ## no critic
51 4         7 use constant SOCKOPT => eval { ## precompile multicast socket options
52 4     4   46 use constant ADDRESS => 'FF02::FB';
  4         25  
  4         470  
53 4     4   27 use constant IP6MREQ => pack_ipv6_mreq( inet_pton( AF_INET6, ADDRESS ), 0 );
  4         6  
  4         369  
54              
55 4     4   28 use constant T => pack( 'i', 1 );
  4         19  
  4         360  
56 4     4   26 use constant F => pack( 'i', 0 );
  4         18  
  4         829  
57 4         8 my @sockopt; # check option names are acceptable
58 4         455 push @sockopt, eval '[SOL_SOCKET, SO_REUSEADDR, T]';
59 4         242 push @sockopt, eval '[SOL_SOCKET, SO_REUSEPORT, T]';
60 4         222 push @sockopt, eval '[IPPROTO_IPV6, IPV6_MULTICAST_LOOP, F]';
61 4         243 push @sockopt, eval '[IPPROTO_IPV6, IPV6_JOIN_GROUP, IP6MREQ]';
62              
63 4         44 my $resolver = Net::DNS::Resolver->new();
64             my $tolerate = sub { # check options are safe to use
65 16         31 return defined eval { $resolver->_create_udp_socket( ADDRESS, Sockopts => [shift] ) }
  16         92  
66 4         3141 };
67 4         11 return grep { &$tolerate($_) } @sockopt; # without any guarantee that they work!
  16         11366  
68 4     4   29 };
  4         8  
69              
70              
71             ## Insert methods into (otherwise empty) Net::DNS::Resolver package
72              
73             my @multicast_group = qw(FF02::FB 224.0.0.251);
74             my $multicast_port = 5353;
75             my $multicast_timeout = 5;
76              
77             my $NAME_REGEX = q/\.(local|[89AB]\.E\.F\.ip6\.arpa|254\.169\.in-addr\.arpa)$/;
78              
79              
80             sub Net::DNS::Resolver::send {
81 2     2 1 179136 my ( $self, @argument ) = @_;
82 2         15 my $packet = $self->_make_query_packet(@argument);
83 2         18235 my ($q) = $packet->question;
84              
85 2 100       28 return Net::DNS::Resolver::Base::send( $self, $packet ) unless $q->qname =~ /$NAME_REGEX/oi;
86              
87 1         120 my $handle = $self->bgsend($packet);
88 1         7 return $self->bgread($handle);
89             }
90              
91              
92             sub Net::DNS::Resolver::bgsend {
93 4     4 1 287266 my ( $self, @argument ) = @_;
94 4         27 my $packet = $self->_make_query_packet(@argument);
95 4         22050 my ($query) = $packet->question;
96              
97 4 100       45 return IO::Select->new( Net::DNS::Resolver::Base::bgsend( $self, $packet ) )
98             unless $query->qname =~ /$NAME_REGEX/oi;
99              
100 3         279 my $select = IO::Select->new();
101 3         41 my $expire = time() + $multicast_timeout;
102 3         26 $self->_reset_errorstring;
103 3         21 local $packet->{status} = 0;
104 3         16 my $qm = $packet->data;
105 3         684 local $query->{qclass};
106 3         14 $query->unicast_response(1);
107 3         12 my $qu = $packet->data;
108              
109 3         457 local @{$self}{qw(nameservers nameserver4 nameserver6 port)};
  3         18  
110 3         34 my $port = $self->port($multicast_port);
111 3         76 foreach my $ip ( $self->nameservers(@multicast_group) ) {
112 4         419 my $socket = $self->_create_udp_socket($ip);
113 4 50       2269 next unless $socket; # uncoverable branch true
114              
115 4         38 $self->_diag( 'bgsend', "[$ip]:$port" );
116 4         27 my $destaddr = $self->_create_dst_sockaddr( $ip, $port );
117 4         167 ${*$socket}{net_dns_bg} = [$expire, $packet];
  4         24  
118              
119 4 100       32 if ( $socket->sockdomain() == AF_INET6 ) {
120 2         40 $socket->setsockopt( IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 0 );
121              
122 2         41 my $multicast = $self->_create_udp_socket(
123             $ip,
124             LocalPort => $port,
125             Sockopts => [SOCKOPT],
126             );
127 2 50       1252 if ($multicast) { # uncoverable branch false
128 2         6 ${*$multicast}{net_dns_bg} = [$expire];
  2         10  
129 2         12 $select->add($multicast);
130 2         141 $multicast->send( $qm, 0, $destaddr );
131             }
132             }
133              
134 4         148 $select->add($socket);
135 4         237 $socket->send( $qu, 0, $destaddr ); # unicast
136             }
137 3         1174 return $select;
138             }
139              
140              
141             sub Net::DNS::Resolver::bgbusy {
142 1     1 1 618 my ( $self, $select ) = @_;
143 1         7 my ($handle) = ( $select->can_read(0), $select->handles );
144 1         49 return Net::DNS::Resolver::Base::bgbusy( $self, $handle );
145             }
146              
147              
148             sub Net::DNS::Resolver::bgread {
149 3     3 1 2607 my ( $self, $select ) = @_;
150 3         7 my $response;
151 3         23 foreach my $handle ( $select->can_read(0), $select->handles ) {
152 5 100       5130813 last if $response = Net::DNS::Resolver::Base::bgread( $self, $handle );
153             }
154 3         229608 return $response;
155             }
156              
157              
158             ## Add access methods for m-DNS flags
159              
160             sub Net::DNS::Question::unicast_response {
161 6     6 0 262910 my ( $self, $value ) = @_; # uncoverable pod
162 6   100     58 my $class = $self->{qclass} || 1; # IN implicit
163 6 100       29 $self->{qclass} = $class |= 0x8000 if $value; # set only
164 6         27 return $class >> 15;
165             }
166              
167             sub Net::DNS::RR::cache_flush {
168 3     3 0 8063 my ( $self, $value ) = @_; # uncoverable pod
169 3   100     18 my $class = $self->{class} || 1; # IN implicit
170 3 100       10 $self->{class} = $class |= 0x8000 if $value; # set only
171 3         21 return $class >> 15;
172             }
173              
174              
175             1;
176             __END__