1//===-- SocketTestUtilities.cpp -------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "TestingSupport/Host/SocketTestUtilities.h"
10#include "lldb/Host/Config.h"
11#include "lldb/Utility/StreamString.h"
12
13#ifdef _WIN32
14#include <winsock2.h>
15#include <ws2tcpip.h>
16#else
17#include <arpa/inet.h>
18#endif
19
20using namespace lldb_private;
21
22template <typename SocketType>
23void lldb_private::CreateConnectedSockets(
24 llvm::StringRef listen_remote_address,
25 const std::function<std::string(const SocketType &)> &get_connect_addr,
26 std::unique_ptr<SocketType> *a_up, std::unique_ptr<SocketType> *b_up) {
27 Status error;
28 auto listen_socket_up = std::make_unique<SocketType>(true);
29 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
30 error = listen_socket_up->Listen(listen_remote_address, 5);
31 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
32 ASSERT_TRUE(listen_socket_up->IsValid());
33
34 std::string connect_remote_address = get_connect_addr(*listen_socket_up);
35 auto connect_socket_up = std::make_unique<SocketType>(true);
36 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
37 error = connect_socket_up->Connect(connect_remote_address);
38 ASSERT_THAT_ERROR(error.ToError(), llvm::Succeeded());
39 ASSERT_TRUE(connect_socket_up->IsValid());
40
41 a_up->swap(connect_socket_up);
42 ASSERT_TRUE((*a_up)->IsValid());
43
44 Socket *accept_socket;
45 ASSERT_THAT_ERROR(
46 listen_socket_up->Accept(std::chrono::seconds(1), accept_socket)
47 .takeError(),
48 llvm::Succeeded());
49
50 b_up->reset(static_cast<SocketType *>(accept_socket));
51 ASSERT_NE(nullptr, b_up->get());
52 ASSERT_TRUE((*b_up)->IsValid());
53
54 listen_socket_up.reset();
55}
56
57bool lldb_private::CreateTCPConnectedSockets(
58 std::string listen_remote_ip, std::unique_ptr<TCPSocket> *socket_a_up,
59 std::unique_ptr<TCPSocket> *socket_b_up) {
60 StreamString strm;
61 strm.Printf(format: "[%s]:0", listen_remote_ip.c_str());
62 CreateConnectedSockets<TCPSocket>(
63 listen_remote_address: strm.GetString(),
64 get_connect_addr: [=](const TCPSocket &s) {
65 char connect_remote_address[64];
66 snprintf(s: connect_remote_address, maxlen: sizeof(connect_remote_address),
67 format: "[%s]:%u", listen_remote_ip.c_str(), s.GetLocalPortNumber());
68 return std::string(connect_remote_address);
69 },
70 a_up: socket_a_up, b_up: socket_b_up);
71 return true;
72}
73
74#if LLDB_ENABLE_POSIX
75void lldb_private::CreateDomainConnectedSockets(
76 llvm::StringRef path, std::unique_ptr<DomainSocket> *socket_a_up,
77 std::unique_ptr<DomainSocket> *socket_b_up) {
78 return CreateConnectedSockets<DomainSocket>(
79 listen_remote_address: path, get_connect_addr: [=](const DomainSocket &) { return path.str(); }, a_up: socket_a_up,
80 b_up: socket_b_up);
81}
82#endif
83
84static bool CheckIPSupport(llvm::StringRef Proto, llvm::StringRef Addr) {
85 llvm::Expected<std::unique_ptr<TCPSocket>> Sock = Socket::TcpListen(host_and_port: Addr);
86 if (Sock)
87 return true;
88 llvm::Error Err = Sock.takeError();
89 GTEST_LOG_(WARNING) << llvm::formatv(
90 Fmt: "Creating a canary {0} TCP socket failed: {1}.",
91 Vals&: Proto, Vals&: Err)
92 .str();
93 bool HasProtocolError = false;
94 handleAllErrors(E: std::move(Err), Handlers: [&](std::unique_ptr<llvm::ECError> ECErr) {
95 std::error_code ec = ECErr->convertToErrorCode();
96 if (ec == std::make_error_code(e: std::errc::address_family_not_supported) ||
97 ec == std::make_error_code(e: std::errc::address_not_available))
98 HasProtocolError = true;
99 });
100 if (HasProtocolError) {
101 GTEST_LOG_(WARNING)
102 << llvm::formatv(
103 Fmt: "Assuming the host does not support {0}. Skipping test.", Vals&: Proto)
104 .str();
105 return false;
106 }
107 GTEST_LOG_(WARNING) << "Continuing anyway. The test will probably fail.";
108 return true;
109}
110
111bool lldb_private::HostSupportsIPv4() {
112 return CheckIPSupport(Proto: "IPv4", Addr: "127.0.0.1:0");
113}
114
115bool lldb_private::HostSupportsIPv6() {
116 return CheckIPSupport(Proto: "IPv6", Addr: "[::1]:0");
117}
118
119bool lldb_private::HostSupportsLocalhostToIPv4() {
120 if (!HostSupportsIPv4())
121 return false;
122
123 auto addresses = SocketAddress::GetAddressInfo(
124 hostname: "localhost", servname: nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
125 return llvm::any_of(Range&: addresses, P: [](const SocketAddress &addr) {
126 return addr.GetFamily() == AF_INET;
127 });
128}
129
130bool lldb_private::HostSupportsLocalhostToIPv6() {
131 if (!HostSupportsIPv6())
132 return false;
133
134 auto addresses = SocketAddress::GetAddressInfo(
135 hostname: "localhost", servname: nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
136 return llvm::any_of(Range&: addresses, P: [](const SocketAddress &addr) {
137 return addr.GetFamily() == AF_INET6;
138 });
139}
140
141llvm::Expected<std::string> lldb_private::GetLocalhostIP() {
142 if (HostSupportsIPv4())
143 return "127.0.0.1";
144 if (HostSupportsIPv6())
145 return "[::1]";
146 return llvm::make_error<llvm::StringError>(
147 Args: "Neither IPv4 nor IPv6 appear to be supported",
148 Args: llvm::inconvertibleErrorCode());
149}
150

source code of lldb/unittests/TestingSupport/Host/SocketTestUtilities.cpp