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   252 use strict;
  44         83  
  44         1160  
8 44     44   178 use warnings;
  44         70  
  44         1405  
9              
10             package Net::SIP::Util;
11              
12 44     44   230 use Digest::MD5 'md5_hex';
  44         58  
  44         2073  
13 44         8504 use Socket 1.95 qw(
14             inet_ntop inet_pton
15             AF_INET unpack_sockaddr_in pack_sockaddr_in
16             getaddrinfo
17 44     44   652 );
  44         3685  
18 44     44   271 use Net::SIP::Debug;
  44         70  
  44         235  
19 44     44   291 use Carp qw(confess croak);
  44         78  
  44         2108  
20 44     44   228 use base 'Exporter';
  44         84  
  44         14960  
21              
22             BEGIN {
23 44     44   144 my $mod6 = '';
24 44 50       68 if (eval {
    0          
25 44         22633 require IO::Socket::IP;
26 44         1186569 IO::Socket::IP->VERSION(0.31);
27 44         1430 Socket->import('AF_INET6');
28 44         275 AF_INET6();
29             }) {
30 44         127 $mod6 = 'IO::Socket::IP';
31             *INETSOCK = sub {
32 432 50   432   42108198 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         3878 my %args = @_;
38             $args{GetAddrInfoFlags} = 0 if ! defined $args{GetAddrInfoFlags}
39 432 100 66     4680 and $args{Domain} || $args{Family};
      66        
40 432         6878 return IO::Socket::IP->new(%args);
41 44         243 };
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   273 no warnings 'redefine';
  44         72  
  44         5256  
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       217 *CAN_IPV6 = $mod6 ? sub() { 1 } : sub() { 0 };
68 44 50       143337 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 2205 croak( "usage: sip_hdrval2parts( key => val )" ) if @_!=2;
109 769         2251 my ($key,$v) = @_;
110 769 50       2192 return if !defined($v);
111 769   100     4009 my $delim = $delimiter{$key} || ';';
112              
113             # split on delimiter (but not if quoted)
114 769         3257 my @v = ('');
115 769         1186 my $quoted = 0;
116 769         2216 my $bracket = 0;
117 769         1012 while (1) {
118 1325 100       11491 if ( $v =~m{\G(.*?)([\\"<>$delim])}gc ) {
119 556 100       3687 if ( $2 eq "\\" ) {
    100          
    100          
    100          
    50          
120 6         66 $v[-1].=$1.$2.substr( $v,pos($v),1 );
121 6         22 pos($v)++;
122             } elsif ( $2 eq '"' ) {
123 62         113 $v[-1].=$1.$2;
124 62 50       118 $quoted = !$quoted if ! $bracket;
125             } elsif ( $2 eq '<' ) {
126 29         96 $v[-1].=$1.$2;
127 29 50 33     220 $bracket = 1 if ! $bracket && ! $quoted;
128             } elsif ( $2 eq '>' ) {
129 29         76 $v[-1].=$1.$2;
130 29 50 33     185 $bracket = 0 if $bracket && ! $quoted;
131             } elsif ( $2 eq $delim ) {
132             # next item if not quoted
133 430 100 66     2519 if ( ! $quoted && ! $bracket ) {
134 393         2593 ( $v[-1].=$1 ) =~s{\s+$}{}; # strip trailing space
135 393         1085 push @v,'' ;
136 393         1423 $v =~m{\G\s+}gc; # skip space after $delim
137             } else {
138 37         123 $v[-1].=$1.$2
139             }
140             }
141             } else {
142             # add rest to last from @v
143 769   100     6366 $v[-1].= substr($v,pos($v)||0 );
144 769         1640 last;
145             }
146             }
147              
148             # with delimiter ',' it starts 'Digest realm=...' so $v[0]
149             # contains method and first parameter
150 769         1435 my $data = shift(@v);
151 769 100       2000 if ( $delim eq ',' ) {
152 8         34 $data =~s{^(\S+)\s*(.*)}{$1};
153 8         18 unshift @v,$2;
154             }
155             # rest will be interpreted as parameters with key|key=value
156 769         5025 my %hash;
157 769         2055 foreach my $vv (@v) {
158 401         2189 my ($key,$value) = split( m{\s*=\s*},$vv,2 );
159 401 100       1017 if ( defined($value) ) {
160 390         1228 $value =~s{^"(.*)"$}{$1}; # unquote
161             # TODO Q: what's the meaning of "\%04", e.g. is it
162             # '%04' or "\\\004" ??
163 390         690 $value =~s{\\(.)}{$1}sg; # unescape backslashes
164 390         662 $value =~s{%([a-fA-F][a-fA-F])}{ chr(hex($1)) }esg; # resolve uri encoding
  0         0  
165             }
166 401         1812 $hash{lc($key)} = $value;
167             }
168 769         3789 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 1004 my ($key,$data,$param) = @_;
183              
184 154   50     1855 my $delim = $delimiter{$key} || ';';
185              
186 154         488 my $val = $data; # FIXME: need to escape $data?
187 154         1054 for my $k ( sort keys %$param ) {
188 69         599 $val .= $delim.$k;
189 69         734 my $v = $param->{$k};
190 69 50       660 if ( defined $v ) {
191             # escape special chars
192 69         1414 $v =~s{([%\r\n\t"[:^print:]])}{ sprintf "%%%02x",ord($1) }esg;
  0         0  
193 69 50       1789 $v = '"'.$v.'"' if $v =~m{\s|$delim};
194 69         505 $val .= '='.$v
195             }
196             }
197 154         980 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 691 my $uri = shift;
213 314 100       22034 $uri = $1 if $uri =~m{<([^>]+)>\s*$};
214 314         1387 my ($data,$param) = sip_hdrval2parts( uri => $uri );
215 314 50       2761 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         2003 my ($proto,$user,$domain) = ($1,$2,$3);
225 314         840 $domain = lc($domain);
226 314   100     1712 $proto ||= 'sip';
227             return wantarray
228 314 50       2442 ? ($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 141935 my ($domain,$user,$proto,$param) = @_;
247 133 100 50     2444 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     2608 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 85611 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     1712 $param->{transport} ? lc($param->{transport}) : # transport -> tcp|udp
    100          
275             undef; # not restricted
276 178         998 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 17 my ($uri1,$uri2) = @_;
311 9 100       18 return 1 if $uri1 eq $uri2; # shortcut for common case
312 8         16 my ($d1,$u1,$p1) = sip_uri2parts($uri1);
313 8         20 my ($d2,$u2,$p2) = sip_uri2parts($uri2);
314 8 50 33     51 my $port1 = $d1 =~s{:(\d+)$|\[(\d+)\]$}{} ? $1||$2
    100          
315             : $p1 eq 'sips' ? 5061 : 5060;
316 8 50 33     29 my $port2 = $d2 =~s{:(\d+)$|\[(\d+)\]$}{} ? $1||$2
    100          
317             : $p2 eq 'sips' ? 5061 : 5060;
318 8   33     85 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         184 my ($addr, $port, $fam) = ip_string2parts($dst);
339 5 50       23 $fam or return; # no IP destination
340 5 50       28 for my $src (@src ? @src : (undef)) {
341 5 50 100     68 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         4154 my @parts = ip_sockaddr2parts(getsockname($sock));
349 5 50       121 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 11486 my ($dst_addr,$proto) = @_;
371 5   50     76 $proto ||= 'udp';
372              
373 5         33 my ($laddr,$fam) = laddr4dst($dst_addr);
374 5         39 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       34 for my $p ( $proto eq 'tls' ? 5061:5060, 5062..5100, 0 ) {
380 8 50       1015 $DEBUG && DEBUG( "try to listen on %s",
381             ip_parts2string($laddr,$p,$fam));
382 8 50       34 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     1786 my $port = $p || (ip_sockaddr2parts(getsockname($sock)))[1];
390 5 50       15 $DEBUG && DEBUG("listen on %s",ip_parts2string($laddr,$port,$fam));
391 5 100       92 return $sock if ! wantarray;
392 3         9 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 309 my ($laddr,$range,$min,$max,$tries) = @_;
414 52   100     440 $range ||= 2;
415 52 50       157 if ( ! $min ) {
416 52         98 $min = $RTP_MIN_PORT;
417 52   33     379 $max ||= $RTP_MAX_PORT;
418             } else {
419 0   0     0 $max ||= $min+10000;
420             }
421 52         147 $min += $min%2; # make even
422 52   50     368 $tries ||= 1000;
423              
424 52         175 my $diff2 = int(($max-$min)/2) - $range +1;
425              
426 52         6549 my (@socks,$port);
427 52         158 my $fam = (ip_string2parts($laddr))[2];
428 52         191 while ( $tries-- >0 ) {
429              
430 104 100       28480 last if @socks == $range;
431 52         267 close $_ for @socks;
432 52         88 @socks = ();
433              
434 52         1047 $port = 2*int(rand($diff2)) + $min;
435 52         247 for( my $i=0;$i<$range;$i++ ) {
436 104   50     33599 push @socks, INETSOCK(
437             Family => $fam,
438             Proto => 'udp',
439             LocalAddr => $laddr,
440             LocalPort => $port + $i,
441             ) || last;
442             }
443             }
444 52 50       223 return if @socks != $range; # failed
445 52         344 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 14088     14088 1 44502 my ($cb,@more_args) = @_;
465 14088 100 66     91509 if ( UNIVERSAL::isa( $cb,'CODE' )) {
    100          
    100          
    100          
    100          
    50          
466             # anon sub
467 3953         18473 return $cb->(@more_args)
468             } elsif ( my $sub = UNIVERSAL::can( $cb,'run' )) {
469             # Callback object
470 4         26 return $sub->($cb,@more_args );
471             } elsif ( UNIVERSAL::isa( $cb,'ARRAY' )) {
472 9954         35130 my ($sub,@args) = @$cb;
473             # [ \&sub,@arg ]
474 9954         45523 return $sub->( @args,@more_args );
475             } elsif ( UNIVERSAL::isa( $cb,'Regexp' )) {
476 2 50       7 @more_args or return;
477 2         4 for(@more_args) {
478 2 50       37 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       257 $$cb = @more_args ? shift(@more_args) : 1;
484 82         250 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 429     429 1 1350 my ($addr,$opaque) = @_;
504 429         854 my ($host,$port,$family);
505 429 100       2549 if ($addr =~m{:[^:\s]*(:)?}) {
506 246 50       920 if (!$1) {
    0          
507             # (ipv4|host):port
508 246         1103 ($host,$port) = split(':',$addr,2);
509 246         728 $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         442 ($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 429 100       2432 if ($addr = inet_pton($family,$host)) {
    100          
    50          
530             # valid IP address
531 376 50       1997 $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         110 $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 429 100       1496 $host = lc($host) if ! $opaque;
547 429 50       3050 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 180149 my ($host,$port,$fam,$ipv6_brackets);
574 463 100       1615 if (ref($_[0])) {
575 232         614 (my $hash,$ipv6_brackets) = @_;
576 232         485 $port = $hash->{port};
577 232         632 $fam = $hash->{family};
578 232   33     648 $host = $hash->{addr} || $hash->{host};
579 232 50 66     2552 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     1641 if (exists $hash->{default_port} && $port == $hash->{default_port}) {
587 6         12 $port = 0;
588             }
589             } else {
590 231         635 ($host,$port,$fam,$ipv6_brackets) = @_;
591             }
592 463         1137 $host = lc($host);
593 463 100 100     2182 return $host if ! $port && !$ipv6_brackets;
594 462   0     1483 $fam ||= $host =~m{:} && AF_INET6;
      33        
595              
596 462 50 33     2415 $host = "[$host]" if $fam && $fam != AF_INET;
597 462 100       955 return $host if ! $port;
598 457         2409 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 221     221 1 385 my ($addr,$port,$fam);
613 221 100       529 if (ref($_[0])) {
614 127         272 $addr = $_[0]->{addr};
615 127         258 $port = $_[0]->{port};
616 127         449 $fam = $_[0]->{family};
617             } else {
618 94         193 ($addr,$port,$fam) = @_;
619             }
620 221 50 66     1179 $fam ||= $addr =~m{:} ? AF_INET6 : AF_INET;
621 221 50       486 if ($fam == AF_INET) {
622 221         203424 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 1818 my ($sockaddr,$fam) = @_;
643 378 50 33     3398 $fam ||= length($sockaddr)>=24 ? AF_INET6 : AF_INET;
644 378 50 50     1599 die "no IPv6 support" if $fam != AF_INET && !CAN_IPV6;
645 378 50       2573 my ($port,$addr) = $fam == AF_INET
646             ? unpack_sockaddr_in($sockaddr)
647             : unpack_sockaddr_in6($sockaddr);
648 378         2463 $addr = inet_ntop($fam,$addr);
649 378 100       1584 return ($addr,$port,$fam) if wantarray;
650             return {
651 358         5170 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 187 my ($ip,$family) = @_;
686 68 0 33     305 $family ||= $ip=~m{:} ? AF_INET6 : AF_INET;
687 68 50       206 if ($family == AF_INET) {
688 68         677 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 165 my ($ip,$family) = @_;
706 48 50 33     694 $family ||= $ip=~m{:} ? AF_INET6 : AF_INET;
707 48         393 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 1031 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 66 inet_pton(AF_INET, $_[0]) ? AF_INET :
    50          
744             inet_pton(AF_INET6, $_[0]) ? AF_INET6 :
745             undef;
746             }
747              
748             1;