|
Packit |
a83d8b |
#!/usr/bin/env perl
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
use strict;
|
|
Packit |
a83d8b |
use warnings;
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
BEGIN {
|
|
Packit |
a83d8b |
unless(grep /blib/, @INC) {
|
|
Packit |
a83d8b |
chdir 't' if -d 't';
|
|
Packit |
a83d8b |
unshift @INC,'../lib';
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
use Config;
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
BEGIN {
|
|
Packit |
a83d8b |
if(-d "lib" && -f "TEST") {
|
|
Packit |
a83d8b |
my $reason;
|
|
Packit |
a83d8b |
if (! $Config{'d_fork'}) {
|
|
Packit |
a83d8b |
$reason = 'no fork';
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
elsif ($Config{'extensions'} !~ /\bSocket\b/) {
|
|
Packit |
a83d8b |
$reason = 'Socket extension unavailable';
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
elsif ($Config{'extensions'} !~ /\bSocket6\b/) {
|
|
Packit |
a83d8b |
$reason = 'Socket6 extension unavailable';
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
elsif ($Config{'extensions'} !~ /\bIO\b/) {
|
|
Packit |
a83d8b |
$reason = 'IO extension unavailable';
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
if ($reason) {
|
|
Packit |
a83d8b |
print "1..0 # SKIP $reason\n";
|
|
Packit |
a83d8b |
exit 0;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
if ($^O eq 'MSWin32') {
|
|
Packit |
a83d8b |
print "1..0 # SKIP accept() fails for IPv6 under MSWin32\n";
|
|
Packit |
a83d8b |
exit 0;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# check that localhost resolves to 127.0.0.1 and ::1
|
|
Packit |
a83d8b |
# otherwise the test will not work
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
use Socket (qw(
|
|
Packit |
a83d8b |
AF_INET6 PF_INET6 SOCK_RAW SOCK_STREAM INADDR_ANY SOCK_DGRAM
|
|
Packit |
a83d8b |
AF_INET SO_REUSEADDR SO_REUSEPORT AF_UNSPEC SO_BROADCAST
|
|
Packit |
a83d8b |
sockaddr_in unpack_sockaddr_in
|
|
Packit |
a83d8b |
)
|
|
Packit |
a83d8b |
);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# IO::Socket and Socket already import stuff here - possibly AF_INET6
|
|
Packit |
a83d8b |
# and PF_INET6 so selectively import things from Socket6.
|
|
Packit |
a83d8b |
use Socket6 (
|
|
Packit |
a83d8b |
qw(AI_PASSIVE getaddrinfo
|
|
Packit |
a83d8b |
sockaddr_in6 unpack_sockaddr_in6 pack_sockaddr_in6_all in6addr_any
|
|
Packit |
a83d8b |
inet_ntop
|
|
Packit |
a83d8b |
)
|
|
Packit |
a83d8b |
);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
{
|
|
Packit |
a83d8b |
my %resolved_addresses;
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
my @r = getaddrinfo('localhost',1);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
if (@r < 5) {
|
|
Packit |
a83d8b |
print "1..0 # SKIP getaddrinfo('localhost',1) failed: $r[0]\n";
|
|
Packit |
a83d8b |
exit 0;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
while (@r) {
|
|
Packit |
a83d8b |
my @values = splice(@r,0,5);
|
|
Packit |
a83d8b |
my ($fam,$addr) = @values[0,3];
|
|
Packit |
a83d8b |
$addr =
|
|
Packit |
a83d8b |
(
|
|
Packit |
a83d8b |
($fam == AF_INET)
|
|
Packit |
a83d8b |
? ( (unpack_sockaddr_in($addr))[1] )
|
|
Packit |
a83d8b |
: ( (unpack_sockaddr_in6($addr))[1] )
|
|
Packit |
a83d8b |
);
|
|
Packit |
a83d8b |
$resolved_addresses{inet_ntop($fam,$addr)}++;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
if (! $resolved_addresses{'127.0.0.1'} || ! $resolved_addresses{'::1'}) {
|
|
Packit |
a83d8b |
print "1..0 # SKIP localhost does not resolve to both 127.0.0.1 and ::1\n";
|
|
Packit |
a83d8b |
exit 0;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# IO::Socket has an import method that is inherited by IO::Socket::INET6 ,
|
|
Packit |
a83d8b |
# and so we should instruct it not to import anything.
|
|
Packit |
a83d8b |
use IO::Socket::INET6 ();
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
$| = 1;
|
|
Packit |
a83d8b |
print "1..8\n";
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
eval {
|
|
Packit |
a83d8b |
$SIG{ALRM} = sub { die; };
|
|
Packit |
a83d8b |
alarm 60;
|
|
Packit |
a83d8b |
};
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# find out if the host prefers inet or inet6 by offering
|
|
Packit |
a83d8b |
# both and checking where it connects
|
|
Packit |
a83d8b |
my ($port,@srv);
|
|
Packit |
a83d8b |
for my $addr ( '127.0.0.1','::1' ) {
|
|
Packit |
a83d8b |
push @srv,
|
|
Packit |
a83d8b |
IO::Socket::INET6->new(
|
|
Packit |
a83d8b |
Listen => 2,
|
|
Packit |
a83d8b |
LocalAddr => $addr,
|
|
Packit |
a83d8b |
LocalPort => $port,
|
|
Packit |
a83d8b |
) or die "listen on $addr port $port: $!";
|
|
Packit |
a83d8b |
$port ||= $srv[-1]->sockport;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
print "ok 1\n";
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
if (my $pid = fork()) {
|
|
Packit |
a83d8b |
my $vec = '';
|
|
Packit |
a83d8b |
vec($vec,fileno($_),1) = 1 for(@srv);
|
|
Packit |
a83d8b |
select($vec,undef,undef,5) or die $!;
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# connected to first, not second
|
|
Packit |
a83d8b |
my ($first,$second) = vec($vec,fileno($srv[0]),1) ? @srv[0,1]:@srv[1,0];
|
|
Packit |
a83d8b |
my $cl = $first->accept or die $!;
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# listener should not work for next connect
|
|
Packit |
a83d8b |
# so it needs to try second
|
|
Packit |
a83d8b |
close($first);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# make sure established connection works
|
|
Packit |
a83d8b |
my $fam0 = ( $cl->sockdomain == AF_INET ) ? 'inet':'inet6';
|
|
Packit |
a83d8b |
print {$cl} "ok 2 # $fam0\n";
|
|
Packit |
a83d8b |
print $cl->getline(); # ok 3
|
|
Packit |
a83d8b |
# So we'll be sure ok 3 has already been printed.
|
|
Packit |
a83d8b |
print {$cl} "Move on, will ya!\n";
|
|
Packit |
a83d8b |
close($cl);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# ... ok 4 comes when client fails to connect to first
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# wait for connect on second and make sure it works
|
|
Packit |
a83d8b |
$vec = '';
|
|
Packit |
a83d8b |
vec($vec,fileno($second),1) = 1;
|
|
Packit |
a83d8b |
if ( select($vec,undef,undef,5)) {
|
|
Packit |
a83d8b |
my $cl2 = $second->accept or die $!;
|
|
Packit |
a83d8b |
my $fam1 = ( $cl2->sockdomain == AF_INET ) ? 'inet':'inet6';
|
|
Packit |
a83d8b |
print {$cl2} "ok 5 # $fam1\n";
|
|
Packit |
a83d8b |
print $cl2->getline(); # ok 6
|
|
Packit |
a83d8b |
close($cl2);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# should be different families
|
|
Packit |
a83d8b |
print "not " if $fam0 eq $fam1;
|
|
Packit |
a83d8b |
print "ok 7\n";
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
waitpid($pid,0);
|
|
Packit |
a83d8b |
print "ok 8\n";
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
} elsif (defined $pid) {
|
|
Packit |
a83d8b |
close($_) for (@srv);
|
|
Packit |
a83d8b |
# should work because server is listening on inet and inet6
|
|
Packit |
a83d8b |
my $cl = IO::Socket::INET6->new(
|
|
Packit |
a83d8b |
PeerPort => $port,
|
|
Packit |
a83d8b |
PeerAddr => 'localhost',
|
|
Packit |
a83d8b |
Timeout => 5,
|
|
Packit |
a83d8b |
) or die "$@";
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
print $cl->getline(); # ok 2
|
|
Packit |
a83d8b |
print {$cl} "ok 3\n";
|
|
Packit |
a83d8b |
# So we'll be sure ok 3 has already been printed.
|
|
Packit |
a83d8b |
$cl->getline();
|
|
Packit |
a83d8b |
close($cl);
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# this should not work because listener is closed
|
|
Packit |
a83d8b |
if ( $cl = IO::Socket::INET6->new(
|
|
Packit |
a83d8b |
PeerPort => $port,
|
|
Packit |
a83d8b |
PeerAddr => 'localhost',
|
|
Packit |
a83d8b |
Timeout => 5,
|
|
Packit |
a83d8b |
)) {
|
|
Packit |
a83d8b |
print "not ok 4\n";
|
|
Packit |
a83d8b |
exit;
|
|
Packit |
a83d8b |
}
|
|
Packit |
a83d8b |
print "ok 4\n";
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
# but same thing with multihoming should work because server
|
|
Packit |
a83d8b |
# is still listening on the other family
|
|
Packit |
a83d8b |
$cl = IO::Socket::INET6->new(
|
|
Packit |
a83d8b |
PeerPort => $port,
|
|
Packit |
a83d8b |
PeerAddr => 'localhost',
|
|
Packit |
a83d8b |
Timeout => 5,
|
|
Packit |
a83d8b |
MultiHomed => 1,
|
|
Packit |
a83d8b |
) or do {
|
|
Packit |
a83d8b |
print "not ok 5\n";
|
|
Packit |
a83d8b |
exit;
|
|
Packit |
a83d8b |
};
|
|
Packit |
a83d8b |
print $cl->getline(); # ok 5
|
|
Packit |
a83d8b |
print {$cl} "ok 6\n";
|
|
Packit |
a83d8b |
exit;
|
|
Packit |
a83d8b |
|
|
Packit |
a83d8b |
} else {
|
|
Packit |
a83d8b |
die $!; # fork failed
|
|
Packit |
a83d8b |
}
|