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 | std::string addr_buffer = |
30 | llvm::formatv(Fmt: "{0}:{1}" , Vals: (const char *)baton, Vals&: port).str(); |
31 | llvm::Expected<std::unique_ptr<Socket>> socket_or_err = |
32 | Socket::TcpConnect(host_and_port: addr_buffer); |
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 | const char *fmt = addresses.front().GetFamily() == AF_INET6 ? "[{0}]" : "{0}" ; |
63 | std::string addr_wrap = llvm::formatv(Fmt: fmt, Vals&: addr).str(); |
64 | |
65 | RNBSocket server_socket; |
66 | auto result = server_socket.Listen(addr, 0, ServerCallbackv4, |
67 | (const void *)addr_wrap.c_str()); |
68 | ASSERT_TRUE(result == rnb_success); |
69 | result = server_socket.Write(hello.c_str(), hello.length()); |
70 | ASSERT_TRUE(result == rnb_success); |
71 | std::string bye; |
72 | result = server_socket.Read(bye); |
73 | ASSERT_TRUE(result == rnb_success); |
74 | ASSERT_EQ(bye, goodbye); |
75 | |
76 | int exit_status; |
77 | wait(stat_loc: &exit_status); |
78 | ASSERT_EQ(exit_status, 0); |
79 | } |
80 | |
81 | TEST(RNBSocket, LoopBackListenIPv4) { TestSocketListen(addr: "127.0.0.1" ); } |
82 | |
83 | TEST(RNBSocket, LoopBackListenIPv6) { TestSocketListen(addr: "::1" ); } |
84 | |
85 | TEST(RNBSocket, AnyListen) { TestSocketListen(addr: "*" ); } |
86 | |
87 | void TestSocketConnect(const char *addr) { |
88 | // Skip IPv6 tests if there isn't a valid interafce |
89 | auto addresses = lldb_private::SocketAddress::GetAddressInfo( |
90 | hostname: addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); |
91 | if (addresses.size() == 0) |
92 | return; |
93 | |
94 | const char *fmt = |
95 | addresses.front().GetFamily() == AF_INET6 ? "[{0}]:0" : "{0}:0" ; |
96 | std::string addr_wrap = llvm::formatv(Fmt: fmt, Vals&: addr).str(); |
97 | |
98 | Socket *server_socket; |
99 | llvm::Expected<std::unique_ptr<Socket>> socket_or_err = |
100 | Socket::TcpListen(host_and_port: addr_wrap, backlog: false); |
101 | ASSERT_THAT_EXPECTED(socket_or_err, llvm::Succeeded()); |
102 | server_socket = socket_or_err->get(); |
103 | |
104 | auto port = ((TCPSocket *)server_socket)->GetLocalPortNumber(); |
105 | auto child_pid = fork(); |
106 | if (child_pid != 0) { |
107 | RNBSocket client_socket; |
108 | auto result = client_socket.Connect(addr, port); |
109 | ASSERT_TRUE(result == rnb_success); |
110 | result = client_socket.Write(hello.c_str(), hello.length()); |
111 | ASSERT_TRUE(result == rnb_success); |
112 | std::string bye; |
113 | result = client_socket.Read(bye); |
114 | ASSERT_TRUE(result == rnb_success); |
115 | ASSERT_EQ(bye, goodbye); |
116 | } else { |
117 | Socket *connected_socket; |
118 | Status err = |
119 | server_socket->Accept(timeout: std::chrono::seconds(10), socket&: connected_socket); |
120 | if (err.Fail()) { |
121 | llvm::errs() << err.AsCString(); |
122 | abort(); |
123 | } |
124 | char buffer[32]; |
125 | size_t read_size = 32; |
126 | err = connected_socket->Read(buf: (void *)&buffer[0], num_bytes&: read_size); |
127 | if (err.Fail()) { |
128 | llvm::errs() << err.AsCString(); |
129 | abort(); |
130 | } |
131 | std::string Recv(&buffer[0], read_size); |
132 | if (Recv != hello) { |
133 | llvm::errs() << err.AsCString(); |
134 | abort(); |
135 | } |
136 | size_t write_size = goodbye.length(); |
137 | err = connected_socket->Write(buf: goodbye.c_str(), num_bytes&: write_size); |
138 | if (err.Fail()) { |
139 | llvm::errs() << err.AsCString(); |
140 | abort(); |
141 | } |
142 | if (write_size != goodbye.length()) { |
143 | llvm::errs() << err.AsCString(); |
144 | abort(); |
145 | } |
146 | exit(status: 0); |
147 | } |
148 | int exit_status; |
149 | wait(stat_loc: &exit_status); |
150 | ASSERT_EQ(exit_status, 0); |
151 | } |
152 | |
153 | TEST(RNBSocket, LoopBackConnectIPv4) { TestSocketConnect(addr: "127.0.0.1" ); } |
154 | |
155 | TEST(RNBSocket, LoopBackConnectIPv6) { TestSocketConnect(addr: "::1" ); } |
156 | |