[lldb/unittests] Skip IPv6 test on systems which don't have IPv6 configured

Sadly IPv6 is still not present anywhere. The test was attempting to
detect&skip such hosts, but the way it did that (essentially, by calling
getaddrinfo) meant that it only detected hosts which have IPv6 support
completely compiled out. It did not do anything about hosts which have
it compiled in, but lack runtime configuration even for the ::1 loopback
address.

This patch changes the detection logic to use a new method. It does it
by attempting to bind a socket to the appropriate loopback address. That
should ensure the hosts loopback interface is fully set up. In an effort
to avoid silently skipping the test on too many hosts, the test is
fairly strict about the kind of error it expects in these cases -- it
will only skip the test when receiving EADDRNOTAVAIL. If we find other
error codes that can be reasonably returned in these situations, we can
add more of them.

The (small) change in TCPSocket.cpp is to ensure that the code correctly
propagates the error received from the OS.
This commit is contained in:
Pavel Labath 2020-04-27 17:15:36 +02:00
parent f5b1301ce8
commit 18e96a31fe
5 changed files with 61 additions and 32 deletions

View File

@ -42,6 +42,16 @@ typedef const void *set_socket_option_arg_type;
using namespace lldb; using namespace lldb;
using namespace lldb_private; using namespace lldb_private;
static Status GetLastSocketError() {
std::error_code EC;
#ifdef _WIN32
EC = llvm::mapWindowsError(WSAGetLastError());
#else
EC = std::error_code(errno, std::generic_category());
#endif
return EC;
}
namespace { namespace {
const int kType = SOCK_STREAM; const int kType = SOCK_STREAM;
} }
@ -192,10 +202,8 @@ Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
for (SocketAddress &address : addresses) { for (SocketAddress &address : addresses) {
int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP, int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP,
m_child_processes_inherit, error); m_child_processes_inherit, error);
if (error.Fail()) { if (error.Fail())
error.Clear();
continue; continue;
}
// enable local address reuse // enable local address reuse
int option_value = 1; int option_value = 1;
@ -216,6 +224,7 @@ Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
err = ::listen(fd, backlog); err = ::listen(fd, backlog);
if (-1 == err) { if (-1 == err) {
error = GetLastSocketError();
CLOSE_SOCKET(fd); CLOSE_SOCKET(fd);
continue; continue;
} }
@ -228,9 +237,11 @@ Status TCPSocket::Listen(llvm::StringRef name, int backlog) {
m_listen_sockets[fd] = address; m_listen_sockets[fd] = address;
} }
if (m_listen_sockets.size() == 0) if (m_listen_sockets.size() == 0) {
error.SetErrorString("Failed to connect port"); assert(error.Fail());
return error; return error;
}
return Status();
} }
void TCPSocket::CloseListenSockets() { void TCPSocket::CloseListenSockets() {

View File

@ -22,11 +22,6 @@ public:
void TestGetURI(std::string ip) { void TestGetURI(std::string ip) {
std::unique_ptr<TCPSocket> socket_a_up; std::unique_ptr<TCPSocket> socket_a_up;
std::unique_ptr<TCPSocket> socket_b_up; std::unique_ptr<TCPSocket> socket_b_up;
if (!IsAddressFamilySupported(ip)) {
GTEST_LOG_(WARNING) << "Skipping test due to missing IPv"
<< (IsIPv4(ip) ? "4" : "6") << " support.";
return;
}
CreateTCPConnectedSockets(ip, &socket_a_up, &socket_b_up); CreateTCPConnectedSockets(ip, &socket_a_up, &socket_b_up);
auto socket = socket_a_up.release(); auto socket = socket_a_up.release();
ConnectionFileDescriptor connection_file_descriptor(socket); ConnectionFileDescriptor connection_file_descriptor(socket);
@ -42,6 +37,14 @@ public:
} }
}; };
TEST_F(ConnectionFileDescriptorTest, TCPGetURIv4) { TestGetURI("127.0.0.1"); } TEST_F(ConnectionFileDescriptorTest, TCPGetURIv4) {
if (!HostSupportsIPv4())
return;
TestGetURI("127.0.0.1");
}
TEST_F(ConnectionFileDescriptorTest, TCPGetURIv6) { TestGetURI("::1"); } TEST_F(ConnectionFileDescriptorTest, TCPGetURIv6) {
if (!HostSupportsIPv6())
return;
TestGetURI("::1");
}

View File

@ -111,10 +111,8 @@ TEST_F(SocketTest, TCPListen0ConnectAccept) {
TEST_F(SocketTest, TCPGetAddress) { TEST_F(SocketTest, TCPGetAddress) {
std::unique_ptr<TCPSocket> socket_a_up; std::unique_ptr<TCPSocket> socket_a_up;
std::unique_ptr<TCPSocket> socket_b_up; std::unique_ptr<TCPSocket> socket_b_up;
if (!IsAddressFamilySupported("127.0.0.1")) { if (!HostSupportsIPv4())
GTEST_LOG_(WARNING) << "Skipping test due to missing IPv4 support.";
return; return;
}
CreateTCPConnectedSockets("127.0.0.1", &socket_a_up, &socket_b_up); CreateTCPConnectedSockets("127.0.0.1", &socket_a_up, &socket_b_up);
EXPECT_EQ(socket_a_up->GetLocalPortNumber(), EXPECT_EQ(socket_a_up->GetLocalPortNumber(),
@ -148,10 +146,8 @@ TEST_F(SocketTest, TCPListen0GetPort) {
TEST_F(SocketTest, TCPGetConnectURI) { TEST_F(SocketTest, TCPGetConnectURI) {
std::unique_ptr<TCPSocket> socket_a_up; std::unique_ptr<TCPSocket> socket_a_up;
std::unique_ptr<TCPSocket> socket_b_up; std::unique_ptr<TCPSocket> socket_b_up;
if (!IsAddressFamilySupported("127.0.0.1")) { if (!HostSupportsIPv4())
GTEST_LOG_(WARNING) << "Skipping test due to missing IPv4 support.";
return; return;
}
CreateTCPConnectedSockets("127.0.0.1", &socket_a_up, &socket_b_up); CreateTCPConnectedSockets("127.0.0.1", &socket_a_up, &socket_b_up);
llvm::StringRef scheme; llvm::StringRef scheme;
@ -165,10 +161,8 @@ TEST_F(SocketTest, TCPGetConnectURI) {
} }
TEST_F(SocketTest, UDPGetConnectURI) { TEST_F(SocketTest, UDPGetConnectURI) {
if (!IsAddressFamilySupported("127.0.0.1")) { if (!HostSupportsIPv4())
GTEST_LOG_(WARNING) << "Skipping test due to missing IPv4 support.";
return; return;
}
llvm::Expected<std::unique_ptr<UDPSocket>> socket = llvm::Expected<std::unique_ptr<UDPSocket>> socket =
UDPSocket::Connect("127.0.0.1:0", /*child_processes_inherit=*/false); UDPSocket::Connect("127.0.0.1:0", /*child_processes_inherit=*/false);
ASSERT_THAT_EXPECTED(socket, llvm::Succeeded()); ASSERT_THAT_EXPECTED(socket, llvm::Succeeded());

View File

@ -91,13 +91,34 @@ void lldb_private::CreateDomainConnectedSockets(
} }
#endif #endif
bool lldb_private::IsAddressFamilySupported(std::string ip) { static bool CheckIPSupport(llvm::StringRef Proto, llvm::StringRef Addr) {
auto addresses = lldb_private::SocketAddress::GetAddressInfo( llvm::Expected<std::unique_ptr<TCPSocket>> Sock = Socket::TcpListen(
ip.c_str(), NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); Addr, /*child_processes_inherit=*/false, /*predicate=*/nullptr);
return addresses.size() > 0; if (Sock)
return true;
llvm::Error Err = Sock.takeError();
GTEST_LOG_(WARNING) << llvm::formatv(
"Creating a canary {0} TCP socket failed: {1}.",
Proto, Err)
.str();
if (Err.isA<llvm::ECError>() &&
errorToErrorCode(std::move(Err)) ==
std::make_error_code(std::errc::address_not_available)) {
GTEST_LOG_(WARNING)
<< llvm::formatv(
"Assuming the host does not support {0}. Skipping test.", Proto)
.str();
return false;
}
consumeError(std::move(Err));
GTEST_LOG_(WARNING) << "Continuing anyway. The test will probably fail.";
return true;
} }
bool lldb_private::IsIPv4(std::string ip) { bool lldb_private::HostSupportsIPv4() {
struct sockaddr_in sock_addr; return CheckIPSupport("IPv4", "127.0.0.1:0");
return inet_pton(AF_INET, ip.c_str(), &(sock_addr.sin_addr)) != 0; }
bool lldb_private::HostSupportsIPv6() {
return CheckIPSupport("IPv6", "[::1]:0");
} }

View File

@ -40,8 +40,8 @@ void CreateDomainConnectedSockets(llvm::StringRef path,
std::unique_ptr<DomainSocket> *b_up); std::unique_ptr<DomainSocket> *b_up);
#endif #endif
bool IsAddressFamilySupported(std::string ip); bool HostSupportsIPv6();
bool IsIPv4(std::string ip); bool HostSupportsIPv4();
} // namespace lldb_private } // namespace lldb_private
#endif #endif