File Coverage

blib/lib/IO/Socket/INET6.pm
Criterion Covered Total %
statement 167 196 85.2
branch 96 170 56.4
condition 45 78 57.6
subroutine 24 27 88.8
pod 16 16 100.0
total 348 487 71.4


line stmt bran cond sub pod time code
1             # IO::Socket::INET6.pm
2             #
3             # Copyright (c) 1997-8 Graham Barr . All rights reserved.
4             # This program is free software; you can redistribute it and/or
5             # modify it under the same terms as Perl itself.
6             #
7             # Modified by Rafael Martinez-Torres
8             # Euro6IX project (www.euro6ix.org) 2003.
9              
10             package IO::Socket::INET6;
11              
12 14     14   247783 use strict;
  14         29  
  14         656  
13 14     14   82 use warnings;
  14         29  
  14         448  
14              
15 14     14   404 use 5.008;
  14         62  
  14         907  
16              
17             our(@ISA, $VERSION);
18              
19             # Do it so we won't import any symbols from IO::Socket which it does export
20             # by default:
21             #
22             # IO::Socket is stupidstupidstupid beyond belief. Despite being an
23             # object class, it has an import method
24             # So you have to use IO::Socket ();
25             # Having done that, this test is now clean
26 14     14   22103 use IO::Socket ();
  14         770487  
  14         562  
27              
28 14         5360 use Socket (qw(
29             AF_INET6 PF_INET6 SOCK_RAW SOCK_STREAM INADDR_ANY SOCK_DGRAM
30             AF_INET SO_REUSEADDR SO_REUSEPORT AF_UNSPEC SO_BROADCAST
31             sockaddr_in
32             )
33 14     14   134 );
  14         29  
34              
35             # IO::Socket and Socket already import stuff here - possibly AF_INET6
36             # and PF_INET6 so selectively import things from Socket6.
37             use Socket6 (
38 14         1962 qw(AI_PASSIVE getaddrinfo
39             sockaddr_in6 unpack_sockaddr_in6_all pack_sockaddr_in6_all in6addr_any)
40 14     14   22853 );
  14         74419  
41              
42 14     14   126 use Carp;
  14         20  
  14         774  
43 14     14   88 use Errno;
  14         22  
  14         112059  
44              
45             @ISA = qw(IO::Socket);
46             $VERSION = "2.72";
47             #Purpose: allow protocol independent protocol and original interface.
48              
49             my $EINVAL = exists(&Errno::EINVAL) ? Errno::EINVAL() : 1;
50              
51             IO::Socket::INET6->register_domain( AF_INET6 );
52              
53              
54             my %socket_type = ( tcp => SOCK_STREAM,
55             udp => SOCK_DGRAM,
56             icmp => SOCK_RAW
57             );
58              
59             sub new {
60 57     57 1 5234867 my $class = shift;
61 57 100       628 unshift(@_, "PeerAddr") if @_ == 1;
62 57         764 return $class->SUPER::new(@_);
63             }
64              
65             # Parsing analysis:
66             # addr,port,and proto may be syntactically related...
67             sub _sock_info {
68 59     59   321 my($addr,$port,$proto) = @_;
69 59         90 my $origport = $port;
70 59         123 my @proto = ();
71 59         96 my @serv = ();
72              
73 59 100       250 if (defined $addr) {
74 24 100       409 if (!Socket6::inet_pton(AF_INET6,$addr)) {
75 15 100       716 if($addr =~ s,^\[([\da-fA-F:]+)\]:([\w\(\)/]+)$,$1,) {
    50          
    50          
    100          
76 4         65 $port = $2;
77             } elsif($addr =~ s,^\[(::[\da-fA-F.:]+)\]:([\w\(\)/]+)$,$1,) {
78 0         0 $port = $2;
79             } elsif($addr =~ s,^\[([\da-fA-F:]+)\],$1,) {
80 0         0 $port = $origport;
81             } elsif($addr =~ s,:([\w\(\)/]+)$,,) {
82 5         87 $port = $1
83             }
84             }
85             }
86              
87             # $proto as "string".
88 59 100 100     768 if(defined $proto && $proto =~ /\D/) {
89 19 50       17944 if(@proto = getprotobyname($proto)) {
90 19   50     115 $proto = $proto[2] || undef;
91             }
92             else {
93 0         0 $@ = "Bad protocol '$proto'";
94 0         0 return;
95             }
96             }
97              
98 59 100       199 if(defined $port) {
99 18 50       182 my $defport = ($port =~ s,\((\d+)\)$,,) ? $1 : undef;
100 18         226 my $pnum = ($port =~ m,^(\d+)$,)[0];
101              
102 18 50 0     161 @serv = getservbyname($port, $proto[0] || "")
103             if ($port =~ m,\D,);
104              
105 18   33     316 $port = $serv[2] || $defport || $pnum;
106 18 50       145 unless (defined $port) {
107 0         0 $@ = "Bad service '$origport'";
108 0         0 return;
109             }
110              
111 18 50 0     98 $proto = (getprotobyname($serv[3]))[2] || undef
      33        
112             if @serv && !$proto;
113             }
114             #printf "Selected port is $port and proto is $proto \n";
115              
116 59   100     1075 return ($addr || undef,
      100        
      100        
117             $port || undef,
118             $proto || undef,
119             );
120              
121             }
122              
123             sub _error {
124 2     2   38 my $sock = shift;
125 2         12 my $err = shift;
126             {
127 2         6 local($!);
  2         18  
128 2         7 my $title = ref($sock).": ";
129 2 50       76 $@ = join("", $_[0] =~ /^$title/ ? "" : $title, @_);
130 2 50       54 close($sock)
131             if(defined fileno($sock));
132             }
133 2         5 $! = $err;
134 2         40 return undef;
135             }
136              
137             sub configure {
138 40     40 1 1011800 my($sock,$arg) = @_;
139              
140 40 50 33     274 $arg->{LocalAddr} = $arg->{LocalHost}
141             if exists $arg->{LocalHost} && !exists $arg->{LocalAddr};
142 40 50 33     238 $arg->{PeerAddr} = $arg->{PeerHost}
143             if exists $arg->{PeerHost} && !exists $arg->{PeerAddr};
144              
145 40         94 my $family = $arg->{Domain};
146             # in case no local and peer is given we prefer AF_INET6
147             # because we are IO::Socket::INET6
148 40   100     1215 $family ||= ! $arg->{LocalAddr} && ! $arg->{PeerAddr} && AF_INET6
      100        
149             || AF_UNSPEC;
150              
151             # parse Local*
152 40 50       487 my ($laddr,$lport,$proto) = _sock_info(
153             $arg->{LocalAddr},
154             $arg->{LocalPort},
155             $arg->{Proto}
156             ) or return _error($sock, $!, "sock_info: $@");
157 40   100     585 $laddr ||= '';
158 40   100     252 $lport ||= 0;
159 40   66     5169 $proto ||= (getprotobyname('tcp'))[2];
160              
161              
162             # MSWin32 expects at least one of $laddr or $lport to be specified
163             # and does not accept 0 for $lport if $laddr is specified.
164 40 50       576 if ($^O eq 'MSWin32') {
165 0 0 0     0 if ((!$laddr) && (!$lport)) {
    0          
166 0 0       0 $laddr = ($family == AF_INET) ? '0.0.0.0' : '::';
167 0         0 $lport = '';
168             } elsif (!$lport) {
169 0         0 $lport = '';
170             }
171             }
172              
173 40   33     3260 my $type = $arg->{Type} || $socket_type{(getprotobynumber($proto))[0]};
174              
175             # parse Peer*
176 40         97 my($rport,$raddr);
177 40 100       171 unless(exists $arg->{Listen}) {
178 19 50       256 ($raddr,$rport) = _sock_info(
179             $arg->{PeerAddr},
180             $arg->{PeerPort},
181             $proto
182             ) or return _error($sock, $!, "sock_info: $@");
183             }
184              
185             # find out all combinations of local and remote addr with
186             # the same family
187 40         2330 my @lres = getaddrinfo($laddr,$lport,$family,$type,$proto,AI_PASSIVE);
188 40 50       6207 return _error($sock, $EINVAL, "getaddrinfo: $lres[0]") if @lres<5;
189 40         70 my @rres;
190 40 100       127 if ( defined $raddr ) {
191 14         3977 @rres = getaddrinfo($raddr,$rport,$family,$type,$proto);
192 14 50       145 return _error($sock, $EINVAL, "getaddrinfo: $rres[0]") if @rres<5;
193             }
194              
195 40         68 my @flr;
196 40 100       121 if (@rres) {
197             # Collect all combinations with the same family in lres and rres
198             # the order we search should be defined by the order of @rres,
199             # not @lres!
200 14         102 for( my $r=0;$r<@rres;$r+=5 ) {
201 23         87 for( my $l=0;$l<@lres;$l+=5) {
202 45         72 my $fam_listen = $lres[$l];
203 45 100       214 next if $rres[$r] != $fam_listen; # must be same family
204 23         158 push @flr,[ $fam_listen,$lres[$l+3],$rres[$r+3] ];
205             }
206             }
207             } else {
208 26         105 for( my $l=0;$l<@lres;$l+=5) {
209 26         68 my $fam_listen = $lres[$l];
210 26         63 my $lsockaddr = $lres[$l+3];
211             # collect only the binding side
212 26         168 push @flr,[ $fam_listen,$lsockaddr ];
213             }
214             }
215              
216             # try to bind and maybe connect
217             # if multihomed try all combinations until success
218 40         209 for my $flr (@flr) {
219 41         107 my ($family,$lres,$rres) = @$flr;
220              
221 41 100       110 if ( $family == AF_INET6) {
222 38 100 66     381 if ($arg->{LocalFlow} || $arg->{LocalScope}) {
223 1         6 my @sa_in6 = unpack_sockaddr_in6_all($lres);
224 1   50     5 $sa_in6[1] = $arg->{LocalFlow} || 0;
225 1   50     7 $sa_in6[3] = _scope_ntohl($arg->{LocalScope}) || 0;
226 1         12 $lres = pack_sockaddr_in6_all(@sa_in6);
227             }
228             }
229              
230 41 50       407 $sock->socket($family, $type, $proto) or
231             return _error($sock, $!, "socket: $!");
232              
233 41 100       2447 if (defined $arg->{Blocking}) {
234 3 50       31 defined $sock->blocking($arg->{Blocking}) or
235             return _error($sock, $!, "sockopt: $!");
236             }
237              
238 41 100 66     472 if ($arg->{Reuse} || $arg->{ReuseAddr}) {
239 1 50       18 $sock->sockopt(SO_REUSEADDR,1) or
240             return _error($sock, $!, "sockopt: $!");
241             }
242              
243 41 50       149 if ($arg->{ReusePort}) {
244 0 0       0 $sock->sockopt(SO_REUSEPORT,1) or
245             return _error($sock, $!, "sockopt: $!");
246             }
247              
248 41 50       119 if ($arg->{Broadcast}) {
249 0 0       0 $sock->sockopt(SO_BROADCAST,1) or
250             return _error($sock, $!, "sockopt: $!");
251             }
252              
253 41 100       161 if ( $family == AF_INET ) {
254 3         42 my ($p,$a) = sockaddr_in($lres);
255 3 100 50     72 $sock->bind($lres) or return _error($sock, $!, "bind: $!")
      66        
256             if ($a ne INADDR_ANY or $p!=0);
257             } else {
258 38         391 my ($p,$a) = sockaddr_in6($lres);
259 38 100 100     771 $sock->bind($lres) or return _error($sock, $!, "bind: $!")
      100        
260             if ($a ne in6addr_any or $p!=0);
261             }
262              
263 40 100       364 if(exists $arg->{Listen}) {
264 20 50 100     278 $sock->listen($arg->{Listen} || 5) or
265             return _error($sock, $!, "listen: $!");
266             }
267              
268             # connect only if PeerAddr and thus $rres is given
269 40 100       664 last if ! $rres;
270              
271 15 100       54 if ( $family == AF_INET6) {
272 14 50 33     126 if ($arg->{PeerFlow} || $arg->{PeerScope}) {
273 0         0 my @sa_in6 = unpack_sockaddr_in6_all($rres);
274 0   0     0 $sa_in6[1] = $arg->{PeerFlow} || 0;
275 0   0     0 $sa_in6[3] = _scope_ntohl($arg->{PeerScope}) || 0;
276 0         0 $rres = pack_sockaddr_in6_all(@sa_in6);
277             }
278             }
279              
280 15         74 undef $@;
281 15 100       117 last if $sock->connect($rres);
282              
283 2 100 50     617 return _error($sock, $!, $@ || "Timeout")
284             if ! $arg->{MultiHomed};
285              
286             }
287              
288 38         7244 return $sock;
289             }
290              
291             sub _scope_ntohl($)
292             {
293             # As of Socket6 0.17 the scope field is incorrectly put into
294             # network byte order when it should be in host byte order
295             # in the sockaddr_in6 structure. We correct for that here.
296              
297 2 50 50 2   136 if ((Socket6->VERSION <= 0.17) && (pack('s', 0x1234) ne pack('n', 0x1234)))
298             {
299 0         0 unpack('N', pack('V', $_[0]));
300             } else {
301 2         17 $_[0];
302             }
303             }
304              
305             sub sockdomain
306             {
307 32     32 1 144 my $sock = shift;
308 32 50       294 $sock->SUPER::sockdomain(@_) || AF_INET6;
309             }
310              
311             sub accept
312             {
313 21     21 1 3037582 my $sock = shift;
314              
315 21         1410 my ($new, $peer) = $sock->SUPER::accept(@_);
316              
317 21 100       24086284 return unless defined($new);
318              
319 20         57 ${*$new}{io_socket_domain} = ${*$sock}{io_socket_domain};
  20         71  
  20         1061  
320 20         40 ${*$new}{io_socket_type} = ${*$sock}{io_socket_type};
  20         82  
  20         91  
321 20         40 ${*$new}{io_socket_proto} = ${*$sock}{io_socket_proto};
  20         50  
  20         42  
322              
323 20 50       163 return wantarray ? ($new, $peer) : $new;
324             }
325              
326             sub bind {
327 11 50   11 1 51 @_ == 2 or
328             croak 'usage: $sock->bind(NAME) ';
329 11         15 my $sock = shift;
330 11         142 return $sock->SUPER::bind( shift );
331             }
332              
333             sub connect {
334 15 50   15 1 103 @_ == 2 or
335             croak 'usage: $sock->connect(NAME) ';
336 15         51 my $sock = shift;
337 15         808 return $sock->SUPER::connect( shift );
338             }
339              
340             sub sockaddr {
341 3 50   3 1 16 @_ == 1 or croak 'usage: $sock->sockaddr()';
342 3         6 my ($sock) = @_;
343 3 50       27 return undef unless (my $name = $sock->sockname);
344 3 50       56 ($sock->sockdomain == AF_INET) ? (sockaddr_in($name))[1] : (sockaddr_in6($name))[1];
345             }
346              
347             sub sockport {
348 20 50   20 1 1466 @_ == 1 or croak 'usage: $sock->sockport()';
349 20         37 my($sock) = @_;
350 20 50       123 return undef unless (my $name = $sock->sockname);
351 20 100       349 ($sock->sockdomain == AF_INET) ? (sockaddr_in($name))[0] : (sockaddr_in6($name))[0];
352             }
353              
354             sub sockhost {
355 3 50   3 1 32 @_ == 1 or croak 'usage: $sock->sockhost()';
356 3         5 my ($sock) = @_;
357 3 50       15 return undef unless (my $addr = $sock->sockaddr);
358 3         83 Socket6::inet_ntop($sock->sockdomain, $addr);
359             }
360              
361             sub sockflow
362             {
363 1 50   1 1 6 @_ == 1 or croak 'usage: $sock->sockflow()';
364 1         3 my ($sock) = @_;
365 1 50       6 return undef unless (my $name = $sock->sockname);
366 1 50       15 ($sock->sockdomain == AF_INET6) ? (unpack_sockaddr_in6_all($name))[1] : 0;
367             }
368              
369             sub sockscope
370             {
371 0 0   0 1 0 @_ == 1 or croak 'usage: $sock->sockscope()';
372 0         0 my ($sock) = @_;
373 0 0       0 return undef unless (my $name = $sock->sockname);
374 0 0       0 _scope_ntohl(($sock->sockdomain == AF_INET6) ? (unpack_sockaddr_in6_all($name))[3] : 0);
375             }
376              
377             sub peeraddr {
378 1 50   1 1 4 @_ == 1 or croak 'usage: $sock->peeraddr()';
379 1         2 my ($sock) = @_;
380 1 50       28 return undef unless (my $name = $sock->peername);
381 1 50       48 ($sock->sockdomain == AF_INET) ? (sockaddr_in($name))[1] : (sockaddr_in6($name))[1];
382             }
383              
384             sub peerport {
385 0 0   0 1 0 @_ == 1 or croak 'usage: $sock->peerport()';
386 0         0 my($sock) = @_;
387 0 0       0 return undef unless (my $name = $sock->peername);
388 0 0       0 ($sock->sockdomain == AF_INET) ? (sockaddr_in($name))[0] : (sockaddr_in6($name))[0];
389             }
390              
391             sub peerhost {
392 1 50   1 1 1393 @_ == 1 or croak 'usage: $sock->peerhost()';
393 1         13 my ($sock) = @_;
394 1 50       6 return undef unless (my $addr = $sock->peeraddr);
395 1         31 Socket6::inet_ntop($sock->sockdomain, $addr);
396             }
397              
398             sub peerflow
399             {
400 1 50   1 1 5 @_ == 1 or croak 'usage: $sock->peerflow()';
401 1         4 my ($sock) = @_;
402 1 50       6 return undef unless (my $name = $sock->peername);
403 1 50       111 _scope_ntohl(($sock->sockdomain == AF_INET6) ? (unpack_sockaddr_in6_all($name))[1] : 0);
404             }
405              
406             sub peerscope
407             {
408 0 0   0 1   @_ == 1 or croak 'usage: $sock->peerscope()';
409 0           my ($sock) = @_;
410 0 0         return undef unless (my $name = $sock->peername);
411 0 0         ($sock->sockdomain == AF_INET6) ? (unpack_sockaddr_in6_all($name))[3] : 0;
412             }
413              
414             1;
415              
416             __END__