File Coverage

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