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