1 | //===-- RNBSocketTest.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 "gtest/gtest.h" |
10 | |
11 | #include <arpa/inet.h> |
12 | #include <sys/sysctl.h> |
13 | #include <unistd.h> |
14 | |
15 | #include "RNBDefs.h" |
16 | #include "RNBSocket.h" |
17 | #include "lldb/Host/Socket.h" |
18 | #include "lldb/Host/common/TCPSocket.h" |
19 | #include "llvm/Testing/Support/Error.h" |
20 | |
21 | using namespace lldb_private; |
22 | |
23 | std::string hello = "Hello, world!" ; |
24 | std::string goodbye = "Goodbye!" ; |
25 | |
26 | static void ServerCallbackv4(const void *baton, in_port_t port) { |
27 | auto child_pid = fork(); |
28 | if (child_pid == 0) { |
29 | char addr_buffer[256]; |
30 | sprintf(s: addr_buffer, format: "%s:%d" , (const char *)baton, port); |
31 | llvm::Expected<std::unique_ptr<Socket>> socket_or_err = |
32 | Socket::TcpConnect(host_and_port: addr_buffer, child_processes_inherit: false); |
33 | ASSERT_THAT_EXPECTED(socket_or_err, llvm::Succeeded()); |
34 | Socket *client_socket = socket_or_err->get(); |
35 | |
36 | char buffer[32]; |
37 | size_t read_size = 32; |
38 | Status err = client_socket->Read(buf: (void *)&buffer[0], num_bytes&: read_size); |
39 | if (err.Fail()) |
40 | abort(); |
41 | std::string Recv(&buffer[0], read_size); |
42 | if (Recv != hello) |
43 | abort(); |
44 | size_t write_size = goodbye.length(); |
45 | err = client_socket->Write(buf: goodbye.c_str(), num_bytes&: write_size); |
46 | if (err.Fail()) |
47 | abort(); |
48 | if (write_size != goodbye.length()) |
49 | abort(); |
50 | delete client_socket; |
51 | exit(status: 0); |
52 | } |
53 | } |
54 | |
55 | void TestSocketListen(const char *addr) { |
56 | // Skip IPv6 tests if there isn't a valid interafce |
57 | auto addresses = lldb_private::SocketAddress::GetAddressInfo( |
58 | hostname: addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); |
59 | if (addresses.size() == 0) |
60 | return; |
61 | |
62 | char addr_wrap[256]; |
63 | if (addresses.front().GetFamily() == AF_INET6) |
64 | sprintf(s: addr_wrap, format: "[%s]" , addr); |
65 | else |
66 | sprintf(s: addr_wrap, format: "%s" , addr); |
67 | |
68 | RNBSocket server_socket; |
69 | auto result = |
70 | server_socket.Listen(addr, 0, ServerCallbackv4, (const void *)addr_wrap); |
71 | ASSERT_TRUE(result == rnb_success); |
72 | result = server_socket.Write(hello.c_str(), hello.length()); |
73 | ASSERT_TRUE(result == rnb_success); |
74 | std::string bye; |
75 | result = server_socket.Read(bye); |
76 | ASSERT_TRUE(result == rnb_success); |
77 | ASSERT_EQ(bye, goodbye); |
78 | |
79 | int exit_status; |
80 | wait(stat_loc: &exit_status); |
81 | ASSERT_EQ(exit_status, 0); |
82 | } |
83 | |
84 | TEST(RNBSocket, LoopBackListenIPv4) { TestSocketListen(addr: "127.0.0.1" ); } |
85 | |
86 | TEST(RNBSocket, LoopBackListenIPv6) { TestSocketListen(addr: "::1" ); } |
87 | |
88 | TEST(RNBSocket, AnyListen) { TestSocketListen(addr: "*" ); } |
89 | |
90 | void TestSocketConnect(const char *addr) { |
91 | // Skip IPv6 tests if there isn't a valid interafce |
92 | auto addresses = lldb_private::SocketAddress::GetAddressInfo( |
93 | hostname: addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); |
94 | if (addresses.size() == 0) |
95 | return; |
96 | |
97 | char addr_wrap[256]; |
98 | if (addresses.front().GetFamily() == AF_INET6) |
99 | sprintf(s: addr_wrap, format: "[%s]:0" , addr); |
100 | else |
101 | sprintf(s: addr_wrap, format: "%s:0" , addr); |
102 | |
103 | Socket *server_socket; |
104 | llvm::Expected<std::unique_ptr<Socket>> socket_or_err = |
105 | Socket::TcpListen(host_and_port: addr_wrap, child_processes_inherit: false); |
106 | ASSERT_THAT_EXPECTED(socket_or_err, llvm::Succeeded()); |
107 | server_socket = socket_or_err->get(); |
108 | |
109 | auto port = ((TCPSocket *)server_socket)->GetLocalPortNumber(); |
110 | auto child_pid = fork(); |
111 | if (child_pid != 0) { |
112 | RNBSocket client_socket; |
113 | auto result = client_socket.Connect(addr, port); |
114 | ASSERT_TRUE(result == rnb_success); |
115 | result = client_socket.Write(hello.c_str(), hello.length()); |
116 | ASSERT_TRUE(result == rnb_success); |
117 | std::string bye; |
118 | result = client_socket.Read(bye); |
119 | ASSERT_TRUE(result == rnb_success); |
120 | ASSERT_EQ(bye, goodbye); |
121 | } else { |
122 | Socket *connected_socket; |
123 | Status err = server_socket->Accept(socket&: connected_socket); |
124 | if (err.Fail()) { |
125 | llvm::errs() << err.AsCString(); |
126 | abort(); |
127 | } |
128 | char buffer[32]; |
129 | size_t read_size = 32; |
130 | err = connected_socket->Read(buf: (void *)&buffer[0], num_bytes&: read_size); |
131 | if (err.Fail()) { |
132 | llvm::errs() << err.AsCString(); |
133 | abort(); |
134 | } |
135 | std::string Recv(&buffer[0], read_size); |
136 | if (Recv != hello) { |
137 | llvm::errs() << err.AsCString(); |
138 | abort(); |
139 | } |
140 | size_t write_size = goodbye.length(); |
141 | err = connected_socket->Write(buf: goodbye.c_str(), num_bytes&: write_size); |
142 | if (err.Fail()) { |
143 | llvm::errs() << err.AsCString(); |
144 | abort(); |
145 | } |
146 | if (write_size != goodbye.length()) { |
147 | llvm::errs() << err.AsCString(); |
148 | abort(); |
149 | } |
150 | exit(status: 0); |
151 | } |
152 | int exit_status; |
153 | wait(stat_loc: &exit_status); |
154 | ASSERT_EQ(exit_status, 0); |
155 | } |
156 | |
157 | TEST(RNBSocket, LoopBackConnectIPv4) { TestSocketConnect(addr: "127.0.0.1" ); } |
158 | |
159 | TEST(RNBSocket, LoopBackConnectIPv6) { TestSocketConnect(addr: "::1" ); } |
160 | |