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