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