test: sock: Enable socket pair tests on Windows
What changed, and why it matters
This commit only changes test code. It replaces Unix-only socketpair() with a cross-platform TCP loopback socket helper so the same socket tests can run on Windows. There is no change to production Bitcoin Core networking code, consensus logic, or wallet handling, so it does not affect live node security.
No security action required. Treat as a normal test-portability improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors src/test/sock_tests.cpp. It removes the #ifndef WIN32 guard and the AF_UNIX socketpair() helper, and introduces a TcpSocketPair RAII struct that creates two TCP sockets, binds/listens on the receiver, connects the sender to INADDR_LOOPBACK on an ephemeral port, and accepts the connection. Existing test cases (send_and_receive, wait, recv_until_terminator_limit) are updated to use the new helper. All changes are confined to unit tests; no production code is modified.
Changed components
src/test/sock_tests.cppInspect captured patch +64 / −55
diff --git a/src/test/sock_tests.cpp b/src/test/sock_tests.cpp
index 06d9f7d3..56fdfaaf 100644
--- a/src/test/sock_tests.cpp
+++ b/src/test/sock_tests.cpp
@@ -81,61 +81,75 @@ BOOST_AUTO_TEST_CASE(move_assignment)
BOOST_CHECK(SocketIsClosed(s2));
}
-#ifndef WIN32 // Windows does not have socketpair(2).
-
-static void CreateSocketPair(int s[2])
-{
- BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0);
-}
-
-static void SendAndRecvMessage(const Sock& sender, const Sock& receiver)
-{
- const char* msg = "abcd";
- constexpr ssize_t msg_len = 4;
- char recv_buf[10];
-
- BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
- BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
- BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
-}
+struct TcpSocketPair {
+ Sock sender;
+ Sock receiver;
+
+ TcpSocketPair()
+ : sender{Sock{CreateSocket()}},
+ receiver{Sock{CreateSocket()}}
+ {
+ connect_pair();
+ }
+
+ TcpSocketPair(const TcpSocketPair&) = delete;
+ TcpSocketPair& operator= (const TcpSocketPair&) = delete;
+ TcpSocketPair(TcpSocketPair&&) = default;
+ TcpSocketPair& operator= (TcpSocketPair&&) = default;
+
+ void connect_pair()
+ {
+ sockaddr_in addr{};
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = 0;
+
+ BOOST_REQUIRE_EQUAL(receiver.Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0);
+ BOOST_REQUIRE_EQUAL(receiver.Listen(1), 0);
+
+ // Get the address of the listener.
+ sockaddr_in bound{};
+ socklen_t blen = sizeof(bound);
+ BOOST_REQUIRE_EQUAL(receiver.GetSockName(reinterpret_cast<sockaddr*>(&bound), &blen), 0);
+ BOOST_REQUIRE_EQUAL(blen, sizeof(bound));
+
+ BOOST_REQUIRE_EQUAL(sender.Connect(reinterpret_cast<sockaddr*>(&bound), sizeof(bound)), 0);
+
+ std::unique_ptr<Sock> accepted = receiver.Accept(nullptr, nullptr);
+ BOOST_REQUIRE(accepted != nullptr);
+
+ receiver = std::move(*accepted);
+ }
+
+ void send_and_receive()
+ {
+ const char* msg = "abcd";
+ constexpr ssize_t msg_len = 4;
+ char recv_buf[10];
+
+ BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
+ BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
+ BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
+ }
+};
BOOST_AUTO_TEST_CASE(send_and_receive)
{
- int s[2];
- CreateSocketPair(s);
-
- Sock* sock0 = new Sock(s[0]);
- Sock* sock1 = new Sock(s[1]);
+ TcpSocketPair socks{};
+ socks.send_and_receive();
- SendAndRecvMessage(*sock0, *sock1);
-
- Sock* sock0moved = new Sock(std::move(*sock0));
- Sock* sock1moved = new Sock(INVALID_SOCKET);
- *sock1moved = std::move(*sock1);
-
- delete sock0;
- delete sock1;
-
- SendAndRecvMessage(*sock1moved, *sock0moved);
-
- delete sock0moved;
- delete sock1moved;
-
- BOOST_CHECK(SocketIsClosed(s[0]));
- BOOST_CHECK(SocketIsClosed(s[1]));
+ // Sockets are still connected after being moved.
+ TcpSocketPair socks_moved = std::move(socks);
+ socks_moved.send_and_receive();
}
BOOST_AUTO_TEST_CASE(wait)
{
- int s[2];
- CreateSocketPair(s);
+ TcpSocketPair socks = TcpSocketPair{};
- Sock sock0(s[0]);
- Sock sock1(s[1]);
+ std::thread waiter([&socks]() { (void)socks.receiver.Wait(24h, Sock::RECV); });
- std::thread waiter([&sock0]() { (void)sock0.Wait(24h, Sock::RECV); });
-
- BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
+ BOOST_REQUIRE_EQUAL(socks.sender.Send("a", 1, 0), 1);
waiter.join();
}
@@ -144,31 +158,26 @@ BOOST_AUTO_TEST_CASE(recv_until_terminator_limit)
{
constexpr auto timeout = 1min; // High enough so that it is never hit.
CThreadInterrupt interrupt;
- int s[2];
- CreateSocketPair(s);
- Sock sock_send(s[0]);
- Sock sock_recv(s[1]);
+ TcpSocketPair socks = TcpSocketPair{};
- std::thread receiver([&sock_recv, &timeout, &interrupt]() {
+ std::thread receiver([&socks, &timeout, &interrupt]() {
constexpr size_t max_data{10};
bool threw_as_expected{false};
// BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which
// creates a data race. So mimic it manually.
try {
- (void)sock_recv.RecvUntilTerminator('\n', timeout, interrupt, max_data);
+ (void)socks.receiver.RecvUntilTerminator('\n', timeout, interrupt, max_data);
} catch (const std::runtime_error& e) {
threw_as_expected = HasReason("too many bytes without a terminator")(e);
}
assert(threw_as_expected);
});
- BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("1234567", timeout, interrupt));
- BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("89a\n", timeout, interrupt));
+ BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("1234567", timeout, interrupt));
+ BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("89a\n", timeout, interrupt));
receiver.join();
}
-#endif /* WIN32 */
-
BOOST_AUTO_TEST_SUITE_END()
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.