Test both 0.0.0.0 and 127.0.0.x addresses to find a usable port.

Commit c098509927 changed
PostgresNode::get_new_node() to probe 0.0.0.0 instead of 127.0.0.1, but
the new test was less effective for Windows native Perl.  This increased
the failure rate of buildfarm members bowerbird and jacana.  Instead,
test 0.0.0.0 and concrete addresses.  This restores the old level of
defense, but the algorithm is still subject to its longstanding time of
check to time of use race condition.  Back-patch to 9.6, like the
previous change.

Discussion: https://postgr.es/m/GrdLgAdUK9FdyZg8VIcTDKVOkys122ZINEb3CjjoySfGj2KyPiMKTh1zqtRp0TAD7FJ27G-OBB3eplxIB5GhcQH5o8zzGZfp0MuJaXJxVxk=@yesql.se
This commit is contained in:
Noah Misch 2019-04-14 20:02:19 -07:00
parent d9f543e9e9
commit 4ab02e8156
1 changed files with 41 additions and 21 deletions

View File

@ -1088,27 +1088,30 @@ sub get_new_node
$found = 0 if ($node->port == $port);
}
# Check to see if anything else is listening on this TCP port. Accept
# only ports available for all possible listen_addresses values, so
# the caller can harness this port for the widest range of purposes.
# This is *necessary* on Windows, and seems like a good idea on Unixen
# as well, even though we don't ask the postmaster to open a TCP port
# on Unix.
# Check to see if anything else is listening on this TCP port. This
# is *necessary* on $use_tcp (Windows) configurations. Seek a port
# available for all possible listen_addresses values, for own_host
# nodes and so the caller can harness this port for the widest range
# of purposes. The 0.0.0.0 test achieves that for post-2006 Cygwin,
# which automatically sets SO_EXCLUSIVEADDRUSE. The same holds for
# MSYS (a Cygwin fork). Testing 0.0.0.0 is insufficient for Windows
# native Perl (https://stackoverflow.com/a/14388707), so we also test
# individual addresses.
#
# This seems like a good idea on Unixen as well, even though we don't
# ask the postmaster to open a TCP port on Unix. On Non-Linux,
# non-Windows kernels, binding to 127.0.0.1/24 addresses other than
# 127.0.0.1 fails with EADDRNOTAVAIL.
#
# XXX A port available now may become unavailable by the time we start
# the postmaster.
if ($found == 1)
{
my $iaddr = inet_aton('0.0.0.0');
my $paddr = sockaddr_in($port, $iaddr);
my $proto = getprotobyname("tcp");
socket(SOCK, PF_INET, SOCK_STREAM, $proto)
or die "socket failed: $!";
# As in postmaster, don't use SO_REUSEADDR on Windows
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
unless $TestLib::windows_os;
(bind(SOCK, $paddr) && listen(SOCK, SOMAXCONN))
or $found = 0;
close(SOCK);
foreach my $addr (qw(127.0.0.1 0.0.0.0),
$use_tcp ? qw(127.0.0.2 127.0.0.3) : ())
{
can_bind($addr, $port) or $found = 0;
}
}
}
@ -1120,8 +1123,6 @@ sub get_new_node
{
if ($use_tcp)
{
# This assumes $use_tcp platforms treat every address in
# 127.0.0.1/24, not just 127.0.0.1, as a usable loopback.
$last_host_assigned++;
$last_host_assigned > 254 and BAIL_OUT("too many own_host nodes");
$host = '127.0.0.' . $last_host_assigned;
@ -1145,6 +1146,25 @@ sub get_new_node
return $node;
}
# Internal routine to check whether a host:port is available to bind
sub can_bind
{
my ($host, $port) = @_;
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);
my $proto = getprotobyname("tcp");
socket(SOCK, PF_INET, SOCK_STREAM, $proto)
or die "socket failed: $!";
# As in postmaster, don't use SO_REUSEADDR on Windows
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
unless $TestLib::windows_os;
my $ret = bind(SOCK, $paddr) && listen(SOCK, SOMAXCONN);
close(SOCK);
return $ret;
}
# Retain the errno on die() if set, else assume a generic errno of 1.
# This will instruct the END handler on how to handle artifacts left
# behind from tests.