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