From 9f65081cf74b402e3e136e787e7efcb5bd32d2b4 Mon Sep 17 00:00:00 2001 From: Noah Misch Date: Sun, 14 Apr 2019 20:02:19 -0700 Subject: [PATCH] Test both 0.0.0.0 and 127.0.0.x addresses to find a usable port. Commit c098509927f9a49ebceb301a2cb6a477ecd4ac3c 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 --- src/test/perl/PostgresNode.pm | 62 +++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm index fd2a67c8c9e..0df8c0aa45f 100644 --- a/src/test/perl/PostgresNode.pm +++ b/src/test/perl/PostgresNode.pm @@ -967,27 +967,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; + } } } @@ -999,8 +1002,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; @@ -1024,6 +1025,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; +} + # Automatically shut down any still-running nodes when the test script exits. # Note that this just stops the postmasters (in the same order the nodes were # created in). Temporary PGDATA directories are deleted, in an unspecified