=cut
sub parse_ipv6($) {
# quick test to avoid longer processing
my $n = $_[0] =~ y/://;
return undef if $n < 2 || $n > 8;
my ($h, $t) = split /::/, $_[0], 2;
unless (defined $t) {
($h, $t) = (undef, $h);
}
my @h = split /:/, $h;
my @t = split /:/, $t;
# check for ipv4 tail
if (@t && $t[-1]=~ /\./) {
return undef if $n > 6;
my $ipn = parse_ipv4 pop @t
or return undef;
push @t, map +(sprintf "%x", $_), unpack "nn", $ipn;
}
# no :: then we need to have exactly 8 components
return undef unless @h + @t == 8 || $_[0] =~ /::/;
# now check all parts for validity
return undef if grep !/^[0-9a-fA-F]{1,4}$/, @h, @t;
# now pad...
push @h, 0 while @h + @t < 8;
# and done
pack "n*", map hex, @h, @t
}
sub parse_unix($) {
$_[0] eq "unix/"
? pack "S", AF_UNIX
: undef
}
=item $ipn = parse_address $text
Combines C<parse_ipv4> and C<parse_ipv6> in one function. The address
here refers to the host address (not socket address) in network form
(binary).
If the C<$text> is C<unix/>, then this function returns a special token
recognised by the other functions in this module to mean "UNIX domain
socket".
=cut
sub parse_address($) {
&parse_ipv4 || &parse_ipv6 || &parse_unix
}
*parse_ip =\&parse_address; #d#
=item $sa_family = address_family $ipn
Returns the address family/protocol-family (AF_xxx/PF_xxx, in one value :)
of the given host address in network format.
=cut
sub address_family($) {
4 == length $_[0]
? AF_INET
: 16 == length $_[0]
? AF_INET6
: unpack "S", $_[0]
}
=item $text = format_address $ipn
Covnvert a host address in network format (e.g. 4 octets for IPv4 or 16
octets for IPv6) and convert it into textual form.
Returns C<unix/> for UNIX domain sockets.
This function works similarly to C<inet_ntop AF_INET || AF_INET6, ...>,
except it automatically detects the address type.
Returns C<undef> if it cannot detect the type.
=cut
sub format_address;
sub format_address($) {
my $af = address_family $_[0];
if ($af == AF_INET) {
return join ".", unpack "C4", $_[0]
} elsif ($af == AF_INET6) {
=2= |