1 | //===-- SocketTest.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 "TestingSupport/SubsystemRAII.h" |
11 | #include "lldb/Host/Config.h" |
12 | #include "lldb/Utility/UriParser.h" |
13 | #include "llvm/Testing/Support/Error.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | using namespace lldb_private; |
17 | |
18 | struct SocketTestParams { |
19 | bool is_ipv6; |
20 | std::string localhost_ip; |
21 | }; |
22 | |
23 | class SocketTest : public testing::TestWithParam<SocketTestParams> { |
24 | public: |
25 | SubsystemRAII<Socket> subsystems; |
26 | |
27 | protected: |
28 | bool HostSupportsProtocol() const { |
29 | if (GetParam().is_ipv6) |
30 | return HostSupportsIPv6(); |
31 | return HostSupportsIPv4(); |
32 | } |
33 | }; |
34 | |
35 | TEST_P(SocketTest, DecodeHostAndPort) { |
36 | EXPECT_THAT_EXPECTED(Socket::DecodeHostAndPort("localhost:1138" ), |
37 | llvm::HasValue(Socket::HostAndPort{"localhost" , 1138})); |
38 | |
39 | EXPECT_THAT_EXPECTED( |
40 | Socket::DecodeHostAndPort("google.com:65536" ), |
41 | llvm::FailedWithMessage( |
42 | "invalid host:port specification: 'google.com:65536'" )); |
43 | |
44 | EXPECT_THAT_EXPECTED( |
45 | Socket::DecodeHostAndPort("google.com:-1138" ), |
46 | llvm::FailedWithMessage( |
47 | "invalid host:port specification: 'google.com:-1138'" )); |
48 | |
49 | EXPECT_THAT_EXPECTED( |
50 | Socket::DecodeHostAndPort("google.com:65536" ), |
51 | llvm::FailedWithMessage( |
52 | "invalid host:port specification: 'google.com:65536'" )); |
53 | |
54 | EXPECT_THAT_EXPECTED(Socket::DecodeHostAndPort("12345" ), |
55 | llvm::HasValue(Socket::HostAndPort{"" , 12345})); |
56 | |
57 | EXPECT_THAT_EXPECTED(Socket::DecodeHostAndPort("*:0" ), |
58 | llvm::HasValue(Socket::HostAndPort{"*" , 0})); |
59 | |
60 | EXPECT_THAT_EXPECTED(Socket::DecodeHostAndPort("*:65535" ), |
61 | llvm::HasValue(Socket::HostAndPort{"*" , 65535})); |
62 | |
63 | EXPECT_THAT_EXPECTED( |
64 | Socket::DecodeHostAndPort("[::1]:12345" ), |
65 | llvm::HasValue(Socket::HostAndPort{"::1" , 12345})); |
66 | |
67 | EXPECT_THAT_EXPECTED( |
68 | Socket::DecodeHostAndPort("[abcd:12fg:AF58::1]:12345" ), |
69 | llvm::HasValue(Socket::HostAndPort{"abcd:12fg:AF58::1" , 12345})); |
70 | } |
71 | |
72 | #if LLDB_ENABLE_POSIX |
73 | TEST_P(SocketTest, DomainListenConnectAccept) { |
74 | llvm::SmallString<64> Path; |
75 | std::error_code EC = llvm::sys::fs::createUniqueDirectory(Prefix: "DomainListenConnectAccept" , ResultPath&: Path); |
76 | ASSERT_FALSE(EC); |
77 | llvm::sys::path::append(path&: Path, a: "test" ); |
78 | |
79 | // Skip the test if the $TMPDIR is too long to hold a domain socket. |
80 | if (Path.size() > 107u) |
81 | return; |
82 | |
83 | std::unique_ptr<DomainSocket> socket_a_up; |
84 | std::unique_ptr<DomainSocket> socket_b_up; |
85 | CreateDomainConnectedSockets(path: Path, a_up: &socket_a_up, b_up: &socket_b_up); |
86 | } |
87 | #endif |
88 | |
89 | TEST_P(SocketTest, TCPListen0ConnectAccept) { |
90 | if (!HostSupportsProtocol()) |
91 | return; |
92 | std::unique_ptr<TCPSocket> socket_a_up; |
93 | std::unique_ptr<TCPSocket> socket_b_up; |
94 | CreateTCPConnectedSockets(listen_remote_ip: GetParam().localhost_ip, a_up: &socket_a_up, |
95 | b_up: &socket_b_up); |
96 | } |
97 | |
98 | TEST_P(SocketTest, TCPGetAddress) { |
99 | std::unique_ptr<TCPSocket> socket_a_up; |
100 | std::unique_ptr<TCPSocket> socket_b_up; |
101 | if (!HostSupportsProtocol()) |
102 | return; |
103 | CreateTCPConnectedSockets(listen_remote_ip: GetParam().localhost_ip, a_up: &socket_a_up, |
104 | b_up: &socket_b_up); |
105 | |
106 | EXPECT_EQ(socket_a_up->GetLocalPortNumber(), |
107 | socket_b_up->GetRemotePortNumber()); |
108 | EXPECT_EQ(socket_b_up->GetLocalPortNumber(), |
109 | socket_a_up->GetRemotePortNumber()); |
110 | EXPECT_NE(socket_a_up->GetLocalPortNumber(), |
111 | socket_b_up->GetLocalPortNumber()); |
112 | EXPECT_STREQ(GetParam().localhost_ip.c_str(), |
113 | socket_a_up->GetRemoteIPAddress().c_str()); |
114 | EXPECT_STREQ(GetParam().localhost_ip.c_str(), |
115 | socket_b_up->GetRemoteIPAddress().c_str()); |
116 | } |
117 | |
118 | TEST_P(SocketTest, UDPConnect) { |
119 | // UDPSocket::Connect() creates sockets with AF_INET (IPv4). |
120 | if (!HostSupportsIPv4()) |
121 | return; |
122 | llvm::Expected<std::unique_ptr<UDPSocket>> socket = |
123 | UDPSocket::Connect(name: "127.0.0.1:0" , /*child_processes_inherit=*/false); |
124 | |
125 | ASSERT_THAT_EXPECTED(socket, llvm::Succeeded()); |
126 | EXPECT_TRUE(socket.get()->IsValid()); |
127 | } |
128 | |
129 | TEST_P(SocketTest, TCPListen0GetPort) { |
130 | if (!HostSupportsIPv4()) |
131 | return; |
132 | llvm::Expected<std::unique_ptr<TCPSocket>> sock = |
133 | Socket::TcpListen(host_and_port: "10.10.12.3:0" , child_processes_inherit: false); |
134 | ASSERT_THAT_EXPECTED(sock, llvm::Succeeded()); |
135 | ASSERT_TRUE(sock.get()->IsValid()); |
136 | EXPECT_NE(sock.get()->GetLocalPortNumber(), 0); |
137 | } |
138 | |
139 | TEST_P(SocketTest, TCPGetConnectURI) { |
140 | std::unique_ptr<TCPSocket> socket_a_up; |
141 | std::unique_ptr<TCPSocket> socket_b_up; |
142 | if (!HostSupportsProtocol()) |
143 | return; |
144 | CreateTCPConnectedSockets(listen_remote_ip: GetParam().localhost_ip, a_up: &socket_a_up, |
145 | b_up: &socket_b_up); |
146 | |
147 | std::string uri(socket_a_up->GetRemoteConnectionURI()); |
148 | EXPECT_EQ((URI{"connect" , GetParam().localhost_ip, |
149 | socket_a_up->GetRemotePortNumber(), "/" }), |
150 | URI::Parse(uri)); |
151 | } |
152 | |
153 | TEST_P(SocketTest, UDPGetConnectURI) { |
154 | // UDPSocket::Connect() creates sockets with AF_INET (IPv4). |
155 | if (!HostSupportsIPv4()) |
156 | return; |
157 | llvm::Expected<std::unique_ptr<UDPSocket>> socket = |
158 | UDPSocket::Connect(name: "127.0.0.1:0" , /*child_processes_inherit=*/false); |
159 | ASSERT_THAT_EXPECTED(socket, llvm::Succeeded()); |
160 | |
161 | std::string uri = socket.get()->GetRemoteConnectionURI(); |
162 | EXPECT_EQ((URI{"udp" , "127.0.0.1" , 0, "/" }), URI::Parse(uri)); |
163 | } |
164 | |
165 | #if LLDB_ENABLE_POSIX |
166 | TEST_P(SocketTest, DomainGetConnectURI) { |
167 | llvm::SmallString<64> domain_path; |
168 | std::error_code EC = |
169 | llvm::sys::fs::createUniqueDirectory(Prefix: "DomainListenConnectAccept" , ResultPath&: domain_path); |
170 | ASSERT_FALSE(EC); |
171 | llvm::sys::path::append(path&: domain_path, a: "test" ); |
172 | |
173 | // Skip the test if the $TMPDIR is too long to hold a domain socket. |
174 | if (domain_path.size() > 107u) |
175 | return; |
176 | |
177 | std::unique_ptr<DomainSocket> socket_a_up; |
178 | std::unique_ptr<DomainSocket> socket_b_up; |
179 | CreateDomainConnectedSockets(path: domain_path, a_up: &socket_a_up, b_up: &socket_b_up); |
180 | |
181 | std::string uri(socket_a_up->GetRemoteConnectionURI()); |
182 | EXPECT_EQ((URI{"unix-connect" , "" , std::nullopt, domain_path}), |
183 | URI::Parse(uri)); |
184 | |
185 | EXPECT_EQ(socket_b_up->GetRemoteConnectionURI(), "" ); |
186 | } |
187 | #endif |
188 | |
189 | INSTANTIATE_TEST_SUITE_P( |
190 | SocketTests, SocketTest, |
191 | testing::Values(SocketTestParams{/*is_ipv6=*/false, |
192 | /*localhost_ip=*/"127.0.0.1" }, |
193 | SocketTestParams{/*is_ipv6=*/true, /*localhost_ip=*/"::1" }), |
194 | // Prints "SocketTests/SocketTest.DecodeHostAndPort/ipv4" etc. in test logs. |
195 | [](const testing::TestParamInfo<SocketTestParams> &info) { |
196 | return info.param.is_ipv6 ? "ipv6" : "ipv4" ; |
197 | }); |
198 | |