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