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