Branch data Line data Source code
1 : : // Copyright (c) 2024-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/pcp.h>
6 : : #include <netbase.h>
7 : : #include <test/util/logging.h>
8 : : #include <test/util/common.h>
9 : : #include <test/util/setup_common.h>
10 : : #include <util/time.h>
11 : :
12 : : #include <boost/test/unit_test.hpp>
13 : :
14 : : #include <algorithm>
15 : : #include <deque>
16 : :
17 : : using namespace std::literals;
18 : :
19 : : static CThreadInterrupt g_interrupt;
20 : :
21 : : /// UDP test server operation.
22 [ + - + - ]: 104 : struct TestOp {
23 : : std::chrono::milliseconds delay;
24 : : enum Op {
25 : : SEND, // Expect send (with optional data)
26 : : RECV, // Expect receive (with data)
27 : : NOP, // Just delay
28 : : } op;
29 : : std::vector<uint8_t> data;
30 : :
31 : : //! Injected error.
32 : : //! Set this field to a non-zero value to return the errno code from send or receive operation.
33 : : int error;
34 : :
35 : 26 : TestOp(std::chrono::milliseconds delay_in, Op op_in, const std::vector<uint8_t> &data_in, int error_in):
36 [ + - + - : 23 : delay(delay_in), op(op_in), data(data_in), error(error_in) {}
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ]
37 : : };
38 : :
39 : : /// Save the value of CreateSock and restore when the test ends.
40 : : class PCPTestingSetup : public BasicTestingSetup
41 : : {
42 : : public:
43 : 10 : explicit PCPTestingSetup(const ChainType chainType = ChainType::MAIN,
44 : : TestOpts opts = {})
45 : 10 : : BasicTestingSetup{chainType, opts},
46 [ + - + - : 20 : m_create_sock_orig{CreateSock}
+ - + - +
- + - ]
47 : : {
48 [ + - + - : 20 : const std::optional<CService> local_ipv4{Lookup("192.168.0.6", 1, false)};
+ - ]
49 [ + - + - : 20 : const std::optional<CService> local_ipv6{Lookup("2a10:1234:5678:9abc:def0:1234:5678:9abc", 1, false)};
+ - ]
50 [ + - + - : 20 : const std::optional<CService> gateway_ipv4{Lookup("192.168.0.1", 1, false)};
+ - ]
51 [ + - + - : 20 : const std::optional<CService> gateway_ipv6{Lookup("2a10:1234:5678:9abc:def0:0000:0000:0000", 1, false)};
+ - ]
52 [ + - + - : 20 : BOOST_REQUIRE(local_ipv4 && local_ipv6 && gateway_ipv4 && gateway_ipv6);
+ - + - -
+ + - ]
53 : 10 : default_local_ipv4 = *local_ipv4;
54 : 10 : default_local_ipv6 = *local_ipv6;
55 : 10 : default_gateway_ipv4 = *gateway_ipv4;
56 : 10 : default_gateway_ipv6 = *gateway_ipv6;
57 : :
58 : 10 : struct in_addr inaddr_any;
59 [ + - ]: 10 : inaddr_any.s_addr = htonl(INADDR_ANY);
60 [ + - ]: 10 : bind_any_ipv4 = CNetAddr(inaddr_any);
61 : 10 : }
62 : :
63 : 10 : ~PCPTestingSetup()
64 : 10 : {
65 : 10 : CreateSock = m_create_sock_orig;
66 : 10 : MockableSteadyClock::ClearMockTime();
67 : 10 : }
68 : :
69 : : // Default testing nonce.
70 : : static constexpr PCPMappingNonce TEST_NONCE{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc};
71 : : // Default network addresses.
72 : : CNetAddr default_local_ipv4;
73 : : CNetAddr default_local_ipv6;
74 : : CNetAddr default_gateway_ipv4;
75 : : CNetAddr default_gateway_ipv6;
76 : : // IPv4 bind
77 : : CNetAddr bind_any_ipv4;
78 : : private:
79 : : const decltype(CreateSock) m_create_sock_orig;
80 : : };
81 : :
82 : : /** Simple scripted UDP server emulation for testing.
83 : : */
84 : : class PCPTestSock final : public Sock
85 : : {
86 : : public:
87 : : // Note: we awkwardly mark all methods as const, and properties as mutable,
88 : : // because Sock expects all networking calls to be const.
89 : 11 : explicit PCPTestSock(const CNetAddr &local_ip, const CNetAddr &gateway_ip, const std::vector<TestOp> &script)
90 : 11 : : Sock{INVALID_SOCKET},
91 : 11 : m_script(script),
92 : 11 : m_local_ip(local_ip),
93 [ + - + - : 22 : m_gateway_ip(gateway_ip)
+ - ]
94 : : {
95 [ + - ]: 11 : PrepareOp();
96 : 11 : }
97 : :
98 : 0 : PCPTestSock& operator=(Sock&& other) override
99 : : {
100 : 0 : assert(false && "Move of Sock into PCPTestSock not allowed.");
101 : : return *this;
102 : : }
103 : :
104 : 16 : ssize_t Send(const void* data, size_t len, int) const override {
105 [ + - ]: 16 : if (!m_connected) return -1;
106 [ - + ]: 16 : std::span in_pkt = std::span(static_cast<const uint8_t*>(data), len);
107 [ - + ]: 13 : if (AtEndOfScript() || CurOp().op != TestOp::SEND) {
108 : : // Ignore sends after end of script, or sends when we expect a receive.
109 : 6 : FailScript();
110 : 3 : return len;
111 : : }
112 [ + - ]: 13 : if (CurOp().error) return -1; // Inject failure
113 [ + + - + ]: 13 : if (CurOp().data.empty() || std::ranges::equal(CurOp().data, in_pkt)) {
114 : 13 : AdvanceOp();
115 : : } else {
116 : : // Wrong send, fail script
117 : 0 : FailScript();
118 : : }
119 : 13 : return len;
120 : : }
121 : :
122 : 12 : ssize_t Recv(void* buf, size_t len, int flags) const override
123 : : {
124 [ + - + - : 24 : if (!m_connected || AtEndOfScript() || CurOp().op != TestOp::RECV || m_time_left != 0s) {
- + ]
125 : 0 : return -1;
126 : : }
127 [ + + ]: 12 : if (CurOp().error) return -1; // Inject failure
128 : 11 : const auto &recv_pkt = CurOp().data;
129 [ - + - + ]: 11 : const size_t consume_bytes{std::min(len, recv_pkt.size())};
130 [ + - ]: 11 : std::memcpy(buf, recv_pkt.data(), consume_bytes);
131 [ + - ]: 11 : if ((flags & MSG_PEEK) == 0) {
132 : 11 : AdvanceOp();
133 : : }
134 : 11 : return consume_bytes;
135 : : }
136 : :
137 : 11 : int Connect(const sockaddr* sa, socklen_t sa_len) const override {
138 : 11 : CService service;
139 [ + - + - : 22 : if (service.SetSockAddr(sa, sa_len) && service == CService(m_gateway_ip, 5351)) {
+ - + - +
- + - ]
140 [ + - + + ]: 11 : if (m_bound.IsBindAny()) { // If bind-any, bind to local ip.
141 [ + - ]: 9 : m_bound = CService(m_local_ip, 0);
142 : : }
143 [ + - + - ]: 11 : if (m_bound.GetPort() == 0) { // If no port assigned, assign port 1.
144 [ + - ]: 11 : m_bound = CService(m_bound, 1);
145 : : }
146 : 11 : m_connected = true;
147 : 11 : return 0;
148 : : }
149 : : return -1;
150 : 11 : }
151 : :
152 : 8 : int Bind(const sockaddr* sa, socklen_t sa_len) const override {
153 : 8 : CService service;
154 [ + - + - ]: 8 : if (service.SetSockAddr(sa, sa_len)) {
155 : : // Can only bind to one of our local ips
156 [ + - + + : 8 : if (!service.IsBindAny() && service != m_local_ip) {
+ - + - ]
157 : : return -1;
158 : : }
159 : 8 : m_bound = service;
160 : 8 : return 0;
161 : : }
162 : : return -1;
163 : 8 : }
164 : :
165 : 0 : int Listen(int) const override { return -1; }
166 : :
167 : 0 : std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override
168 : : {
169 : 0 : return nullptr;
170 : : };
171 : :
172 : 0 : int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
173 : : {
174 : 0 : return -1;
175 : : }
176 : :
177 : 0 : int SetSockOpt(int, int, const void*, socklen_t) const override { return 0; }
178 : :
179 : 11 : int GetSockName(sockaddr* name, socklen_t* name_len) const override
180 : : {
181 : : // Return the address we've been bound to.
182 [ - + ]: 11 : return m_bound.GetSockAddr(name, name_len) ? 0 : -1;
183 : : }
184 : :
185 : 0 : bool SetNonBlocking() const override { return true; }
186 : :
187 : 0 : bool IsSelectable() const override { return true; }
188 : :
189 : 16 : bool Wait(std::chrono::milliseconds timeout,
190 : : Event requested,
191 : : Event* occurred = nullptr) const override
192 : : {
193 : : // Only handles receive events.
194 [ + - ]: 13 : if (AtEndOfScript() || requested != Sock::RECV) {
195 : 3 : m_clock += timeout;
196 : : } else {
197 : 13 : std::chrono::milliseconds delay = std::min(m_time_left, timeout);
198 : 13 : m_clock += delay;
199 : 13 : m_time_left -= delay;
200 [ + + + - : 13 : if (CurOp().op == TestOp::RECV && m_time_left == 0s && occurred != nullptr) {
+ - ]
201 : 12 : *occurred = Sock::RECV;
202 : : }
203 [ + + ]: 13 : if (CurOp().op == TestOp::NOP) {
204 : : // This was a pure delay operation, move to the next op.
205 : 1 : AdvanceOp();
206 : : }
207 : : }
208 : 16 : return true;
209 : : }
210 : :
211 : 0 : bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override
212 : : {
213 : 0 : return false;
214 : : }
215 : :
216 : 0 : bool IsConnected(std::string&) const override
217 : : {
218 : 0 : return true;
219 : : }
220 : :
221 : : private:
222 : : const std::vector<TestOp> m_script;
223 : : mutable size_t m_script_ptr = 0;
224 : : mutable std::chrono::milliseconds m_time_left;
225 : : mutable FakeSteadyClock m_clock{};
226 : : mutable bool m_connected{false};
227 : : mutable CService m_bound;
228 : : mutable CNetAddr m_local_ip;
229 : : mutable CNetAddr m_gateway_ip;
230 : :
231 [ - + + + : 44 : bool AtEndOfScript() const { return m_script_ptr == m_script.size(); }
- + + - -
+ + + ]
232 : 138 : const TestOp &CurOp() const {
233 [ - + + - ]: 276 : BOOST_REQUIRE(m_script_ptr < m_script.size());
234 : 138 : return m_script[m_script_ptr];
235 : : }
236 : :
237 : 36 : void PrepareOp() const {
238 [ - + + + ]: 36 : if (AtEndOfScript()) return;
239 : 26 : m_time_left = CurOp().delay;
240 : : }
241 : :
242 : 25 : void AdvanceOp() const
243 : : {
244 : 25 : m_script_ptr += 1;
245 : 25 : PrepareOp();
246 : 25 : }
247 : :
248 [ - + - - ]: 3 : void FailScript() const { m_script_ptr = m_script.size(); }
249 : : };
250 : :
251 : : BOOST_FIXTURE_TEST_SUITE(pcp_tests, PCPTestingSetup)
252 : :
253 : : // NAT-PMP IPv4 good-weather scenario.
254 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(natpmp_ipv4)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
255 : : {
256 : 1 : const std::vector<TestOp> script{
257 : : {
258 : : 0ms, TestOp::SEND,
259 : : {
260 : : 0x00, 0x00, // version, opcode (request external IP)
261 : : }, 0
262 : : },
263 : : {
264 : : 2ms, TestOp::RECV,
265 : : {
266 : : 0x00, 0x80, 0x00, 0x00, // version, opcode (external IP), result code (success)
267 : : 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch
268 : : 0x01, 0x02, 0x03, 0x04, // external IP address
269 : : }, 0
270 : : },
271 : : {
272 : : 0ms, TestOp::SEND,
273 : : {
274 : : 0x00, 0x02, 0x00, 0x00, // version, opcode (request map TCP)
275 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
276 : : 0x00, 0x00, 0x03, 0xe8, // requested mapping lifetime in seconds
277 : : }, 0
278 : : },
279 : : {
280 : : 2ms, TestOp::RECV,
281 : : {
282 : : 0x00, 0x82, 0x00, 0x00, // version, opcode (mapped TCP)
283 : : 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch
284 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, mapped external port
285 : : 0x00, 0x00, 0x01, 0xf4, // mapping lifetime in seconds
286 : : }, 0
287 : : },
288 [ + + - - ]: 6 : };
289 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
290 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
291 : 0 : return std::unique_ptr<PCPTestSock>();
292 : 1 : };
293 : :
294 [ + - ]: 1 : auto res = NATPMPRequestPortMap(default_gateway_ipv4, 1234, 1000, g_interrupt, 1, 200ms);
295 : :
296 [ + - ]: 1 : MappingResult* mapping = std::get_if<MappingResult>(&res);
297 [ + - + - : 2 : BOOST_REQUIRE(mapping);
+ - ]
298 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mapping->version, 0);
299 [ + - + - : 1 : BOOST_CHECK_EQUAL(mapping->internal.ToStringAddrPort(), "192.168.0.6:1234");
+ - ]
300 [ + - + - : 1 : BOOST_CHECK_EQUAL(mapping->external.ToStringAddrPort(), "1.2.3.4:1234");
+ - ]
301 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mapping->lifetime, 500);
302 [ + - + - : 6 : }
+ - + - +
- - - ]
303 : :
304 : : // PCP IPv4 good-weather scenario.
305 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_ipv4)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
306 : : {
307 : 1 : const std::vector<TestOp> script{
308 : : {
309 : : 0ms, TestOp::SEND,
310 : : {
311 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
312 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
313 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP
314 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
315 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
316 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
317 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP
318 : : }, 0
319 : : },
320 : : {
321 : : 250ms, TestOp::RECV, // 250ms delay before answer
322 : : {
323 : : 0x02, 0x81, 0x00, 0x00, // version, opcode, result success
324 : : 0x00, 0x00, 0x01, 0xf4, // granted lifetime
325 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
326 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
327 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
328 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port
329 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, // assigned external IP
330 : : }, 0
331 : : },
332 [ + + - - ]: 4 : };
333 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
334 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
335 : 0 : return std::unique_ptr<PCPTestSock>();
336 : 1 : };
337 : :
338 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 1, 1000ms);
339 : :
340 [ + - ]: 1 : MappingResult* mapping = std::get_if<MappingResult>(&res);
341 [ + - + - : 2 : BOOST_REQUIRE(mapping);
+ - ]
342 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mapping->version, 2);
343 [ + - + - : 1 : BOOST_CHECK_EQUAL(mapping->internal.ToStringAddrPort(), "192.168.0.6:1234");
+ - ]
344 [ + - + - : 1 : BOOST_CHECK_EQUAL(mapping->external.ToStringAddrPort(), "1.2.3.4:1234");
+ - ]
345 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mapping->lifetime, 500);
346 [ + - + - : 4 : }
+ - - - ]
347 : :
348 : : // PCP IPv6 good-weather scenario.
349 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_ipv6)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
350 : : {
351 : 1 : const std::vector<TestOp> script{
352 : : {
353 : : 0ms, TestOp::SEND,
354 : : {
355 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
356 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
357 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // internal IP
358 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
359 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
360 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
361 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP
362 : : }, 0
363 : : },
364 : : {
365 : : 500ms, TestOp::RECV, // 500ms delay before answer
366 : : {
367 : : 0x02, 0x81, 0x00, 0x00, // version, opcode, result success
368 : : 0x00, 0x00, 0x01, 0xf4, // granted lifetime
369 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
370 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
371 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
372 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port
373 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP
374 : : }, 0
375 : : },
376 [ + + - - ]: 4 : };
377 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
378 [ + - + - ]: 1 : if (domain == AF_INET6 && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv6, default_gateway_ipv6, script);
379 : 0 : return std::unique_ptr<PCPTestSock>();
380 : 1 : };
381 : :
382 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv6, default_local_ipv6, 1234, 1000, g_interrupt, 1, 1000ms);
383 : :
384 [ + - ]: 1 : MappingResult* mapping = std::get_if<MappingResult>(&res);
385 [ + - + - : 2 : BOOST_REQUIRE(mapping);
+ - ]
386 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mapping->version, 2);
387 [ + - + - : 1 : BOOST_CHECK_EQUAL(mapping->internal.ToStringAddrPort(), "[2a10:1234:5678:9abc:def0:1234:5678:9abc]:1234");
+ - ]
388 [ + - + - : 1 : BOOST_CHECK_EQUAL(mapping->external.ToStringAddrPort(), "[2a10:1234:5678:9abc:def0:1234:5678:9abc]:1234");
+ - ]
389 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mapping->lifetime, 500);
390 [ + - + - : 4 : }
+ - - - ]
391 : :
392 : : // PCP timeout.
393 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_timeout)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
394 : : {
395 : 1 : const std::vector<TestOp> script{};
396 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
397 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
398 : 0 : return std::unique_ptr<PCPTestSock>();
399 : 1 : };
400 : :
401 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("pcp: Retrying (1)");
402 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("pcp: Retrying (2)");
403 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("pcp: Timeout");
404 : :
405 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 2000ms);
406 : :
407 [ + - ]: 1 : MappingError* err = std::get_if<MappingError>(&res);
408 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
409 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::NETWORK_ERROR);
410 : 1 : }
411 : :
412 : : // PCP failure receiving (router sends ICMP port closed).
413 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_connrefused)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
414 : : {
415 : 1 : const std::vector<TestOp> script{
416 : : {
417 : : 0ms, TestOp::SEND,
418 : : { // May send anything.
419 : : }, 0
420 : : },
421 : : {
422 : : 0ms, TestOp::RECV,
423 : : {
424 : : }, ECONNREFUSED
425 : : },
426 [ + + - - ]: 4 : };
427 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
428 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
429 : 0 : return std::unique_ptr<PCPTestSock>();
430 : 1 : };
431 : :
432 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("pcp: Could not receive response");
433 : :
434 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 2000ms);
435 : :
436 [ + - ]: 1 : MappingError* err = std::get_if<MappingError>(&res);
437 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
438 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::NETWORK_ERROR);
439 [ + - + - : 2 : }
+ - - - ]
440 : :
441 : : // PCP IPv6 success after one timeout.
442 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_ipv6_timeout_success)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
443 : : {
444 : 1 : const std::vector<TestOp> script{
445 : : {
446 : : 0ms, TestOp::SEND,
447 : : {
448 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
449 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
450 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // internal IP
451 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
452 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
453 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
454 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP
455 : : }, 0
456 : : },
457 : : {
458 : : 2001ms, TestOp::NOP, // Takes longer to respond than timeout of 2000ms
459 : : {}, 0
460 : : },
461 : : {
462 : : 0ms, TestOp::SEND, // Repeated send (try 2)
463 : : {
464 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
465 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
466 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // internal IP
467 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
468 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
469 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
470 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP
471 : : }, 0
472 : : },
473 : : {
474 : : 200ms, TestOp::RECV, // This time we're in time
475 : : {
476 : : 0x02, 0x81, 0x00, 0x00, // version, opcode, result success
477 : : 0x00, 0x00, 0x01, 0xf4, // granted lifetime
478 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
479 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
480 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
481 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port
482 : : 0x2a, 0x10, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, // suggested external IP
483 : : }, 0
484 : : },
485 [ + + - - ]: 6 : };
486 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
487 [ + - + - ]: 1 : if (domain == AF_INET6 && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv6, default_gateway_ipv6, script);
488 : 0 : return std::unique_ptr<PCPTestSock>();
489 : 1 : };
490 : :
491 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("pcp: Retrying (1)");
492 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("pcp: Timeout");
493 : :
494 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv6, default_local_ipv6, 1234, 1000, g_interrupt, 2, 2000ms);
495 : :
496 [ + - + - : 3 : BOOST_CHECK(std::get_if<MappingResult>(&res));
+ - ]
497 [ + - + - : 5 : }
+ - + - +
- - - ]
498 : :
499 : : // PCP IPv4 failure (no resources).
500 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_ipv4_fail_no_resources)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
501 : : {
502 : 1 : const std::vector<TestOp> script{
503 : : {
504 : : 0ms, TestOp::SEND,
505 : : {
506 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
507 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
508 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP
509 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
510 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
511 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
512 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP
513 : : }, 0
514 : : },
515 : : {
516 : : 500ms, TestOp::RECV,
517 : : {
518 : : 0x02, 0x81, 0x00, 0x08, // version, opcode, result 0x08: no resources
519 : : 0x00, 0x00, 0x00, 0x00, // granted lifetime
520 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
521 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
522 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
523 : : 0x04, 0xd2, 0x00, 0x00, // internal port, assigned external port
524 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // assigned external IP
525 : : }, 0
526 : : },
527 [ + + - - ]: 4 : };
528 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
529 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
530 : 0 : return std::unique_ptr<PCPTestSock>();
531 : 1 : };
532 : :
533 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 1000ms);
534 : :
535 [ + - ]: 1 : MappingError* err = std::get_if<MappingError>(&res);
536 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
537 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::NO_RESOURCES);
538 [ + - + - : 4 : }
+ - - - ]
539 : :
540 : : // PCP IPv4 failure (test NATPMP downgrade scenario).
541 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_ipv4_fail_unsupported_version)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
542 : : {
543 : 1 : const std::vector<TestOp> script{
544 : : {
545 : : 0ms, TestOp::SEND,
546 : : {
547 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
548 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
549 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP
550 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
551 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
552 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
553 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP
554 : : }, 0
555 : : },
556 : : {
557 : : 500ms, TestOp::RECV,
558 : : {
559 : : 0x00, 0x81, 0x00, 0x01, // version, opcode, result 0x01: unsupported version
560 : : 0x00, 0x00, 0x00, 0x00,
561 : : }, 0
562 : : },
563 [ + + - - ]: 4 : };
564 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
565 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
566 : 0 : return std::unique_ptr<PCPTestSock>();
567 : 1 : };
568 : :
569 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 3, 1000ms);
570 : :
571 [ + - ]: 1 : MappingError* err = std::get_if<MappingError>(&res);
572 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
573 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::UNSUPP_VERSION);
574 [ + - + - : 4 : }
+ - - - ]
575 : :
576 : : // NAT-PMP IPv4 protocol error scenarii.
577 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(natpmp_protocol_error)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
578 : : {
579 : : // First scenario: non-0 result code when requesting external IP.
580 : 1 : std::vector<TestOp> script{
581 : : {
582 : : 0ms, TestOp::SEND,
583 : : {
584 : : 0x00, 0x00, // version, opcode (request external IP)
585 : : }, 0
586 : : },
587 : : {
588 : : 2ms, TestOp::RECV,
589 : : {
590 : : 0x00, 0x80, 0x00, 0x42, // version, opcode (external IP), result code (*NOT* success)
591 : : 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch
592 : : 0x01, 0x02, 0x03, 0x04, // external IP address
593 : : }, 0
594 : : },
595 [ + + - - ]: 4 : };
596 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
597 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
598 : 0 : return std::unique_ptr<PCPTestSock>();
599 : 1 : };
600 : :
601 [ + - ]: 1 : auto res = NATPMPRequestPortMap(default_gateway_ipv4, 1234, 1000, g_interrupt, 1, 200ms);
602 : :
603 [ + - ]: 1 : MappingError* err = std::get_if<MappingError>(&res);
604 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
605 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::PROTOCOL_ERROR);
606 : :
607 : : // First scenario: non-0 result code when requesting port mapping.
608 : 1 : script = {
609 : : {
610 : : 0ms, TestOp::SEND,
611 : : {
612 : : 0x00, 0x00, // version, opcode (request external IP)
613 : : }, 0
614 : : },
615 : : {
616 : : 2ms, TestOp::RECV,
617 : : {
618 : : 0x00, 0x80, 0x00, 0x00, // version, opcode (external IP), result code (success)
619 : : 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch
620 : : 0x01, 0x02, 0x03, 0x04, // external IP address
621 : : }, 0
622 : : },
623 : : {
624 : : 0ms, TestOp::SEND,
625 : : {
626 : : 0x00, 0x02, 0x00, 0x00, // version, opcode (request map TCP)
627 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
628 : : 0x00, 0x00, 0x03, 0xe8, // requested mapping lifetime in seconds
629 : : }, 0
630 : : },
631 : : {
632 : : 2ms, TestOp::RECV,
633 : : {
634 : : 0x00, 0x82, 0x00, 0x43, // version, opcode (mapped TCP)
635 : : 0x66, 0xfd, 0xa1, 0xee, // seconds sinds start of epoch
636 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, mapped external port
637 : : 0x00, 0x00, 0x01, 0xf4, // mapping lifetime in seconds
638 : : }, 0
639 : : },
640 [ + + - - ]: 6 : };
641 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
642 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
643 : 0 : return std::unique_ptr<PCPTestSock>();
644 : 1 : };
645 : :
646 [ + - ]: 2 : res = NATPMPRequestPortMap(default_gateway_ipv4, 1234, 1000, g_interrupt, 1, 200ms);
647 : :
648 [ + - ]: 1 : err = std::get_if<MappingError>(&res);
649 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
650 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::PROTOCOL_ERROR);
651 [ + - + - : 9 : }
+ - + - +
- + - + -
+ - - - -
- ]
652 : :
653 : : // PCP IPv4 protocol error scenario.
654 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(pcp_protocol_error)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
655 : : {
656 : 1 : const std::vector<TestOp> script{
657 : : {
658 : : 0ms, TestOp::SEND,
659 : : {
660 : : 0x02, 0x01, 0x00, 0x00, // version, opcode
661 : : 0x00, 0x00, 0x03, 0xe8, // lifetime
662 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x06, // internal IP
663 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
664 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
665 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, suggested external port
666 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // suggested external IP
667 : : }, 0
668 : : },
669 : : {
670 : : 250ms, TestOp::RECV, // 250ms delay before answer
671 : : {
672 : : 0x02, 0x81, 0x00, 0x42, // version, opcode, result error
673 : : 0x00, 0x00, 0x01, 0xf4, // granted lifetime
674 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
675 : : 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, // nonce
676 : : 0x06, 0x00, 0x00, 0x00, // protocol (TCP), reserved
677 : : 0x04, 0xd2, 0x04, 0xd2, // internal port, assigned external port
678 : : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, // assigned external IP
679 : : }, 0
680 : : },
681 [ + + - - ]: 4 : };
682 : 2 : CreateSock = [this, &script](int domain, int type, int protocol) {
683 [ + - + - ]: 1 : if (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP) return std::make_unique<PCPTestSock>(default_local_ipv4, default_gateway_ipv4, script);
684 : 0 : return std::unique_ptr<PCPTestSock>();
685 : 1 : };
686 : :
687 [ + - ]: 1 : auto res = PCPRequestPortMap(TEST_NONCE, default_gateway_ipv4, bind_any_ipv4, 1234, 1000, g_interrupt, 1, 1000ms);
688 : :
689 [ + - ]: 1 : MappingError* err = std::get_if<MappingError>(&res);
690 [ + - + - : 2 : BOOST_REQUIRE(err);
+ - ]
691 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(*err, MappingError::PROTOCOL_ERROR);
692 [ + - + - : 4 : }
+ - - - ]
693 : :
694 : : BOOST_AUTO_TEST_SUITE_END()
695 : :
|