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