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