File Coverage

blib/lib/Net/SIP/Util.pm
Criterion Covered Total %
statement 212 261 81.2
branch 119 206 57.7
condition 57 118 48.3
subroutine 28 32 87.5
pod 22 22 100.0
total 438 639 68.5


line stmt bran cond sub pod time code
1              
2             ###########################################################################
3             # Net::SIP::Util
4             # various functions for helping in SIP programs
5             ###########################################################################
6              
7 42     42   324 use strict;
  42         76  
  42         1367  
8 42     42   212 use warnings;
  42         99  
  42         1539  
9              
10             package Net::SIP::Util;
11              
12 42     42   221 use Digest::MD5 'md5_hex';
  42         70  
  42         2300  
13 42         9391 use Socket 1.95 qw(
14             inet_ntop inet_pton
15             AF_INET unpack_sockaddr_in pack_sockaddr_in
16             getaddrinfo
17 42     42   842 );
  42         4723  
18 42     42   318 use Net::SIP::Debug;
  42         74  
  42         270  
19 42     42   326 use Carp qw(confess croak);
  42         82  
  42         2471  
20 42     42   272 use base 'Exporter';
  42         77  
  42         18803  
21              
22             BEGIN {
23 42     42   191 my $mod6 = '';
24 42 50       76 if (eval {
    0          
25 42         26425 require IO::Socket::IP;
26 42         1333540 IO::Socket::IP->VERSION(0.31);
27 42         1564 Socket->import('AF_INET6');
28 42         311 AF_INET6();
29             }) {
30 42         115 $mod6 = 'IO::Socket::IP';
31 42         96 my %cached_proto;
32             *INETSOCK = sub {
33 420 50   420   44382375 return IO::Socket::IP->new(@_) if @_ == 1;
34             # Hack to work around the problem that IO::Socket::IP defaults to
35             # AI_ADDRCONFIG which creates problems if we have only the loopback
36             # interface. If we already know the family this flag is more harmful
37             # then useful.
38 420         4606 my %args = @_;
39             $args{GetAddrInfoFlags} = 0 if ! defined $args{GetAddrInfoFlags}
40 420 100 66     5011 and $args{Domain} || $args{Family};
      66        
41             # cache IO::Socket::IP protocol lookup to speed it up
42             $args{Proto} = $cached_proto{$args{Proto}}
43             ||= getprotobyname($args{Proto})
44             || die "Unknown protocol: $args{Proto}"
45 420 50 50     7184 if $args{Proto};
      66        
46 420         5995 return IO::Socket::IP->new(%args);
47 42         304 };
48              
49             } elsif (eval {
50 0         0 require IO::Socket::INET6;
51 0         0 IO::Socket::INET6->VERSION(2.62);
52 0         0 Socket->import('AF_INET6');
53 0         0 AF_INET6();
54             }) {
55 0         0 $mod6 = 'IO::Socket::INET6';
56             *INETSOCK = sub {
57 0 0       0 return IO::Socket::INET6->new(@_) if @_ == 1;
58 0         0 my %args = @_;
59 0 0       0 $args{Domain} = delete $args{Family} if exists $args{Family};
60 0         0 return IO::Socket::INET6->new(%args);
61 0         0 };
62              
63             } else {
64 0         0 *INETSOCK = sub { return IO::Socket::INET->new(@_) };
  0         0  
65 42     42   320 no warnings 'redefine';
  42         78  
  42         5952  
66             # Since value differs between platforms we set it to something that
67             # should not collide with AF_INET and maybe will even cause inet_ntop
68             # etc to croak. In any case this will only be used if CAN_IPV6 is false
69             # because otherwise we have the correct value from Socket.
70 0         0 *AF_INET6 = sub() { -1 };
71             }
72              
73 42 50       221 *CAN_IPV6 = $mod6 ? sub() { 1 } : sub() { 0 };
74 42 50       164625 Socket->import(qw(unpack_sockaddr_in6 pack_sockaddr_in6)) if $mod6;
75             }
76              
77             our @EXPORT = qw(INETSOCK);
78             our @EXPORT_OK = qw(
79             sip_hdrval2parts sip_parts2hdrval
80             sip_uri2parts sip_parts2uri sip_uri_eq sip_uri2sockinfo sip_sockinfo2uri
81             laddr4dst create_socket_to create_rtp_sockets
82             ip_string2parts ip_parts2string
83             ip_parts2sockaddr ip_sockaddr2parts
84             ip_sockaddr2string
85             ip_is_v4 ip_is_v6 ip_is_v46
86             ip_ptr ip_canonical
87             hostname2ip
88             CAN_IPV6
89             invoke_callback
90             );
91             our %EXPORT_TAGS = ( all => [ @EXPORT_OK, @EXPORT ] );
92              
93             our $RTP_MIN_PORT = 2000;
94             our $RTP_MAX_PORT = 12000;
95              
96             ###########################################################################
97             # creates hash from header val, e.g.
98             # 'Digest method="md5",qop="auth",...','www-authenticate' will result in
99             # ( 'Digest', { method => md5, qop => auth,... } )
100             # Args: ($key,$val)
101             # $key: normalized key (lowercase, long)
102             # $val: value
103             # Returns: ( $data,\%parameter )
104             # $data: initial data
105             # %parameter: additional parameter
106             ###########################################################################
107             my %delimiter = (
108             'www-authenticate' => ',',
109             'proxy-authenticate' => ',',
110             'authorization' => ',',
111             'proxy-authorization' => ',',
112             );
113             sub sip_hdrval2parts {
114 744 50   744 1 2480 croak( "usage: sip_hdrval2parts( key => val )" ) if @_!=2;
115 744         2465 my ($key,$v) = @_;
116 744 50       1964 return if !defined($v);
117 744   100     3994 my $delim = $delimiter{$key} || ';';
118              
119             # split on delimiter (but not if quoted)
120 744         2485 my @v = ('');
121 744         1335 my $quoted = 0;
122 744         1239 my $bracket = 0;
123 744         1199 while (1) {
124 1287 100       13133 if ( $v =~m{\G(.*?)([\\"<>$delim])}gc ) {
125 543 100       3891 if ( $2 eq "\\" ) {
    100          
    100          
    100          
    50          
126 6         27 $v[-1].=$1.$2.substr( $v,pos($v),1 );
127 6         28 pos($v)++;
128             } elsif ( $2 eq '"' ) {
129 62         161 $v[-1].=$1.$2;
130 62 50       186 $quoted = !$quoted if ! $bracket;
131             } elsif ( $2 eq '<' ) {
132 29         137 $v[-1].=$1.$2;
133 29 50 33     300 $bracket = 1 if ! $bracket && ! $quoted;
134             } elsif ( $2 eq '>' ) {
135 29         105 $v[-1].=$1.$2;
136 29 50 33     179 $bracket = 0 if $bracket && ! $quoted;
137             } elsif ( $2 eq $delim ) {
138             # next item if not quoted
139 417 100 66     2136 if ( ! $quoted && ! $bracket ) {
140 380         2467 ( $v[-1].=$1 ) =~s{\s+$}{}; # strip trailing space
141 380         1139 push @v,'' ;
142 380         1528 $v =~m{\G\s+}gc; # skip space after $delim
143             } else {
144 37         136 $v[-1].=$1.$2
145             }
146             }
147             } else {
148             # add rest to last from @v
149 744   100     4059 $v[-1].= substr($v,pos($v)||0 );
150 744         1663 last;
151             }
152             }
153              
154             # with delimiter ',' it starts 'Digest realm=...' so $v[0]
155             # contains method and first parameter
156 744         1547 my $data = shift(@v);
157 744 100       2049 if ( $delim eq ',' ) {
158 8         67 $data =~s{^(\S+)\s*(.*)}{$1};
159 8         32 unshift @v,$2;
160             }
161             # rest will be interpreted as parameters with key|key=value
162 744         1462 my %hash;
163 744         1858 foreach my $vv (@v) {
164 388         2876 my ($key,$value) = split( m{\s*=\s*},$vv,2 );
165 388 100       1146 if ( defined($value) ) {
166 377         1130 $value =~s{^"(.*)"$}{$1}; # unquote
167             # TODO Q: what's the meaning of "\%04", e.g. is it
168             # '%04' or "\\\004" ??
169 377         754 $value =~s{\\(.)}{$1}sg; # unescape backslashes
170 377         711 $value =~s{%([a-fA-F][a-fA-F])}{ chr(hex($1)) }esg; # resolve uri encoding
  0         0  
171             }
172 388         1971 $hash{lc($key)} = $value;
173             }
174 744         3719 return ($data,\%hash);
175             }
176              
177              
178             ###########################################################################
179             # reverse to sip_hdrval2parts
180             # Args: ($key,$data,\%parameter)
181             # $key: normalized key (lowercase, long)
182             # $data: initial data
183             # %parameter: additional parameter
184             # Returns: $val
185             # $val: value
186             ###########################################################################
187             sub sip_parts2hdrval {
188 150     150 1 1125 my ($key,$data,$param) = @_;
189              
190 150   50     2171 my $delim = $delimiter{$key} || ';';
191              
192 150         496 my $val = $data; # FIXME: need to escape $data?
193 150         1165 for my $k ( sort keys %$param ) {
194 67         701 $val .= $delim.$k;
195 67         418 my $v = $param->{$k};
196 67 50       742 if ( defined $v ) {
197             # escape special chars
198 67         578 $v =~s{([%\r\n\t"[:^print:]])}{ sprintf "%%%02x",ord($1) }esg;
  0         0  
199 67 50       2202 $v = '"'.$v.'"' if $v =~m{\s|$delim};
200 67         561 $val .= '='.$v
201             }
202             }
203 150         767 return $val;
204             }
205              
206              
207             ###########################################################################
208             # extract parts from SIP URI
209             # Args: $uri
210             # Returns: $domain || ($domain,$user,$proto,$param,$data)
211             # $domain: SIP domain maybe with port
212             # $user: user part
213             # $proto: 'sip'|'sips'
214             # $param: hashref with params, e.g { transport => 'udp',... }
215             # $data: full part before any params
216             ###########################################################################
217             sub sip_uri2parts {
218 306     306 1 786 my $uri = shift;
219 306 100       1958 $uri = $1 if $uri =~m{<([^>]+)>\s*$};
220 306         1269 my ($data,$param) = sip_hdrval2parts( uri => $uri );
221 306 50       3259 if ( $data =~m{^
222             (?: (sips?) : )?
223             (?: ([^\s\@]*) \@ )?
224             (
225             \[ [^\]\s]+ \] ( : \w+)? # [ipv46_or_host]:port
226             | [^:\s]+ ( : \w+)? # ipv4_or_host:port
227             | (?:[a-f\d]*:){2}[a-f\d\.:]* # ipv6
228             )
229             $}ix ) {
230 306         2012 my ($proto,$user,$domain) = ($1,$2,$3);
231 306         1032 $domain = lc($domain);
232 306   100     1914 $proto ||= 'sip';
233             return wantarray
234 306 50       2737 ? ($domain,$user,lc($proto),$param,$data)
235             : $domain
236             } else {
237 0         0 return;
238             }
239             }
240              
241              
242             ###########################################################################
243             # reverse to sip_uri2parts, e.g. construct SIP URI
244             # Args: ($domain,$user,$proto,$param)
245             # $domain: SIP domain maybe with port or [host,port,?family]
246             # $user: user part
247             # $proto: 'sip'|'sips' - defaults to 'sip'
248             # $param: hashref with params, e.g { transport => 'udp',... }
249             # Args: $uri
250             ###########################################################################
251             sub sip_parts2uri {
252 131     131 1 105709 my ($domain,$user,$proto,$param) = @_;
253 131 100 50     3334 my $uri = sip_parts2hdrval('uri',
    50          
254             ($proto || 'sip'). ':'
255             . ($user ? $user.'@' : '')
256             . (ref($domain) ? ip_parts2string(@$domain) : $domain),
257             $param
258             );
259 131 100 66     2688 return $param && %$param ? "<$uri>" : $uri;
260             }
261              
262             ###########################################################################
263             # Extract the parts from a URI which are relevant for creating the socket, i.e
264             # sips:host:port
265             # sip:host;transport=TCP
266             # Args: $uri,?$opaque
267             # $uri: SIP URI
268             # $opaque: don't enforce that host part of URI looks like hostname or IP
269             # Returns: ($proto,$host,$port,$family)
270             # $proto: udp|tcp|tls|undef
271             # $host: ip or hostname from URI
272             # $port: port from URI
273             # $family: family matching $host, i.e. AF_INET|AF_INET6|undef
274             ###########################################################################
275             sub sip_uri2sockinfo {
276 174 50   174 1 81099 my ($domain,undef,$proto,$param) = sip_uri2parts(shift())
277             or return;
278             $proto =
279             ($proto && $proto eq 'sips') ? 'tls' : # sips -> tls
280 174 100 66     1766 $param->{transport} ? lc($param->{transport}) : # transport -> tcp|udp
    100          
281             undef; # not restricted
282 174         1195 return ($proto, ip_string2parts($domain, shift()));
283             }
284              
285             ###########################################################################
286             # Reverse to sip_uri2sockinfo
287             # Args: (\%hash|$proto,$host,$port,$family)
288             # $proto: udp|tcp|tls|undef
289             # $host: ip or hostname from URI
290             # $port: port from URI
291             # $family: family matching $host, i.e. AF_INET|AF_INET6|undef
292             # %hash: hash with keys proto, host, port, family
293             # Returns: $uri
294             ###########################################################################
295             sub sip_sockinfo2uri {
296             my ($proto,$host,$port,$family) = ref($_[0])
297 0 0   0 1 0 ? @{$_[0]}{qw(proto host port family)}
  0         0  
298             : @_;
299 0 0       0 return sip_parts2uri(
    0          
    0          
    0          
300             ip_parts2string($host,$port,$family),
301             undef,
302             !defined $proto ? ('sip', {}) :
303             $proto eq 'tls' ? ('sips', {}) :
304             $proto eq 'tcp' ? ('sip', { transport => 'TCP' }) :
305             $proto eq 'udp' ? ('sip', {}) :
306             die "invalid proto: '$proto'"
307             )
308             }
309              
310             ###########################################################################
311             # returns true if two URIs are the same
312             # Args: $uri1,$uri2
313             # Returns: true if both URI point to same address
314             ###########################################################################
315             sub sip_uri_eq {
316 9     9 1 18 my ($uri1,$uri2) = @_;
317 9 100       22 return 1 if $uri1 eq $uri2; # shortcut for common case
318 8         19 my ($d1,$u1,$p1) = sip_uri2parts($uri1);
319 8         25 my ($d2,$u2,$p2) = sip_uri2parts($uri2);
320 8 50 33     66 my $port1 = $d1 =~s{:(\d+)$|\[(\d+)\]$}{} ? $1||$2
    100          
321             : $p1 eq 'sips' ? 5061 : 5060;
322 8 50 33     38 my $port2 = $d2 =~s{:(\d+)$|\[(\d+)\]$}{} ? $1||$2
    100          
323             : $p2 eq 'sips' ? 5061 : 5060;
324 8   33     108 return lc($d1) eq lc($d2)
325             && $port1 == $port2
326             && ( defined($u1) ? defined($u2) && $u1 eq $u2 : ! defined($u2))
327             && $p1 eq $p2;
328             }
329              
330             ###########################################################################
331             # fid out local address which is used when connecting to destination
332             # Args: ($dst,@src)
333             # $dst: target IP (or ip:port)
334             # @src: optional list of source IP to try, if not given will use any source
335             # Return: $ip|($ip,$family) - source IP used when reaching destination
336             # Comment:
337             # A UDP socket will be created and connected and then the local address
338             # read from the socket. It is expected that the OS kernel will fill in
339             # the local address when connecting even though no packets are actually
340             # send to the peer
341             ###########################################################################
342             sub laddr4dst {
343 5     5 1 24 my ($dst,@src) = @_;
344 5         243 my ($addr, $port, $fam) = ip_string2parts($dst);
345 5 50       34 $fam or return; # no IP destination
346 5 50       46 for my $src (@src ? @src : (undef)) {
347 5 50 100     45 my $sock = INETSOCK(
    50          
348             Proto => 'udp',
349             Family => $fam,
350             PeerAddr => $addr,
351             PeerPort => $port || 5060,
352             $src ? (LocalAddr => $src) : (),
353             ) or next;
354 5         4435 my @parts = ip_sockaddr2parts(getsockname($sock));
355 5 50       146 return wantarray ? @parts[0,2] : $parts[0];
356             }
357 0         0 return; # no route
358             }
359              
360             ###########################################################################
361             # create socket preferable on port 5060 from which one might reach the given IP
362             # Args: ($dst_addr;$proto)
363             # $dst_addr: the adress which must be reachable from this socket
364             # $proto: udp|tcp|tls, default udp
365             # Returns: ($sock,$ip_port) || $sock || ()
366             # $sock: the created socket
367             # $ip_port: ip:port of socket, only given if called in array context
368             # Comment: the IP it needs to come from works by creating a udp socket
369             # to this host and figuring out it's IP by calling getsockname. Then it
370             # tries to create a socket on this IP using port 5060/5061 and if this does
371             # not work it tries the port 5062..5100 and if this does not work too
372             # it let the system use a random port
373             # If creating of socket fails it returns () and $! is set
374             ###########################################################################
375             sub create_socket_to {
376 5     5 1 13876 my ($dst_addr,$proto) = @_;
377 5   50     104 $proto ||= 'udp';
378              
379 5         37 my ($laddr,$fam) = laddr4dst($dst_addr);
380 5         46 DEBUG( "Local IP is $laddr" );
381              
382             # Bind to this IP
383             # First try port 5060..5100, if they are all used use any port
384             # I get from the system
385 5 50       33 for my $p ( $proto eq 'tls' ? 5061:5060, 5062..5100, 0 ) {
386 8 50       1133 $DEBUG && DEBUG( "try to listen on %s",
387             ip_parts2string($laddr,$p,$fam));
388 8 50       49 my $sock = INETSOCK(
    50          
    100          
389             Family => $fam,
390             LocalAddr => $laddr,
391             $p ? (LocalPort => $p) : (),
392             Proto => $proto eq 'tls' ? 'tcp' : $proto,
393             ) or next;
394              
395 5   33     1885 my $port = $p || (ip_sockaddr2parts(getsockname($sock)))[1];
396 5 50       16 $DEBUG && DEBUG("listen on %s",ip_parts2string($laddr,$port,$fam));
397 5 100       95 return $sock if ! wantarray;
398 3         11 return ($sock,ip_parts2string($laddr,$port,$fam));
399             }
400 0         0 die "even binding to port 0 failed: $!";
401             }
402              
403             ###########################################################################
404             # create RTP/RTCP sockets
405             # Args: ($laddr;$range,$min,$max,$tries)
406             # $laddr: local addr
407             # $range: how many sockets, 2 if not defined
408             # $min: minimal port number, default $RTP_MIN_PORT
409             # $max: maximal port number, default 10000 more than $min
410             # or $RTP_MAX_PORT if $min not given
411             # $tries: how many tries, default 100
412             # Returns: ($port,$rtp_sock,$rtcp_sock,@more_socks)
413             # $port: port of RTP socket, port for RTCP is port+1
414             # $rtp_sock: socket for RTP data
415             # $rtcp_sock: socket for RTCP data
416             # @more_socks: more sockets (if range >2)
417             ###########################################################################
418             sub create_rtp_sockets {
419 48     48 1 318 my ($laddr,$range,$min,$max,$tries) = @_;
420 48   100     483 $range ||= 2;
421 48 50       165 if ( ! $min ) {
422 48         112 $min = $RTP_MIN_PORT;
423 48   33     338 $max ||= $RTP_MAX_PORT;
424             } else {
425 0   0     0 $max ||= $min+10000;
426             }
427 48         217 $min += $min%2; # make even
428 48   50     521 $tries ||= 1000;
429              
430 48         219 my $diff2 = int(($max-$min)/2) - $range +1;
431              
432 48         94 my (@socks,$port);
433 48         154 my $fam = (ip_string2parts($laddr))[2];
434 48         223 while ( $tries-- >0 ) {
435              
436 96 100       16826 last if @socks == $range;
437 48         167 close $_ for @socks;
438 48         99 @socks = ();
439              
440 48         1176 $port = 2*int(rand($diff2)) + $min;
441 48         276 for( my $i=0;$i<$range;$i++ ) {
442 96   50     26851 push @socks, INETSOCK(
443             Family => $fam,
444             Proto => 'udp',
445             LocalAddr => $laddr,
446             LocalPort => $port + $i,
447             ) || last;
448             }
449             }
450 48 50       187 return if @socks != $range; # failed
451 48         353 return ($port,@socks);
452             }
453              
454             ###########################################################################
455             # helper to call callback, set variable..
456             # Args: ($cb;@args)
457             # $cb: callback
458             # @args: additional args for callback
459             # Returns: $rv
460             # $rv: return value of callback
461             # Comment:
462             # callback can be
463             # - code ref: will be called with $cb->(@args)
464             # - object with method run, will be called with $cb->run(@args)
465             # - array-ref with [ \&sub,@myarg ], will be called with $sub->(@myarg,@args)
466             # - scalar ref: the scalar will be set to $args[0] if @args, otherwise true
467             # - regex: returns true if anything in @args matches regex
468             ###########################################################################
469             sub invoke_callback {
470 13748     13748 1 45364 my ($cb,@more_args) = @_;
471 13748 100 66     82578 if ( UNIVERSAL::isa( $cb,'CODE' )) {
    100          
    100          
    100          
    100          
    50          
472             # anon sub
473 3896         15921 return $cb->(@more_args)
474             } elsif ( my $sub = UNIVERSAL::can( $cb,'run' )) {
475             # Callback object
476 4         25 return $sub->($cb,@more_args );
477             } elsif ( UNIVERSAL::isa( $cb,'ARRAY' )) {
478 9674         33774 my ($sub,@args) = @$cb;
479             # [ \&sub,@arg ]
480 9674         41914 return $sub->( @args,@more_args );
481             } elsif ( UNIVERSAL::isa( $cb,'Regexp' )) {
482 2 50       10 @more_args or return;
483 2         7 for(@more_args) {
484 2 50       44 return 1 if m{$cb}
485             }
486 0         0 return 0;
487             } elsif ( UNIVERSAL::isa( $cb,'SCALAR' ) || UNIVERSAL::isa( $cb,'REF' )) {
488             # scalar ref, set to true
489 80 50       343 $$cb = @more_args ? shift(@more_args) : 1;
490 80         330 return $$cb;
491             } elsif ( $cb ) {
492 0         0 confess "unknown handler $cb";
493             }
494             }
495              
496             ###########################################################################
497             # split string into host/ip, port and detect family (IPv4 or IPv6)
498             # Args: $addr;$opaque
499             # $addr: ip_or_host, ipv4_or_host:port, [ip_or_host]:port
500             # $opaque: optional argument, if true it will not enforce valid syntax
501             # for the hostname and will not return canonicalized data
502             # Returns: (\%hash|$host,$port,$family)
503             # $host: canonicalized IP address or hostname
504             # $port: the port or undef if no port was given in string
505             # $family: AF_INET or AF_INET6 or undef (hostname not IP given)
506             # %hash: hash with addr, port, family - used if !wantarray
507             ###########################################################################
508             sub ip_string2parts {
509 412     412 1 1398 my ($addr,$opaque) = @_;
510 412         882 my ($host,$port,$family);
511 412 100       2690 if ($addr =~m{:[^:\s]*(:)?}) {
512 237 50       937 if (!$1) {
    0          
513             # (ipv4|host):port
514 237         1185 ($host,$port) = split(':',$addr,2);
515 237         766 $family = AF_INET;
516             } elsif ($addr =~m{^\[(?:(.*:.*)|([^:]*))\](?::(\w+))?\z}) {
517 0         0 $port = $3;
518 0 0       0 ($host,$family) = $1
519             ? ($1, AF_INET6) # [ipv6](:port)?
520             : ($2, AF_INET); # [ipv4|host](:port)?
521             } else {
522             # ipv6
523 0         0 ($host,$family) = ($addr, AF_INET6);
524             }
525             } else {
526             # ipv4|host
527 175         485 ($host,$family) = ($addr, AF_INET);
528             }
529              
530             # we now have:
531             # AF_INET6 if it contains a ':', i.e. either valid IPv6 or smthg invalid
532             # AF_INET otherwise, i.e. IPv4 or hostname or smthg invalid
533              
534             # check if this is an IP address from the expected family
535 412 100       2741 if ($addr = inet_pton($family,$host)) {
    100          
    50          
536             # valid IP address
537 359 50       2087 $addr = $opaque ? $host
538             : inet_ntop($family, $addr); # canonicalized form
539             } elsif ($opaque) {
540             # not a valid IP address - pass through because opaque
541 8         18 $family = $addr = undef;
542             } elsif ($host =~m{^[a-z\d\-\_]+(?:\.[a-z\d\-\_]+)*\.?\z}) {
543             # not a valid IP address but valid hostname
544 45         126 $family = $addr = undef;
545             } else {
546             # neither IP nor valid hostname
547 0         0 Carp::confess("invalid hostname '$host' in '$_[0]'");
548 0         0 die("invalid hostname '$host' in '$_[0]'");
549             }
550              
551             # make sure that it looks like a valid hostname and return it lower case
552 412 100       1551 $host = lc($host) if ! $opaque;
553 412 50       3148 return ($host,$port,$family) if wantarray;
554             return {
555 0         0 host => $host,
556             addr => $addr,
557             port => $port,
558             family => $family
559             };
560              
561             }
562              
563             ###########################################################################
564             # concat ip/host and port to string, i.e. reverse to ip_string2parts
565             # Args: ($host;$port,$family,$ipv6_brackets)
566             # $host: the IP address or hostname
567             # $port: optional port
568             # $family: optional, will be detected from $host if not given
569             # $ipv6_brackets: optional, results in [ipv6] if true and no port given
570             # alternative Args: (\%hash,$ipv6_brackets)
571             # %hash: hash containing addr|host, port and family
572             # if opt default_port is given will treat port as 0 if default
573             # if opt use_host is true will prefer host instead of addr
574             # Returns: $addr
575             # $addr: ip_or_host, ipv4_or_host:port, [ipv6]:port,
576             # [ipv6] (if ipv6_brackets)
577             ###########################################################################
578             sub ip_parts2string {
579 453     453 1 173791 my ($host,$port,$fam,$ipv6_brackets);
580 453 100       1749 if (ref($_[0])) {
581 226         587 (my $hash,$ipv6_brackets) = @_;
582 226         603 $port = $hash->{port};
583 226         486 $fam = $hash->{family};
584 226   33     833 $host = $hash->{addr} || $hash->{host};
585 226 50 66     3024 if (exists $hash->{use_host} && $hash->{use_host}
      33        
      33        
      33        
586             && $hash->{host} && $fam && $hash->{host} ne $hash->{addr}) {
587             # use host instead of addr and set family to undef in order to
588             # not put hostname in brackets
589 0         0 $host = $hash->{host};
590 0         0 $fam = undef;
591             }
592 226 100 100     1471 if (exists $hash->{default_port} && $port == $hash->{default_port}) {
593 6         19 $port = 0;
594             }
595             } else {
596 227         651 ($host,$port,$fam,$ipv6_brackets) = @_;
597             }
598 453         1453 $host = lc($host);
599 453 100 100     1500 return $host if ! $port && !$ipv6_brackets;
600 452   0     1309 $fam ||= $host =~m{:} && AF_INET6;
      33        
601              
602 452 50 33     2662 $host = "[$host]" if $fam && $fam != AF_INET;
603 452 100       1150 return $host if ! $port;
604 447         2607 return $host.':'.$port;
605             }
606              
607             ###########################################################################
608             # create sockaddr from IP, port (and family)
609             # Args: ($addr,$port;$family)
610             # $addr: the IP address
611             # $port: port
612             # $family: optional, will be detected from $ip if not given
613             # alternative Args: \%hash
614             # %hash: hash with addr, port, family
615             # Returns: $sockaddr
616             ###########################################################################
617             sub ip_parts2sockaddr {
618 207     207 1 449 my ($addr,$port,$fam);
619 207 100       558 if (ref($_[0])) {
620 121         319 $addr = $_[0]->{addr};
621 121         259 $port = $_[0]->{port};
622 121         267 $fam = $_[0]->{family};
623             } else {
624 86         220 ($addr,$port,$fam) = @_;
625             }
626 207 50 66     1006 $fam ||= $addr =~m{:} ? AF_INET6 : AF_INET;
627 207 50       523 if ($fam == AF_INET) {
628 207         17627 return pack_sockaddr_in($port,inet_pton(AF_INET,$addr))
629             } elsif (CAN_IPV6) {
630 0         0 return pack_sockaddr_in6($port,inet_pton(AF_INET6,$addr))
631             } else {
632             die "no IPv6 support"
633             }
634             }
635              
636             ###########################################################################
637             # create parts from sockaddr, i.e. reverse to ip_parts2sockaddr
638             # Args: $sockaddr;$family
639             # $sockaddr: sockaddr as returned by getsockname, recvfrom..
640             # $family: optional family, otherwise guessed based on size of sockaddr
641             # Returns: (\%hash | $ip,$port,$family)
642             # $ip: the IP address
643             # $port: port
644             # $family: AF_INET or AF_INET6
645             # %hash: hash with host, addr, port, family - if not wantarray
646             ###########################################################################
647             sub ip_sockaddr2parts {
648 371     371 1 1525 my ($sockaddr,$fam) = @_;
649 371 50 33     3158 $fam ||= length($sockaddr)>=24 ? AF_INET6 : AF_INET;
650 371 50 50     1596 die "no IPv6 support" if $fam != AF_INET && !CAN_IPV6;
651 371 50       2983 my ($port,$addr) = $fam == AF_INET
652             ? unpack_sockaddr_in($sockaddr)
653             : unpack_sockaddr_in6($sockaddr);
654 371         2805 $addr = inet_ntop($fam,$addr);
655 371 100       1407 return ($addr,$port,$fam) if wantarray;
656             return {
657 351         5206 host => $addr,
658             addr => $addr,
659             port => $port,
660             family => $fam,
661             };
662             }
663              
664             ###########################################################################
665             # gets string from sockaddr, i.e. like ip_parts2string(ip_sockaddr2parts(..))
666             # Args: $sockaddr;$family
667             # $sockaddr: sockaddr as returned by getsockname, recvfrom..
668             # $family: optional family, otherwise guessed based on size of sockaddr
669             # Returns: $string
670             ###########################################################################
671             sub ip_sockaddr2string {
672 0     0 1 0 my ($sockaddr,$fam) = @_;
673 0 0 0     0 $fam ||= length($sockaddr)>=24 ? AF_INET6 : AF_INET;
674 0 0       0 if ($fam == AF_INET) {
675 0         0 my ($port,$addr) = unpack_sockaddr_in($sockaddr);
676 0         0 return inet_ntop(AF_INET,$addr) . ":$port";
677             } else {
678 0         0 my ($port,$addr) = unpack_sockaddr_in6($sockaddr);
679 0         0 return '[' . inet_ntop(AF_INET6,$addr) . "]:$port";
680             }
681             }
682              
683             ###########################################################################
684             # return name for PTR lookup of given IP address
685             # Args: $ip;$family
686             # $ip: IP address
687             # $family: optional family
688             # Returns: $ptr_name
689             ###########################################################################
690             sub ip_ptr {
691 64     64 1 195 my ($ip,$family) = @_;
692 64 0 33     282 $family ||= $ip=~m{:} ? AF_INET6 : AF_INET;
693 64 50       211 if ($family == AF_INET) {
694 64         808 return join('.', reverse(unpack("C*",inet_pton(AF_INET,$ip))))
695             . '.in-addr.arpa';
696             } else {
697 0         0 return join('.', reverse(split('',
698             unpack("H*", inet_pton(AF_INET6,$ip)))))
699             . '.ip6.arpa';
700             }
701             }
702              
703             ###########################################################################
704             # convert IP address into canonical form suitable for comparison
705             # Args: $ip;$family
706             # $ip: IP address
707             # $family: optional family
708             # Returns: $ip_canonical
709             ###########################################################################
710             sub ip_canonical {
711 44     44 1 165 my ($ip,$family) = @_;
712 44 50 33     699 $family ||= $ip=~m{:} ? AF_INET6 : AF_INET;
713 44         487 return inet_ntop($family, inet_pton($family, $ip));
714             }
715              
716             ###########################################################################
717             # get IP addresses for hostname
718             # Args: ($name;$family)
719             # $name: hostname
720             # $family: optional family to restrict result to IPv4/IPv6
721             # Returns: @ip | $ip - i.e. list of IP or first of the list
722             ###########################################################################
723             sub hostname2ip {
724 0     0 1 0 my ($name,$family) = @_;
725 0 0 0     0 $family = AF_INET if ! $family && ! CAN_IPV6;
726 0 0       0 my ($err,@result) = getaddrinfo($name,undef,
727             $family ? ({ family => $family }):() );
728 0 0 0     0 return if $err || ! @result;
729 0 0       0 @result = $result[0] if ! wantarray;
730 0         0 ($_) = ip_sockaddr2parts($_->{addr},$_->{family}) for @result;
731 0 0       0 return wantarray ? @result : $result[0]
732             }
733              
734             ###########################################################################
735             # check if address is valid IPv4 or IPv6 address
736             # Args: $ip
737             # Returns: true|false
738             ###########################################################################
739 92     92 1 973 sub ip_is_v4 { inet_pton(AF_INET, $_[0]) }
740 0     0 1 0 sub ip_is_v6 { inet_pton(AF_INET6, $_[0]) }
741              
742             ###########################################################################
743             # check if address is valid IP address
744             # Args: $ip
745             # Returns: AF_INET|AF_INET6|undef
746             ###########################################################################
747             sub ip_is_v46 {
748             return
749 10 0   10 1 99 inet_pton(AF_INET, $_[0]) ? AF_INET :
    50          
750             inet_pton(AF_INET6, $_[0]) ? AF_INET6 :
751             undef;
752             }
753              
754             1;