Branch data Line data Source code
1 : : // Copyright (c) 2021-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <common/system.h>
6 : : #include <compat/compat.h>
7 : : #include <test/util/common.h>
8 : : #include <test/util/setup_common.h>
9 : : #include <util/sock.h>
10 : : #include <util/threadinterrupt.h>
11 : :
12 : : #include <boost/test/unit_test.hpp>
13 : :
14 : : #include <cassert>
15 : : #include <thread>
16 : :
17 : : using namespace std::chrono_literals;
18 : :
19 : : BOOST_FIXTURE_TEST_SUITE(sock_tests, BasicTestingSetup)
20 : :
21 : 12 : static bool SocketIsClosed(const SOCKET& s)
22 : : {
23 : : // Notice that if another thread is running and creates its own socket after `s` has been
24 : : // closed, it may be assigned the same file descriptor number. In this case, our test will
25 : : // wrongly pretend that the socket is not closed.
26 : 12 : int type;
27 : 12 : socklen_t len = sizeof(type);
28 : 12 : return getsockopt(s, SOL_SOCKET, SO_TYPE, reinterpret_cast<char*>(&type), &len) == SOCKET_ERROR;
29 : : }
30 : :
31 : 10 : static SOCKET CreateSocket()
32 : : {
33 : 10 : const SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
34 [ + - ]: 20 : BOOST_REQUIRE(s != static_cast<SOCKET>(SOCKET_ERROR));
35 : 10 : return s;
36 : : }
37 : :
38 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(constructor_and_destructor)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
39 : : {
40 : 1 : const SOCKET s = CreateSocket();
41 [ + - ]: 1 : Sock* sock = new Sock(s);
42 [ + - + - ]: 2 : BOOST_CHECK(*sock == s);
43 [ + - + - ]: 2 : BOOST_CHECK(!SocketIsClosed(s));
44 [ + - ]: 1 : delete sock;
45 [ + - ]: 2 : BOOST_CHECK(SocketIsClosed(s));
46 : 1 : }
47 : :
48 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(move_constructor)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
49 : : {
50 : 1 : const SOCKET s = CreateSocket();
51 [ + - ]: 1 : Sock* sock1 = new Sock(s);
52 [ + - ]: 1 : Sock* sock2 = new Sock(std::move(*sock1));
53 [ + - ]: 1 : delete sock1;
54 [ + - ]: 2 : BOOST_CHECK(!SocketIsClosed(s));
55 [ + - + - : 2 : BOOST_CHECK(*sock2 == s);
+ - ]
56 [ + - ]: 1 : delete sock2;
57 [ + - ]: 2 : BOOST_CHECK(SocketIsClosed(s));
58 : 1 : }
59 : :
60 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(move_assignment)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
61 : : {
62 : 1 : const SOCKET s1 = CreateSocket();
63 : 1 : const SOCKET s2 = CreateSocket();
64 [ + - ]: 1 : Sock* sock1 = new Sock(s1);
65 [ + - ]: 1 : Sock* sock2 = new Sock(s2);
66 : :
67 [ + - ]: 2 : BOOST_CHECK(!SocketIsClosed(s1));
68 [ + - ]: 2 : BOOST_CHECK(!SocketIsClosed(s2));
69 : :
70 : 1 : *sock2 = std::move(*sock1);
71 [ + - ]: 2 : BOOST_CHECK(!SocketIsClosed(s1));
72 [ + - ]: 2 : BOOST_CHECK(SocketIsClosed(s2));
73 [ + - + - : 2 : BOOST_CHECK(*sock2 == s1);
+ - ]
74 : :
75 [ + - ]: 1 : delete sock1;
76 [ + - ]: 2 : BOOST_CHECK(!SocketIsClosed(s1));
77 [ + - ]: 2 : BOOST_CHECK(SocketIsClosed(s2));
78 [ + - + - : 2 : BOOST_CHECK(*sock2 == s1);
+ - ]
79 : :
80 [ + - ]: 1 : delete sock2;
81 [ + - ]: 2 : BOOST_CHECK(SocketIsClosed(s1));
82 [ + - ]: 2 : BOOST_CHECK(SocketIsClosed(s2));
83 : 1 : }
84 : :
85 : 4 : struct TcpSocketPair {
86 : : Sock sender;
87 : : Sock receiver;
88 : :
89 : 3 : TcpSocketPair()
90 : 3 : : sender{Sock{CreateSocket()}},
91 [ + - + - ]: 3 : receiver{Sock{CreateSocket()}}
92 : : {
93 [ + - ]: 3 : connect_pair();
94 : 3 : }
95 : :
96 : : TcpSocketPair(const TcpSocketPair&) = delete;
97 : : TcpSocketPair& operator= (const TcpSocketPair&) = delete;
98 [ + - ]: 1 : TcpSocketPair(TcpSocketPair&&) = default;
99 : : TcpSocketPair& operator= (TcpSocketPair&&) = default;
100 : :
101 : 3 : void connect_pair()
102 : : {
103 : 3 : sockaddr_in addr{};
104 : 3 : addr.sin_family = AF_INET;
105 : 3 : addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
106 : 3 : addr.sin_port = 0;
107 : :
108 [ + - ]: 3 : BOOST_REQUIRE_EQUAL(receiver.Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0);
109 [ + - ]: 3 : BOOST_REQUIRE_EQUAL(receiver.Listen(1), 0);
110 : :
111 : : // Get the address of the listener.
112 : 3 : sockaddr_in bound{};
113 : 3 : socklen_t blen = sizeof(bound);
114 [ + - ]: 3 : BOOST_REQUIRE_EQUAL(receiver.GetSockName(reinterpret_cast<sockaddr*>(&bound), &blen), 0);
115 [ + - ]: 3 : BOOST_REQUIRE_EQUAL(blen, sizeof(bound));
116 : :
117 [ + - ]: 3 : BOOST_REQUIRE_EQUAL(sender.Connect(reinterpret_cast<sockaddr*>(&bound), sizeof(bound)), 0);
118 : :
119 : 3 : std::unique_ptr<Sock> accepted = receiver.Accept(nullptr, nullptr);
120 [ + - + - : 6 : BOOST_REQUIRE(accepted != nullptr);
+ - ]
121 : :
122 [ + - ]: 3 : receiver = std::move(*accepted);
123 : 3 : }
124 : :
125 : 2 : void send_and_receive()
126 : : {
127 : 2 : const char* msg = "abcd";
128 : 2 : constexpr ssize_t msg_len = 4;
129 : 2 : char recv_buf[10];
130 : :
131 [ + - ]: 2 : BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
132 [ + - ]: 2 : BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
133 [ + - ]: 2 : BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
134 : 2 : }
135 : : };
136 : :
137 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(send_and_receive)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
138 : : {
139 : 1 : TcpSocketPair socks{};
140 [ + - ]: 1 : socks.send_and_receive();
141 : :
142 : : // Sockets are still connected after being moved.
143 [ + - ]: 1 : TcpSocketPair socks_moved = std::move(socks);
144 [ + - ]: 1 : socks_moved.send_and_receive();
145 : 2 : }
146 : :
147 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(wait)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
148 : : {
149 : 1 : TcpSocketPair socks = TcpSocketPair{};
150 : :
151 [ + - ]: 2 : std::thread waiter([&socks]() { (void)socks.receiver.Wait(24h, Sock::RECV); });
152 : :
153 [ + - + - : 1 : BOOST_REQUIRE_EQUAL(socks.sender.Send("a", 1, 0), 1);
+ - ]
154 : :
155 [ + - ]: 1 : waiter.join();
156 : 2 : }
157 : :
158 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(recv_until_terminator_limit)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
159 : : {
160 : 1 : constexpr auto timeout = 1min; // High enough so that it is never hit.
161 : 1 : CThreadInterrupt interrupt;
162 : :
163 [ + - ]: 1 : TcpSocketPair socks = TcpSocketPair{};
164 : :
165 : 2 : std::thread receiver([&socks, &timeout, &interrupt]() {
166 : 1 : constexpr size_t max_data{10};
167 : 1 : bool threw_as_expected{false};
168 : : // BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which
169 : : // creates a data race. So mimic it manually.
170 : 1 : try {
171 [ - + ]: 1 : (void)socks.receiver.RecvUntilTerminator('\n', timeout, interrupt, max_data);
172 [ - + ]: 1 : } catch (const std::runtime_error& e) {
173 [ + - ]: 1 : threw_as_expected = HasReason("too many bytes without a terminator")(e);
174 : 1 : }
175 [ - + ]: 1 : assert(threw_as_expected);
176 [ + - ]: 2 : });
177 : :
178 [ + - + - : 2 : BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("1234567", timeout, interrupt));
+ - - - -
- - - ]
179 [ + - + - : 2 : BOOST_REQUIRE_NO_THROW(socks.sender.SendComplete("89a\n", timeout, interrupt));
+ - - - -
- - - ]
180 : :
181 [ + - ]: 1 : receiver.join();
182 : 2 : }
183 : :
184 : : BOOST_AUTO_TEST_SUITE_END()
|