File Coverage

blib/lib/IO/Socket/Multicast6.pm
Criterion Covered Total %
statement 107 157 68.1
branch 54 122 44.2
condition 16 34 47.0
subroutine 18 20 90.0
pod 10 10 100.0
total 205 343 59.7


line stmt bran cond sub pod time code
1             package IO::Socket::Multicast6;
2              
3 7     7   237823 use strict;
  7         15  
  7         268  
4 7     7   38 use vars qw(@ISA $VERSION);
  7         13  
  7         404  
5              
6 7     7   7760 use IO::Socket::INET6;
  7         218320  
  7         58  
7 7     7   10284 use IO::Interface::Simple;
  7         65551  
  7         220  
8 7     7   5548 use Socket::Multicast6 qw/ :all /;
  7         7371  
  7         2012  
9 7     7   46 use Socket;
  7         15  
  7         7576  
10 7     7   38 use Socket6;
  7         14  
  7         3752  
11 7     7   39 use Carp 'croak';
  7         13  
  7         25198  
12              
13              
14              
15             @ISA = qw(IO::Socket::INET6);
16             $VERSION = '0.03';
17              
18              
19             # Regular expressions to match IP addresses
20             my $IPv4 = '\d+\.\d+\.\d+\.\d+';
21             my $IPv6 = '[\da-fA-F:]+';
22              
23              
24             sub new {
25 11     11 1 3045 my $class = shift;
26 11 100       43 unshift @_,(Proto => 'udp') unless @_;
27 11         106 $class->SUPER::new(@_);
28             }
29              
30              
31             sub configure {
32 11     11 1 1153 my($self,$arg) = @_;
33 11   100     71 $arg->{Proto} ||= 'udp';
34 11   100     65 $arg->{ReuseAddr} ||= 1;
35 11         67 $self->SUPER::configure($arg);
36             }
37              
38              
39             sub mcast_add {
40 2     2 1 3263 my $sock = shift;
41 2   33     11 my $group = shift || croak 'usage: $sock->mcast_add($mcast_addr [,$interface])';
42 2         4 my $interface = shift;
43            
44 2 100       14 if ($sock->sockdomain() == AF_INET) {
    50          
45 1         26 my $if_addr = _get_if_ipv4addr($interface);
46 1         17 my $ip_mreq = pack_ip_mreq( inet_pton( AF_INET, $group ),
47             inet_pton( AF_INET, $if_addr ) );
48            
49 1 50       9 setsockopt($sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, $ip_mreq )
50             or croak "Could not set IP_ADD_MEMBERSHIP socket option: $!";
51             } elsif ($sock->sockdomain() == AF_INET6) {
52 1         39 my $if_index = _get_if_index($interface);
53 1         11 my $ipv6_mreq = pack_ipv6_mreq( inet_pton( AF_INET6, $group ),
54             $if_index );
55            
56 1 50       9 setsockopt($sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, $ipv6_mreq )
57             or croak "Could not set IPV6_JOIN_GROUP socket option: $!";
58             } else {
59 0         0 croak("mcast_add failed, unsupported socket family." );
60             }
61            
62             # Success
63 2         392 return 1;
64             }
65              
66             sub mcast_add_source {
67 0     0 1 0 my $sock = shift;
68 0   0     0 my $group = shift || croak 'usage: $sock->mcast_add_source($mcast_addr, $source_addr [,$interface])';
69 0   0     0 my $source = shift || croak 'usage: $sock->mcast_add_source($mcast_addr, $source_addr [,$interface])';
70 0         0 my $interface = shift;
71              
72 0 0       0 if ($sock->sockdomain() == AF_INET) {
    0          
73 0         0 my $if_addr = _get_if_ipv4addr($interface);
74 0         0 my $ip_mreq = pack_ip_mreq_source(
75             inet_pton( AF_INET, $group ),
76             inet_pton( AF_INET, $source ),
77             inet_pton( AF_INET, $if_addr ) );
78            
79 0 0       0 setsockopt($sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, $ip_mreq )
80             or croak "Could not set IP_ADD_SOURCE_MEMBERSHIP socket option: $!";
81             } elsif ($sock->sockdomain() == AF_INET6) {
82 0         0 croak("mcast_add_source failed, IPv6 is currently unsupported." );
83             } else {
84 0         0 croak("mcast_add_source failed, unsupported socket family." );
85             }
86            
87             # Success
88 0         0 return 1;
89             }
90              
91              
92             sub mcast_drop {
93 2     2 1 4 my $sock = shift;
94 2   33     7 my $group = shift || croak 'usage: $sock->mcast_drop($mcast_addr [,$interface])';
95 2         4 my $interface = shift;
96            
97 2 100       10 if ($sock->sockdomain() == AF_INET) {
    50          
98 1         20 my $if_addr = _get_if_ipv4addr($interface);
99 1         8 my $ip_mreq = pack_ip_mreq( inet_pton( AF_INET, $group ),
100             inet_pton( AF_INET, $if_addr ) );
101            
102 1 50       32 setsockopt($sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, $ip_mreq )
103             or croak "Could not set IP_ADD_MEMBERSHIP socket option: $!";
104             } elsif ($sock->sockdomain() == AF_INET6) {
105 1         31 my $if_index = _get_if_index($interface);
106 1         7 my $ipv6_mreq = pack_ipv6_mreq( inet_pton( AF_INET6, $group ),
107             $if_index );
108            
109 1 50       32 setsockopt($sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, $ipv6_mreq )
110             or croak "Could not set IPV6_LEAVE_GROUP socket option: $!";
111             } else {
112 0         0 croak("mcast_add failed, unsupported socket family." );
113             }
114            
115             # Success
116 2         109 return 1;
117             }
118              
119              
120             sub mcast_ttl {
121 6     6 1 2336 my $sock = shift;
122            
123 6         15 my $prev = undef;
124 6 100       19 if ($sock->sockdomain() == AF_INET) {
    50          
125 3 50       87 my $packed = getsockopt($sock, IPPROTO_IP, IP_MULTICAST_TTL)
126             or croak "Could not get IP_MULTICAST_TTL socket option: $!";
127 3         139 $prev=unpack("I", $packed);
128 3 100       8 if (my $ttl = shift) {
129 1 50       21 setsockopt($sock, IPPROTO_IP, IP_MULTICAST_TTL, pack("I", $ttl ) )
130             or croak "Could not set IP_MULTICAST_TTL socket option: $!";
131             }
132             } elsif ($sock->sockdomain() == AF_INET6) {
133 3 50       109 my $packed = getsockopt($sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS)
134             or croak "Could not get IPV6_MULTICAST_HOPS socket option: $!";
135 3         121 $prev=unpack("I", $packed);
136 3 100       7 if (my $ttl = shift) {
137 1 50       20 setsockopt($sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, pack("I", $ttl ) )
138             or croak "Could not set IPV6_MULTICAST_HOPS socket option: $!";
139             }
140             } else {
141 0         0 croak("mcast_ttl failed, unsupported socket family." );
142             }
143              
144 6         45 return $prev;
145             }
146              
147              
148             sub mcast_loopback {
149 6     6 1 3792 my $sock = shift;
150            
151 6         8 my $prev = undef;
152 6 100       24 if ($sock->sockdomain() == AF_INET) {
    50          
153 3 50       109 my $packed = getsockopt($sock, IPPROTO_IP, IP_MULTICAST_LOOP)
154             or croak "Could not get IP_MULTICAST_LOOP socket option: $!";
155 3         171 $prev=unpack("I", $packed);
156 3 100       8 if (my $loopback = shift) {
157 1 50       25 setsockopt($sock, IPPROTO_IP, IP_MULTICAST_LOOP, pack("I", $loopback ) )
158             or croak "Could not set IP_MULTICAST_LOOP socket option: $!";
159             }
160             } elsif ($sock->sockdomain() == AF_INET6) {
161 3 50       291 my $packed = getsockopt($sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP)
162             or croak "Could not get IPV6_MULTICAST_LOOP socket option: $!";
163 3         349 $prev=unpack("I", $packed);
164              
165 3 100       9 if (my $loopback = shift) {
166 1 50       25 setsockopt($sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, pack("I", $loopback ) )
167             or croak "Could not set IPV6_MULTICAST_LOOP socket option: $!";
168             }
169             } else {
170 0         0 croak("mcast_loopback failed, unsupported socket family." );
171             }
172              
173 6         50 return $prev;
174             }
175              
176              
177             sub mcast_if {
178 0     0 1 0 my $sock = shift;
179            
180 0         0 my $prev = undef;
181 0 0       0 if ($sock->sockdomain() == AF_INET) {
    0          
182 0 0       0 my $packed = getsockopt($sock, IPPROTO_IP, IP_MULTICAST_IF)
183             or croak "Could not get IP_MULTICAST_IF socket option: $!";
184 0         0 $prev=$sock->addr_to_interface( inet_ntop( AF_INET, $packed ) );
185              
186 0 0       0 if (my $interface = shift) {
187 0         0 my $if_addr = _get_if_ipv4addr($interface);
188 0 0       0 setsockopt($sock, IPPROTO_IP, IP_MULTICAST_IF, inet_pton( AF_INET, $if_addr ) )
189             or croak "Could not set IP_MULTICAST_IF socket option: $!";
190             }
191             } elsif ($sock->sockdomain() == AF_INET6) {
192 0 0       0 my $packed = getsockopt($sock, IPPROTO_IPV6, IPV6_MULTICAST_IF)
193             or croak "Could not get IPV6_MULTICAST_IF socket option: $!";
194 0         0 $prev = unpack("I", $packed);
195 0 0       0 if ($prev==0) { $prev='any'; }
  0         0  
196 0         0 else { $prev = $sock->if_indextoname($prev); }
197              
198 0 0       0 if (my $interface = shift) {
199 0         0 my $if_index = _get_if_index($interface);
200 0 0       0 setsockopt($sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, pack("I", $if_index ) )
201             or croak "Could not set IPV6_MULTICAST_IF socket option: $!";
202             }
203             } else {
204 0         0 croak("mcast_if failed, unsupported socket family." );
205             }
206              
207 0         0 return $prev;
208             }
209              
210              
211             sub mcast_dest {
212 14     14 1 2924 my $sock = shift;
213 14         19 my ($addr, $port) = @_;
214            
215 14         16 my $prev = ${*$sock}{'io_socket_mcast_dest'};
  14         39  
216 14 100       38 if (defined $addr) {
217 6 100       27 if ($sock->sockdomain() == AF_INET) {
    50          
218 3 100 100     115 if (!defined $port and $addr =~ /^($IPv4):(\d+)$/) {
219 1         3 $addr = $1; $port = $2;
  1         4  
220             }
221            
222 3 100       25 $addr = pack_sockaddr_in($port,inet_pton(AF_INET, $addr)) if (defined $port);
223 3 50 33     29 croak "Invalid destination address" if (!defined $addr or length($addr)==0);
224 3 50       14 croak "Destination isn't an IPv4 address" unless (sockaddr_family($addr)==AF_INET);
225            
226             } elsif ($sock->sockdomain() == AF_INET6) {
227 3 100 100     269 if (!defined $port and $addr =~ /^\[($IPv6)\]:(\d+)$/) {
228 1         3 $addr = $1; $port = $2;
  1         2  
229             }
230            
231 3 100       55 $addr = pack_sockaddr_in6($port,inet_pton(AF_INET6, $addr)) if (defined $port);
232 3 50 33     36 croak "Invalid destination address" if (!defined $addr or length($addr)==0);
233 3 50       80 croak "Destination isn't an IPv6 address" unless (sockaddr_family($addr)==AF_INET6);
234            
235             } else {
236 0         0 croak("mcast_dest failed, unsupported socket family." );
237             }
238            
239 6         23 ${*$sock}{'io_socket_mcast_dest'} = $addr;
  6         16  
240             }
241              
242 14         47 return $prev;
243             }
244              
245              
246              
247             sub mcast_send {
248 2     2 1 4 my $sock = shift;
249 2   33     8 my $data = shift || croak 'usage: $sock->mcast_send($data [,$address[,$port]])';
250 2 50       31 $sock->mcast_dest(@_) if @_;
251 2   33     7 my $dest = $sock->mcast_dest || croak "no destination specified with mcast_send() or mcast_dest()";
252            
253 2         346 return send($sock,$data,0,$dest);
254             }
255              
256              
257             ## Returns the IPv4 address of an interface
258             #
259             sub _get_if_ipv4addr {
260 2     2   5 my ($interface) = @_;
261            
262 2 50       19 return '0.0.0.0' unless (defined $interface);
263 0 0       0 return '0.0.0.0' if ($interface eq 'any');
264 0 0       0 return $interface if ($interface =~ /^$IPv4$/);
265            
266 0         0 my $if = new IO::Interface::Simple( $interface );
267 0 0       0 croak "Unknown interface $interface" unless (defined $if);
268 0 0       0 croak "Interface '$interface' is not multicast capable" unless ($if->is_multicast());
269 0         0 my $address = $if->address();
270 0 0       0 croak "Interface '$interface' does not have an IPv4 address" unless (defined $address);
271 0         0 return $address;
272             }
273              
274              
275             ## Returns the index of an interface
276             #
277             sub _get_if_index {
278 2     2   4 my ($interface) = @_;
279            
280 2 50       39 return 0 unless defined $interface;
281 0 0         return $interface if ($interface =~ /^\d+$/);
282 0 0         return 0 if ($interface =~ /^any$/i);
283              
284 0           my $if = new IO::Interface::Simple( $interface );
285 0 0         croak "Unknown interface $interface" unless (defined $if);
286 0 0         croak "Interface '$interface' is not multicast capable" unless ($if->is_multicast());
287 0           my $index = $if->index();
288 0 0         croak "Can't get index of interface '$interface'." unless (defined $index);
289 0           return $index;
290             }
291              
292              
293             1;
294             __END__