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