File Coverage

blib/lib/Socket/More.pm
Criterion Covered Total %
statement 260 307 84.6
branch 75 116 64.6
condition 53 64 82.8
subroutine 30 36 83.3
pod 12 23 52.1
total 430 546 78.7


line stmt bran cond sub pod time code
1             package Socket::More;
2              
3 2     2   154241 use 5.036000;
  2         7  
4              
5 2     2   1140 use Import::These qw;
  2         2187  
  2         17  
6 2     2   13121 use Socket::More::Lookup ();
  2         1718  
  2         57  
7              
8 2     2   541 use Data::Cmp qw;
  2         765  
  2         167  
9 2     2   1113 use Data::Combination;
  2         1511  
  2         101  
10              
11              
12              
13             my @af_2_name;
14             my %name_2_af;
15             my @sock_2_name;
16             my %name_2_sock;
17              
18 2         14 use constant::more IPV4_ANY=>"0.0.0.0",
19 2     2   12 IPV6_ANY=>"::";
  2         3  
20              
21             BEGIN{
22             #build a list of address family names from socket
23 2     2   221 my @names=grep /^AF_/, keys %Socket::More::Constants::;
24 2     2   523 no strict;
  2         3  
  2         435  
25 2         22 for my $name (@names){
26 24         27 my $val;
27 24         28 eval {
28 24         33 $val=&{$name};
  24         69  
29             };
30 24 50       41 unless($@){
31 24         36 $name_2_af{$name}=$val;
32 24         40 $af_2_name[$val]=$name;
33             }
34             }
35              
36              
37 2         112 @names=grep /^SOCK_/, keys %Socket::More::Constants::;
38              
39             #filter out the following bit masks on BSD, to prevent a huge array:
40             #define SOCK_CLOEXEC 0x10000000
41             #define SOCK_NONBLOCK 0x20000000
42            
43 2         22 for my $ignore(qw){
44 4         15 @names=grep $_ ne $ignore, @names;
45             }
46 2         4 for my $name (@names){
47 10         12 my $val;
48 10         9 eval {
49 10         13 $val=&{$name};
  10         27  
50             };
51 10 50       28 unless($@){
52 10         19 $name_2_sock{$name}=$val;
53 10         170 $sock_2_name[$val]=$name;
54             }
55             }
56             }
57              
58              
59              
60 2         17 use Export::These qw<
61             sockaddr_passive
62             parse_passive_spec
63              
64             socket
65              
66             family_to_string
67             string_to_family
68              
69             socktype_to_string
70             sock_to_string
71              
72             string_to_socktype
73             string_to_sock
74              
75             unpack_sockaddr
76              
77              
78             has_IPv4_interface
79             has_IPv6_interface
80              
81             reify_ports
82             reify_ports_unshared
83              
84             sockaddr_family
85              
86              
87             pack_sockaddr_un
88             unpack_sockaddr_un
89              
90             pack_sockaddr_in
91             unpack_sockaddr_in
92              
93             unpack_sockaddr_in6
94             pack_sockaddr_in6
95              
96              
97 2     2   12 >;
  2         7  
98              
99             sub _reexport {
100 3     3   2994 Socket::More::Constants->import;
101 3         9072 Socket::More::Lookup->import;
102 3         1281 Socket::More::Interface->import;
103             }
104              
105             our $VERSION = 'v0.5.2';
106              
107             sub string_to_family;
108             sub string_to_socktype;
109              
110             # NOTE: These constants allow for perl to optimise away the false condition
111             # tests per platform
112 2         19 use constant::more IS_DARWIN=> !!($^O =~ /darwin/i),
113             IS_LINUX=> !!($^O =~ /linux/i),
114 2     2   2471 IS_BSD=> !!($^O =~ /bsd/i);
  2         5  
115              
116              
117             #Network interface stuff
118             #=======================
119             #
120             #
121             sub sockaddr_family {
122 47     47 0 299 if(IS_LINUX){
123 47         106 return unpack "S", $_[0];
124             }
125 0         0 if(IS_DARWIN){
126             return unpack "xC", $_[0];
127             }
128 0         0 if(IS_BSD){
129             return unpack "xC", $_[0];
130             }
131             }
132              
133              
134              
135             sub socket {
136              
137 12 100   12 1 3494 if(ref($_[1]) eq "HASH"){
138             #assume a 'interface object no need for remaining args
139            
140             #v 0.5.0 rename type to socktype
141 1 50       3 $_[1]{socktype}=delete $_[1]{type} if exists $_[1]{type};
142 1         28 return CORE::socket $_[0], $_[1]{family}, $_[1]{socktype}, $_[1]{protocol};
143             }
144             else{
145 11         401 return &CORE::socket;
146             }
147             }
148              
149              
150             # Creates sockets based on list of specifications
151             sub sockets {
152              
153 0     0 1 0 my @fifo=@_;
154              
155 0         0 while(@fifo){
156 0         0 my $spec=shift @fifo;
157              
158 0 0       0 if(ref($spec) eq "HASH"){
    0          
159             # process as a spec
160 0         0 my $sock;
161 0         0 my $res=Socket::More::socket $sock, $spec;
162 0         0 $spec->{fh}=$sock;
163             }
164             elsif(ref($spec) eq "ARRAY"){
165             # array of specs
166 0         0 unshift @fifo, @$spec;
167             }
168             }
169              
170              
171             # output the inputs as they came in
172 0         0 @_;
173             }
174              
175             sub bind_spec {
176 0     0 0 0 my @fifo=@_;
177 0         0 while(@fifo){
178            
179             }
180             }
181              
182             # Add an handlers for a passive specification. For stream type connections this means what to do when a new socket is accepted.
183             # For datagram types it is what to do with the data
184       0 0   sub handle_spec {
185             # For streams, we are expecting a list of sockets and peers
186             #
187              
188              
189             # For datagrams, we are expecting the list of socket (the passive one) and the peer of the message
190             #
191             #
192              
193             # In both cases the handler is a sub which returns a sub. The returned sub
194             # ref is the code which will directly process data. The sub called will resolve based on the passive spec (tag) and an external table
195             # Note an acceptor class should bind/resolve this
196              
197             }
198              
199              
200              
201              
202             sub unpack_sockaddr_un {
203 21     21 0 470 my ($size, $fam, $name);
204 21         23 if(IS_LINUX){
205 21         48 ($fam,$name)=unpack "SZ[108]", $_[0];
206             }
207 21         25 if(IS_DARWIN){
208              
209             ($size,$fam,$name)=unpack "CCZ[104]", $_[0];
210             }
211 21         24 if(IS_BSD){
212             ($size,$fam,$name)=unpack "CCZ[104]", $_[0];
213             }
214              
215            
216 21         57 $name;
217             }
218              
219             sub pack_sockaddr_un {
220             #pack PACK_SOCKADDR_UN, 106, AF_UNIX, $_[0];
221             #PLATFORM eq pack PACK_SOCKADDR_UN, AF_UNIX, $_[0];
222 34     34 0 476 if(IS_LINUX){
223 34         2742 return pack "SZ[108]", AF_UNIX, $_[0];
224             }
225 0         0 if(IS_DARWIN){
226             return pack "CCZ[104]", 106, AF_UNIX, $_[0];
227             }
228 0         0 if(IS_BSD){
229             return pack "CCZ[104]", 106, AF_UNIX, $_[0];
230             }
231             }
232              
233              
234              
235             sub pack_sockaddr_in {
236             #pack PACK_SOCKADDR_IN, AF_INET, $_[0], $_[1];
237 61     61 0 173686 if(IS_LINUX){
238 61         196 return pack "Sna4x8", AF_INET, $_[0], $_[1];
239             }
240 0         0 if(IS_DARWIN){
241             return pack "xCna4x8", AF_INET, $_[0], $_[1];
242             }
243 0         0 if(IS_BSD){
244             return pack "xCna4x8", AF_INET, $_[0], $_[1];
245             }
246             }
247              
248             sub unpack_sockaddr_in {
249             #my ($port, $addr)=
250 61     61 0 648 unpack "na4", substr($_[0], 2);
251             #($port,$addr);
252             }
253              
254              
255              
256             sub pack_sockaddr_in6 {
257             #pack PACK_SOCKADDR_IN6, AF_INET6, $_[0], $_[3]//0, $_[1], $_[2]//0;
258 31     31 0 484 if(IS_LINUX){
259 31   100     174 return pack "snNa16N", AF_INET6, $_[0], $_[3]//0, $_[1], $_[2]//0;
      100        
260             }
261 0         0 if(IS_DARWIN){
262             return pack "xCnNa16N", AF_INET6, $_[0], $_[3]//0, $_[1], $_[2]//0;
263             }
264 0         0 if(IS_BSD){
265             return pack "xCnNa16N", AF_INET6, $_[0], $_[3]//0, $_[1], $_[2]//0;
266             }
267             }
268              
269             sub unpack_sockaddr_in6{
270 31     31 0 321 my($port,$flow,$ip,$scope)=unpack "nNa16N", substr($_[0], 2);
271 31         82 ($port,$ip, $scope, $flow);
272             }
273              
274             #Return a socket configured for the address
275              
276             sub unpack_sockaddr{
277 0     0 0 0 my ($addr)=@_;
278 0         0 my $family=sockaddr_family $addr;
279 0 0       0 if($family==AF_INET){
    0          
    0          
280 0         0 return unpack_sockaddr_in $addr;
281             }
282             elsif($family==AF_INET6){
283 0         0 return unpack_sockaddr_in6 $addr;
284             }
285             elsif($family == AF_UNIX){
286 0         0 return unpack_sockaddr_un $addr;
287             }
288             else {
289 0         0 die "upack_sockaddr: unsported family type";
290             }
291             }
292              
293              
294             #Used as pseudo interface for filtering to work
295             sub make_unix_interface {
296              
297             {
298 15     15 0 36 name=>"unix",
299             addr=>pack_sockaddr_un("/thii")
300             }
301             }
302              
303              
304             # Main routine to return passive address structures for binding or adding to
305             # multicast group
306             #
307             sub sockaddr_passive{
308 15     15 1 301674 require Scalar::Util;
309 15         27 my ($spec)=@_;
310              
311             # v0.5.2 Copy the input specs
312 15         53 my %copy=%$spec;
313 15         30 $spec=\%copy;
314              
315             # v0.5.0 renamed type to socktype
316 15 100       44 $spec->{socktype}=delete $spec->{type} if exists $spec->{type};
317              
318 15         25 my $r={};
319              
320             #If no interface provided assume all
321 15   100     72 $r->{interface}=$spec->{interface}//".*";
322            
323             ##############################################
324             # if(ref($r->{interface}) ne "ARRAY"){ #
325             # $r->{interface}=[$r->{interface}]; #
326             # } #
327             ##############################################
328              
329 15   100     48 $r->{socktype}=$spec->{socktype}//[SOCK_STREAM, SOCK_DGRAM];
330 15   100     47 $r->{protocol}=$spec->{protocol}//0;
331              
332             #If no family provided assume all
333 15   100     41 $r->{family}=$spec->{family}//[AF_INET, AF_INET6, AF_UNIX];
334            
335             #Configure port and path
336 15   100     32 $r->{port}=$spec->{port}//[];
337 15   100     39 $r->{path}=$spec->{path}//[];
338            
339             ######
340             #v0.4.0 adds string support for type and family
341            
342             # Convert to arrays for unified interface
343 15         30 for($r->{socktype}, $r->{family}){
344 30 100       57 unless(ref eq "ARRAY"){
345 14         21 $_=[$_];
346             }
347             }
348              
349 15         40 for($r->{socktype}->@*){
350 23 50       76 unless(Scalar::Util::looks_like_number $_){
351 0         0 ($_)=string_to_socktype $_;
352             }
353             }
354              
355 15         24 for($r->{family}->@*){
356 31 100       51 unless(Scalar::Util::looks_like_number $_){
357 3         5 ($_)=string_to_family $_;
358             }
359             }
360             # End
361             #####
362              
363              
364             #NOTE: Need to add an undef value to port and path arrays. Port and path are
365             #mutually exclusive
366 15 100       33 if(ref($r->{port}) eq "ARRAY"){
367 8         20 unshift $r->{port}->@*, undef;
368             }
369             else {
370 7         12 $r->{port}=[undef, $r->{port}];
371             }
372              
373              
374 15 50       36 if(ref($r->{path}) eq "ARRAY"){
375 15         24 unshift $r->{path}->@*, undef;
376             }
377             else {
378 0         0 $r->{path}=[undef, $r->{path}];
379             }
380              
381 15 50 33     53 die "No port number specified, no address information will be returned" if ($r->{port}->@*==0) or ($r->{path}->@*==0);
382              
383             #Delete from combination specification... no need to make more combos
384             #
385 15         23 my $enable_group=exists $spec->{group};
386              
387 15         20 my $address=delete $spec->{address};
388 15         24 my $group=delete $spec->{group};
389 15         36 my $data=delete $spec->{data};
390 15   100     34 my $flags=(delete $spec->{flags})//0;
391              
392 15   100     63 $address//=".*";
393 15   100     39 $group//=".*";
394              
395             #Ensure we have an array for later on
396 15 50       27 if(ref($address) ne "ARRAY"){
397 15         43 $address=[$address];
398             }
399              
400 15 50       27 if(ref($group) ne "ARRAY"){
401 15         24 $group=[$group];
402             }
403              
404 15         24 my @interfaces=(make_unix_interface, Socket::More::getifaddrs);
405              
406             #Check for special cases here and adjust accordingly
407 15         55 my @new_address;
408             my @new_interfaces;
409             ##my @new_spec_int;
410 15         0 my @new_fam;
411              
412             # IF IPV4_ANY or IPV6_ANY is specified, nuke any other address provided
413             #
414 15 100       32 if(grep /${\IPV4_ANY()}/, @$address){
  15         152  
415             #push @new_spec_int, IPV4_ANY;
416 5         7 push @new_address, IPV4_ANY;
417 5         8 push @new_fam, AF_INET;
418 5         5 my @results;
419 5         52 Socket::More::Lookup::getaddrinfo(
420             IPV4_ANY,
421             "0",
422             {flags=>AI_NUMERICHOST|AI_NUMERICSERV, family=>AF_INET},
423             @results
424             );
425              
426              
427 5         26 push @new_interfaces, ({name=>IPV4_ANY,addr=>$results[0]{addr}});
428             }
429              
430 15 100       26 if(grep /${\IPV6_ANY()}/, @$address){
  15         73  
431             #push @new_spec_int, IPV6_ANY;
432 1         2 push @new_address, IPV6_ANY;
433 1         2 push @new_fam, AF_INET6;
434 1         2 my @results;
435 1         11 Socket::More::Lookup::getaddrinfo(
436             IPV6_ANY,
437             "0",
438             {flags=>AI_NUMERICHOST|AI_NUMERICSERV, family=>AF_INET6},
439             @results
440             );
441 1         5 push @new_interfaces, ({name=>IPV6_ANY, addr=>$results[0]{addr}});
442             }
443              
444              
445             # TODO: Also add special case for multicast interfaces? for datagrams?
446              
447 15 100       25 if(@new_address){
448 6         10 @$address=@new_address;
449 6         12 @interfaces=@new_interfaces;
450 6         12 $r->{interface}=[".*"];
451             }
452              
453             #$r->{family}=[@new_fam];
454              
455             #Handle localhost
456 15 50       40 if(grep /localhost/, @$address){
457 0         0 @$address=('^127.0.0.1$','^::1$');
458 0         0 $r->{interface}=[".*"];
459             }
460              
461            
462              
463 15         26 $r->{address}=$address;
464              
465             #Generate combinations
466 15         54 my $result=Data::Combination::combinations $r;
467            
468              
469             #Retrieve the interfaces from the os
470             #@interfaces=(make_unix_interface, Socket::More::getifaddrs);
471              
472              
473             #Poor man dereferencing
474 15         13633 my @results=$result->@*;
475            
476             #Force preselection of matching interfaces
477             @interfaces=grep {
478 15         43 my $interface=$_;
  51         51  
479 51         55 scalar grep {$interface->{name} =~ $_->{interface}} @results
  1792         3398  
480             } @interfaces;
481              
482             #Validate Family and fill out port and path
483 2     2   3853 no warnings "uninitialized";
  2         4  
  2         5075  
484              
485 15         16 my @output;
486              
487 15         27 for my $interface (@interfaces){
488 46         77 my $fam= sockaddr_family($interface->{addr});
489 46         71 for(@results){
490 1782 100       2639 next if $fam != $_->{family};
491              
492             #Filter out any families which are not what we asked for straight up
493              
494             goto CLONE if ($fam == AF_UNIX)
495             && ($interface->{name} eq "unix")
496             #&& ("unix"=~ $_->{interface})
497             && (defined($_->{path}))
498 484 100 66     964 && (!defined($_->{port}));
      100        
      100        
499              
500              
501             goto CLONE if
502             ($fam == AF_INET or $fam ==AF_INET6)
503             && defined($_->{port})
504             && !defined($_->{path})
505 466 100 100     1636 && ($_->{interface} ne "unix");
      100        
      100        
      66        
506              
507 370         426 next;
508              
509 114         501 CLONE:
510             my %clone=$_->%*;
511 114         200 my $clone=\%clone;
512 114         217 $clone{data}=$spec->{data};
513 114         144 $clone{flags}=$spec->{flags};
514              
515             #A this point we have a valid family and port/path combo
516             #
517 114         132 my ($err, $res, $service);
518              
519             #Port or path needs to be set
520 114 100       174 if($fam == AF_INET){
    100          
    50          
521 65 100 66     175 if(!exists $_->{address} or $_->{address} eq ".*"){
522 60         87 my (undef, $ip)=unpack_sockaddr_in($interface->{addr});
523              
524             # Get the hostname/ip address as human readable string aka inet_ntop($fam, $ip);
525 60         208 Socket::More::Lookup::getnameinfo($interface->{addr}, my $host="", my $port="", NI_NUMERICHOST|NI_NUMERICSERV);
526              
527             # Pack with desired port
528 60         88 $clone->{address}=$host;
529 60         84 $clone->{addr}=pack_sockaddr_in($_->{port}, $ip);
530             }
531             else {
532 5         6 my @results;
533             #Socket::More::Lookup::getaddrinfo($_->{address},$_->{port},{flags=>AI_NUMERICHOST|AI_NUMERICSERV, family=>AF_INET,socktype=>$_->{socktype},protocol=>$_->{protocol}}, @results);
534 5         35 Socket::More::Lookup::getaddrinfo($_->{address},$_->{port},$_, @results);
535 5         12 $clone->{addr}=$results[0]{addr};
536             }
537 65         96 $clone->{interface}=$interface->{name};
538 65         78 $clone->{if}=$interface; # From v0.5.0
539              
540 65 50       122 if($enable_group){
541 0         0 require Socket::More::IPRanges;
542 0         0 $clone->{group}=Socket::More::IPRanges::ipv4_group($clone->{address});
543             }
544             }
545              
546             elsif($fam == AF_INET6){
547 31 100 66     108 if(!exists $_->{address} or $_->{address} eq ".*"){
548 30         51 my(undef, $ip, $scope, $flow_info)=unpack_sockaddr_in6($interface->{addr});
549 30         102 Socket::More::Lookup::getnameinfo($interface->{addr}, my $host="", my $port="", NI_NUMERICHOST|NI_NUMERICSERV);
550 30         39 $clone->{address}=$host;
551 30         58 $clone->{addr}=pack_sockaddr_in6($_->{port},$ip, $scope, $flow_info);
552             }
553             else {
554 1         3 my @results;
555             #Socket::More::Lookup::getaddrinfo($_->{address},$_->{port},{flags=>AI_NUMERICHOST|AI_NUMERICSERV, family=>AF_INET6,socktype=>$_->{socktype},protocol=>$_->{protocol}}, @results);
556 1         9 Socket::More::Lookup::getaddrinfo($_->{address},$_->{port}, $_, @results);
557 1         3 $clone->{addr}=$results[0]{addr};
558             }
559              
560 31         49 $clone->{interface}=$interface->{name};
561 31         57 $clone->{if}=$interface; # From v0.5.0
562 31 50       50 if($enable_group){
563 0         0 require Socket::More::IPRanges;
564 0         0 $clone->{group}=Socket::More::IPRanges::ipv6_group($clone->{address});
565             }
566             }
567              
568             elsif($fam == AF_UNIX){
569 18 100       35 my $suffix=$_->{socktype}==SOCK_STREAM?"_S":"_D";
570              
571 18         54 $clone->{addr}=pack_sockaddr_un $_->{path}.$suffix;
572 18         29 my $path=unpack_sockaddr_un($clone->{addr});
573 18         23 $clone->{address}=$path;
574 18         22 $clone->{path}=$path;
575 18         29 $clone->{interface}=$interface->{name};
576 18 50       30 $clone->{group}="UNIX" if $enable_group;
577             }
578             else {
579 0         0 die "Unsupported family type";
580 0         0 last;
581             }
582             #$clone->{interface}=$interface->{name};
583              
584             #Final filtering of address and group
585 114 50       152 next unless grep {$clone->{address}=~ /$_/i } @$address;
  114         589  
586            
587 114 50       173 if($enable_group){
588 0 0       0 next unless grep {$clone->{group}=~ /$_/i } @$group;
  0         0  
589             }
590 114 50       180 next unless defined $clone->{addr};
591              
592             #copy data to clone
593 114         132 $clone->{data}=$data;
594 114         128 $clone->{flags}=$flags;
595 114         165 push @output, $clone;
596             }
597             }
598              
599 15         15 my @list;
600              
601             #Ensure items in list are unique
602 15 100       30 push @list, $output[0] if @output;
603 15         60 for(my $i=1; $i<@output; $i++){
604 103         131 my $out=$output[$i];
605 103         130 my $found=grep {!cmp_data($_, $out)} @list;
  1024         37777  
606 103 100       9549 push @list, $out unless $found;
607             }
608              
609              
610            
611             #@output=@list;
612             #@output=siikeysort {$_->{interface}, $_->{family}, $_->{type}} @output;
613             @output=sort {
614             $a->{interface} cmp $b->{interface} || $a->{family} cmp $b->{family}|| $a->{socktype} cmp $b->{socktype}
615 248 50 100     813 }
616             #v0.5.0 renamed type to socktype, alias back for compatibility
617 15         53 map {$_->{type}=$_->{socktype};$_} @list;
  102         112  
  102         203  
618            
619             }
620              
621             #Parser for CLI -l options
622             sub parse_passive_spec {
623             #splits a string by : and tests each set
624 5     5 1 3065 my @output;
625 5         17 my @full=qw;
626 5         12 for my $input(@_){
627 5         8 my %spec;
628              
629             #split fields by comma, each field is a key value pair,
630             #An exception is made for address::port
631              
632 5         20 my @field=split ",", $input;
633              
634             #Add information to the spec
635 5         13 for my $field (@field){
636 11 100       30 if($field!~/=/){
637 4         6 for($field){
638 4 100       19 if(/(.*):(.*)$/){
639             #TCP and ipv4 only
640 3         13 $spec{address}=[$1];
641 3 50       10 $spec{port}=length($2)?[$2]:[];
642              
643 3 50       13 if($spec{address}[0] =~ /localhost/){
    100          
644             #do not set family
645             #$spec{address}=['^127.0.0.1$','^::1$'];
646             }
647             elsif($spec{address}[0] eq ""){
648 2         8 $spec{address}=[IPV6_ANY, IPV4_ANY];
649              
650             #$spec{family}=[AF_INET, AF_INET6];
651             }
652             else{
653 1 50 33     4 if($spec{address}[0]=~s|^\[|| and
654             $spec{address}[0]=~s|\]$||){
655 0         0 $spec{family}=[AF_INET6];
656             }
657             else{
658             #assume an ipv4 address
659 1         3 $spec{family}=[AF_INET];
660             }
661             }
662              
663             #$spec{type}=[SOCK_STREAM];
664              
665             }
666             else {
667             #Unix path
668 1         3 $spec{path}=[$field];
669             #$spec{type}=[SOCK_STREAM];
670 1         3 $spec{family}=[AF_UNIX];
671 1         2 $spec{interface}=['unix'];
672             }
673             }
674             #goto PUSH;
675 4         7 next;
676             }
677 7         19 my ($key, $value)=split "=", $field,2;
678 7         40 $key=~s/ //g;
679 7         9 $value=~s/ //g;
680 7         9 my @val;
681             #Ensure only 0 or 1 keys match
682 7 50       136 die "Ambiguous field name: $key" if 2<=grep /^$key/i, @full;
683 7         63 ($key)=grep /^$key/i, @full;
684              
685             # The string to in constant lookup is also done in sockadd_passive in
686             # v0.4.0 onwards. The conversion below is to keep compatible with
687             # previous version. Also parsing to an actual value is useful outside of
688             # use of this module
689             #
690 7 100       19 if($key eq "family"){
    50          
    100          
691             #Convert string to integer
692 2         5 @val=string_to_family($value);
693             }
694             elsif($key eq "socktype"){
695             #Convert string to integer
696 0         0 @val=string_to_socktype($value);
697             }
698             elsif($key eq "type"){
699             #Convert string to integer
700 4         5 $key="socktype"; #v0.5.0 type was renamed to socktype.
701 4         10 @val=string_to_socktype($value);
702             }
703             else{
704 1         2 @val=($value);
705              
706             }
707            
708             defined($spec{$key})
709             ? (push $spec{$key}->@*, @val)
710 7 50       22 : ($spec{$key}=[@val]);
711             }
712             PUSH:
713 5         11 push @output, \%spec;
714             }
715 5         17 @output;
716             }
717              
718              
719 2     2 1 278 sub family_to_string { $af_2_name[$_[0]]; }
720              
721             sub string_to_family {
722 8     8 1 756 my ($string)=@_;
723 8         45 my @found=grep { /$string/i} sort keys %name_2_af;
  96         264  
724 8         30 @name_2_af{@found};
725             }
726              
727 2     2 1 267 sub socktype_to_string { $sock_2_name[$_[0]]; }
728             # v0.5.0 renamed. Alias to old name
729             *sock_to_string=\*socktype_to_string;
730              
731              
732             sub string_to_socktype {
733 6     6 1 311 my ($string)=@_;
734 6         31 my @found=grep { /$string/i} sort keys %name_2_sock;
  30         121  
735 6         19 @name_2_sock{@found};
736             }
737             # v0.5.0 renamed. Alias to old name
738             *string_to_sock=\*string_to_socktype;
739              
740              
741             sub has_IPv4_interface {
742 0     0 1 0 my $spec={
743             family=>AF_INET,
744             socktype=>SOCK_STREAM,
745             port=>0
746             };
747 0         0 my @results=sockaddr_passive $spec;
748            
749 0         0 @results>=1;
750              
751             }
752              
753             sub has_IPv6_interface{
754 0     0 1 0 my $spec={
755             family=>AF_INET6,
756             socktype=>SOCK_STREAM,
757             port=>0
758             };
759 0         0 my @results=sockaddr_passive $spec;
760            
761 0         0 @results>=1;
762              
763             }
764              
765             sub _reify_ports {
766              
767 2     2   3 my $shared=shift;
768             #if any specs contain a 0 for the port number, then perform a bind to get one from the OS.
769             #Then close the socket, and hope that no one takes it :)
770            
771 2         3 my $port;
772             map {
773 2 50 33     4 if(defined($_->{port}) and $_->{port}==0){
  2         8  
774 2 50 66     9 if($shared and defined $port){
775 0         0 $_->{port}=$port;
776             }
777             else{
778             #attempt a bind
779 2 50       62 die "Could not create socket to reify port $!" unless CORE::socket(my $sock, $_->{family}, $_->{socktype}, 0);
780 2 50       15 die "Could not set reuse address flag $!" unless setsockopt $sock, SOL_SOCKET,SO_REUSEADDR,1;
781 2 50       15 die "Could not bind socket to reify port $!" unless bind($sock, $_->{addr});
782 2         8 my $name=getsockname $sock;
783              
784             #my ($err, $a, $port)=getnameinfo($name, NI_NUMERICHOST);
785             #my ($err, $a, $port)=
786 2         533 my $ok=Socket::More::Lookup::getnameinfo($name, my $host="", my $port="", NI_NUMERICHOST);
787              
788 2 50       11 if($ok){
789 2         4 $_->{port}=$port;
790             }
791 2         37 close $sock;
792             }
793             }
794 2         11 $_;
795             }
796             sockaddr_passive @_;
797             }
798              
799             sub reify_ports {
800 1     1 1 3 _reify_ports 1, @_;
801             }
802              
803             sub reify_ports_unshared {
804 1     1 1 3 _reify_ports 0, @_;
805             }
806              
807              
808             1;
809             __END__