| //===-- SocketTestUtilities.cpp -------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SocketTestUtilities.h" |
| #include "lldb/Host/Config.h" |
| #include "lldb/Utility/StreamString.h" |
| |
| #ifdef _WIN32 |
| #include <winsock2.h> |
| #include <ws2tcpip.h> |
| #else |
| #include <arpa/inet.h> |
| #endif |
| |
| using namespace lldb_private; |
| |
| static void AcceptThread(Socket *listen_socket, bool child_processes_inherit, |
| Socket **accept_socket, Status *error) { |
| *error = listen_socket->Accept(*accept_socket); |
| } |
| |
| template <typename SocketType> |
| void lldb_private::CreateConnectedSockets( |
| llvm::StringRef listen_remote_address, |
| const std::function<std::string(const SocketType &)> &get_connect_addr, |
| std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) { |
| bool child_processes_inherit = false; |
| Status error; |
| std::unique_ptr<SocketType> listen_socket_up( |
| new SocketType(true, child_processes_inherit)); |
| ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); |
| error = listen_socket_up->Listen(listen_remote_address, 5); |
| ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); |
| ASSERT_TRUE(listen_socket_up->IsValid()); |
| |
| Status accept_error; |
| Socket *accept_socket; |
| std::thread accept_thread(AcceptThread, listen_socket_up.get(), |
| child_processes_inherit, &accept_socket, |
| &accept_error); |
| |
| std::string connect_remote_address = get_connect_addr(*listen_socket_up); |
| std::unique_ptr<SocketType> connect_socket_up( |
| new SocketType(true, child_processes_inherit)); |
| ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); |
| error = connect_socket_up->Connect(connect_remote_address); |
| ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded()); |
| ASSERT_TRUE(connect_socket_up->IsValid()); |
| |
| a_up->swap(connect_socket_up); |
| ASSERT_TRUE((*a_up)->IsValid()); |
| |
| accept_thread.join(); |
| b_up->reset(static_cast<SocketType *>(accept_socket)); |
| ASSERT_THAT_ERROR(accept_error.ToError(), llvm::Succeeded()); |
| ASSERT_NE(nullptr, b_up->get()); |
| ASSERT_TRUE((*b_up)->IsValid()); |
| |
| listen_socket_up.reset(); |
| } |
| |
| bool lldb_private::CreateTCPConnectedSockets( |
| std::string listen_remote_ip, std::unique_ptr<TCPSocket> *socket_a_up, |
| std::unique_ptr<TCPSocket> *socket_b_up) { |
| StreamString strm; |
| strm.Printf("[%s]:0", listen_remote_ip.c_str()); |
| CreateConnectedSockets<TCPSocket>( |
| strm.GetString(), |
| [=](const TCPSocket &s) { |
| char connect_remote_address[64]; |
| snprintf(connect_remote_address, sizeof(connect_remote_address), |
| "[%s]:%u", listen_remote_ip.c_str(), s.GetLocalPortNumber()); |
| return std::string(connect_remote_address); |
| }, |
| socket_a_up, socket_b_up); |
| return true; |
| } |
| |
| #if LLDB_ENABLE_POSIX |
| void lldb_private::CreateDomainConnectedSockets( |
| llvm::StringRef path, std::unique_ptr<DomainSocket> *socket_a_up, |
| std::unique_ptr<DomainSocket> *socket_b_up) { |
| return CreateConnectedSockets<DomainSocket>( |
| path, [=](const DomainSocket &) { return path.str(); }, socket_a_up, |
| socket_b_up); |
| } |
| #endif |
| |
| static bool CheckIPSupport(llvm::StringRef Proto, llvm::StringRef Addr) { |
| llvm::Expected<std::unique_ptr<TCPSocket>> Sock = Socket::TcpListen( |
| Addr, /*child_processes_inherit=*/false); |
| 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(); |
| bool HasProtocolError = false; |
| handleAllErrors(std::move(Err), [&](std::unique_ptr<llvm::ECError> ECErr) { |
| std::error_code ec = ECErr->convertToErrorCode(); |
| if (ec == std::make_error_code(std::errc::address_family_not_supported) || |
| ec == std::make_error_code(std::errc::address_not_available)) |
| HasProtocolError = true; |
| }); |
| if (HasProtocolError) { |
| GTEST_LOG_(WARNING) |
| << llvm::formatv( |
| "Assuming the host does not support {0}. Skipping test.", Proto) |
| .str(); |
| return false; |
| } |
| GTEST_LOG_(WARNING) << "Continuing anyway. The test will probably fail."; |
| return true; |
| } |
| |
| bool lldb_private::HostSupportsIPv4() { |
| return CheckIPSupport("IPv4", "127.0.0.1:0"); |
| } |
| |
| bool lldb_private::HostSupportsIPv6() { |
| return CheckIPSupport("IPv6", "[::1]:0"); |
| } |