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