Blame t/io_multihomed6.t

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
}