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