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 https://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <common/pcp.h>
6 : :
7 : : #include <compat/compat.h>
8 : : #include <crypto/common.h>
9 : : #include <crypto/hex_base.h>
10 : : #include <netaddress.h>
11 : : #include <netbase.h>
12 : : #include <tinyformat.h>
13 : : #include <util/check.h>
14 : : #include <util/log.h>
15 : : #include <util/sock.h>
16 : : #include <util/string.h>
17 : : #include <util/threadinterrupt.h>
18 : : #include <util/time.h>
19 : :
20 : : #include <algorithm>
21 : : #include <atomic>
22 : : #include <compare>
23 : : #include <cstring>
24 : : #include <functional>
25 : : #include <map>
26 : : #include <memory>
27 : : #include <optional>
28 : : #include <span>
29 : : #include <utility>
30 : : #include <vector>
31 : :
32 : : namespace {
33 : :
34 : : // RFC6886 NAT-PMP and RFC6887 Port Control Protocol (PCP) implementation.
35 : : // NAT-PMP and PCP use network byte order (big-endian).
36 : :
37 : : // NAT-PMP (v0) protocol constants.
38 : : //! NAT-PMP uses a fixed server port number (RFC6887 section 1.1).
39 : : constexpr uint16_t NATPMP_SERVER_PORT = 5351;
40 : : //! Version byte for NATPMP (RFC6886 1.1)
41 : : constexpr uint8_t NATPMP_VERSION = 0;
42 : : //! Request opcode base (RFC6886 3).
43 : : constexpr uint8_t NATPMP_REQUEST = 0x00;
44 : : //! Response opcode base (RFC6886 3).
45 : : constexpr uint8_t NATPMP_RESPONSE = 0x80;
46 : : //! Get external address (RFC6886 3.2)
47 : : constexpr uint8_t NATPMP_OP_GETEXTERNAL = 0x00;
48 : : //! Map TCP port (RFC6886 3.3)
49 : : constexpr uint8_t NATPMP_OP_MAP_TCP = 0x02;
50 : : //! Shared request header size in bytes.
51 : : constexpr size_t NATPMP_REQUEST_HDR_SIZE = 2;
52 : : //! Shared response header (minimum) size in bytes.
53 : : constexpr size_t NATPMP_RESPONSE_HDR_SIZE = 8;
54 : : //! GETEXTERNAL request size in bytes, including header (RFC6886 3.2).
55 : : constexpr size_t NATPMP_GETEXTERNAL_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 0;
56 : : //! GETEXTERNAL response size in bytes, including header (RFC6886 3.2).
57 : : constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 4;
58 : : //! MAP request size in bytes, including header (RFC6886 3.3).
59 : : constexpr size_t NATPMP_MAP_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 10;
60 : : //! MAP response size in bytes, including header (RFC6886 3.3).
61 : : constexpr size_t NATPMP_MAP_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 8;
62 : :
63 : : // Shared header offsets (RFC6886 3.2, 3.3), relative to start of packet.
64 : : //! Offset of version field in packets.
65 : : constexpr size_t NATPMP_HDR_VERSION_OFS = 0;
66 : : //! Offset of opcode field in packets
67 : : constexpr size_t NATPMP_HDR_OP_OFS = 1;
68 : : //! Offset of result code in packets. Result codes are 16 bit in NAT-PMP instead of 8 bit in PCP.
69 : : constexpr size_t NATPMP_RESPONSE_HDR_RESULT_OFS = 2;
70 : :
71 : : // GETEXTERNAL response offsets (RFC6886 3.2), relative to start of packet.
72 : : //! Returned external address
73 : : constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_IP_OFS = 8;
74 : :
75 : : // MAP request offsets (RFC6886 3.3), relative to start of packet.
76 : : //! Internal port to be mapped.
77 : : constexpr size_t NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS = 4;
78 : : //! Suggested external port for mapping.
79 : : constexpr size_t NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS = 6;
80 : : //! Requested port mapping lifetime in seconds.
81 : : constexpr size_t NATPMP_MAP_REQUEST_LIFETIME_OFS = 8;
82 : :
83 : : // MAP response offsets (RFC6886 3.3), relative to start of packet.
84 : : //! Internal port for mapping (will match internal port of request).
85 : : constexpr size_t NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS = 8;
86 : : //! External port for mapping.
87 : : constexpr size_t NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS = 10;
88 : : //! Created port mapping lifetime in seconds.
89 : : constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12;
90 : :
91 : : // Relevant NETPMP result codes (RFC6886 3.5).
92 : : //! Result code representing success status.
93 : : constexpr uint8_t NATPMP_RESULT_SUCCESS = 0;
94 : : //! Result code representing unsupported version.
95 : : constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1;
96 : : //! Result code representing not authorized (router doesn't support port mapping).
97 : : constexpr uint8_t NATPMP_RESULT_NOT_AUTHORIZED = 2;
98 : : //! Result code representing lack of resources.
99 : : constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4;
100 : :
101 : : //! Mapping of NATPMP result code to string (RFC6886 3.5). Result codes <=2 match PCP.
102 : : const std::map<uint16_t, std::string> NATPMP_RESULT_STR{
103 : : {0, "SUCCESS"},
104 : : {1, "UNSUPP_VERSION"},
105 : : {2, "NOT_AUTHORIZED"},
106 : : {3, "NETWORK_FAILURE"},
107 : : {4, "NO_RESOURCES"},
108 : : {5, "UNSUPP_OPCODE"},
109 : : };
110 : :
111 : : // PCP (v2) protocol constants.
112 : : //! Maximum packet size in bytes (RFC6887 section 7).
113 : : constexpr size_t PCP_MAX_SIZE = 1100;
114 : : //! PCP uses a fixed server port number (RFC6887 section 19.1). Shared with NAT-PMP.
115 : : constexpr uint16_t PCP_SERVER_PORT = NATPMP_SERVER_PORT;
116 : : //! Version byte. 0 is NAT-PMP (RFC6886), 1 is forbidden, 2 for PCP (RFC6887).
117 : : constexpr uint8_t PCP_VERSION = 2;
118 : : //! PCP Request Header. See RFC6887 section 7.1. Shared with NAT-PMP.
119 : : constexpr uint8_t PCP_REQUEST = NATPMP_REQUEST; // R = 0
120 : : //! PCP Response Header. See RFC6887 section 7.2. Shared with NAT-PMP.
121 : : constexpr uint8_t PCP_RESPONSE = NATPMP_RESPONSE; // R = 1
122 : : //! Map opcode. See RFC6887 section 19.2
123 : : constexpr uint8_t PCP_OP_MAP = 0x01;
124 : : //! TCP protocol number (IANA).
125 : : constexpr uint16_t PCP_PROTOCOL_TCP = 6;
126 : : //! Request and response header size in bytes (RFC6887 section 7.1).
127 : : constexpr size_t PCP_HDR_SIZE = 24;
128 : : //! Map request and response size in bytes (RFC6887 section 11.1).
129 : : constexpr size_t PCP_MAP_SIZE = 36;
130 : :
131 : : // Header offsets shared between request and responses (RFC6887 7.1, 7.2), relative to start of packet.
132 : : //! Version field (1 byte).
133 : : constexpr size_t PCP_HDR_VERSION_OFS = NATPMP_HDR_VERSION_OFS;
134 : : //! Opcode field (1 byte).
135 : : constexpr size_t PCP_HDR_OP_OFS = NATPMP_HDR_OP_OFS;
136 : : //! Requested lifetime (request), granted lifetime (response) (4 bytes).
137 : : constexpr size_t PCP_HDR_LIFETIME_OFS = 4;
138 : :
139 : : // Request header offsets (RFC6887 7.1), relative to start of packet.
140 : : //! PCP client's IP address (16 bytes).
141 : : constexpr size_t PCP_REQUEST_HDR_IP_OFS = 8;
142 : :
143 : : // Response header offsets (RFC6887 7.2), relative to start of packet.
144 : : //! Result code (1 byte).
145 : : constexpr size_t PCP_RESPONSE_HDR_RESULT_OFS = 3;
146 : :
147 : : // MAP request/response offsets (RFC6887 11.1), relative to start of opcode-specific data.
148 : : //! Mapping nonce (12 bytes).
149 : : constexpr size_t PCP_MAP_NONCE_OFS = 0;
150 : : //! Protocol (1 byte).
151 : : constexpr size_t PCP_MAP_PROTOCOL_OFS = 12;
152 : : //! Internal port for mapping (2 bytes).
153 : : constexpr size_t PCP_MAP_INTERNAL_PORT_OFS = 16;
154 : : //! Suggested external port (request), assigned external port (response) (2 bytes).
155 : : constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18;
156 : : //! Suggested external IP (request), assigned external IP (response) (16 bytes).
157 : : constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20;
158 : :
159 : : //! Result code representing success (RFC6887 7.4), shared with NAT-PMP.
160 : : constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS;
161 : : //! Result code representing not authorized (RFC6887 7.4), shared with NAT-PMP.
162 : : constexpr uint8_t PCP_RESULT_NOT_AUTHORIZED = NATPMP_RESULT_NOT_AUTHORIZED;
163 : : //! Result code representing lack of resources (RFC6887 7.4).
164 : : constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8;
165 : :
166 : : //! Mapping of PCP result code to string (RFC6887 7.4). Result codes <=2 match NAT-PMP.
167 : : const std::map<uint8_t, std::string> PCP_RESULT_STR{
168 : : {0, "SUCCESS"},
169 : : {1, "UNSUPP_VERSION"},
170 : : {2, "NOT_AUTHORIZED"},
171 : : {3, "MALFORMED_REQUEST"},
172 : : {4, "UNSUPP_OPCODE"},
173 : : {5, "UNSUPP_OPTION"},
174 : : {6, "MALFORMED_OPTION"},
175 : : {7, "NETWORK_FAILURE"},
176 : : {8, "NO_RESOURCES"},
177 : : {9, "UNSUPP_PROTOCOL"},
178 : : {10, "USER_EX_QUOTA"},
179 : : {11, "CANNOT_PROVIDE_EXTERNAL"},
180 : : {12, "ADDRESS_MISMATCH"},
181 : : {13, "EXCESSIVE_REMOTE_PEER"},
182 : : };
183 : :
184 : : //! Return human-readable string from NATPMP result code.
185 : 2 : std::string NATPMPResultString(uint16_t result_code)
186 : : {
187 : 2 : auto result_i = NATPMP_RESULT_STR.find(result_code);
188 [ + - - - : 4 : return strprintf("%s (code %d)", result_i == NATPMP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code);
+ - ]
189 : : }
190 : :
191 : : //! Return human-readable string from PCP result code.
192 : 2 : std::string PCPResultString(uint8_t result_code)
193 : : {
194 : 2 : auto result_i = PCP_RESULT_STR.find(result_code);
195 [ + + - + : 5 : return strprintf("%s (code %d)", result_i == PCP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code);
+ - ]
196 : : }
197 : :
198 : : //! Wrap address in IPv6 according to RFC6887. wrapped_addr needs to be able to store 16 bytes.
199 : 16 : [[nodiscard]] bool PCPWrapAddress(std::span<uint8_t> wrapped_addr, const CNetAddr &addr)
200 : : {
201 [ + + ]: 16 : Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
202 [ + + ]: 16 : if (addr.IsIPv4()) {
203 : 12 : struct in_addr addr4;
204 [ + - ]: 12 : if (!addr.GetInAddr(&addr4)) return false;
205 : : // Section 5: "When the address field holds an IPv4 address, an IPv4-mapped IPv6 address [RFC4291] is used (::ffff:0:0/96)."
206 : 12 : std::memcpy(wrapped_addr.data(), IPV4_IN_IPV6_PREFIX.data(), IPV4_IN_IPV6_PREFIX.size());
207 : 12 : std::memcpy(wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), &addr4, ADDR_IPV4_SIZE);
208 : 12 : return true;
209 [ + - ]: 4 : } else if (addr.IsIPv6()) {
210 : 4 : struct in6_addr addr6;
211 [ + - ]: 4 : if (!addr.GetIn6Addr(&addr6)) return false;
212 : 4 : std::memcpy(wrapped_addr.data(), &addr6, ADDR_IPV6_SIZE);
213 : 4 : return true;
214 : : } else {
215 : : return false;
216 : : }
217 : : }
218 : :
219 : : //! Unwrap PCP-encoded address according to RFC6887.
220 : 5 : CNetAddr PCPUnwrapAddress(std::span<const uint8_t> wrapped_addr)
221 : : {
222 [ + + ]: 5 : Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
223 [ + + ]: 5 : if (util::HasPrefix(wrapped_addr, IPV4_IN_IPV6_PREFIX)) {
224 : 2 : struct in_addr addr4;
225 : 2 : std::memcpy(&addr4, wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), ADDR_IPV4_SIZE);
226 : 2 : return CNetAddr(addr4);
227 : : } else {
228 : 3 : struct in6_addr addr6;
229 : 3 : std::memcpy(&addr6, wrapped_addr.data(), ADDR_IPV6_SIZE);
230 : 3 : return CNetAddr(addr6);
231 : : }
232 : : }
233 : :
234 : : //! PCP or NAT-PMP send-receive loop.
235 : 13 : std::optional<std::vector<uint8_t>> PCPSendRecv(Sock &sock, const std::string &protocol, std::span<const uint8_t> request, int num_tries,
236 : : std::chrono::milliseconds timeout_per_try,
237 : : std::function<bool(std::span<const uint8_t>)> check_packet,
238 : : CThreadInterrupt& interrupt)
239 : : {
240 : 13 : using namespace std::chrono;
241 : : // UDP is a potentially lossy protocol, so we try to send again a few times.
242 : 13 : uint8_t response[PCP_MAX_SIZE];
243 : 13 : bool got_response = false;
244 : 13 : int recvsz = 0;
245 [ + + + + ]: 28 : for (int ntry = 0; !got_response && ntry < num_tries; ++ntry) {
246 [ + + ]: 16 : if (ntry > 0) {
247 [ + - ]: 3 : LogDebug(BCLog::NET, "%s: Retrying (%d)\n", protocol, ntry);
248 : : }
249 : : // Dispatch packet to gateway.
250 [ - + ]: 16 : if (sock.Send(request.data(), request.size(), 0) != static_cast<ssize_t>(request.size())) {
251 [ # # # # ]: 0 : LogDebug(BCLog::NET, "%s: Could not send request: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
252 : 0 : return std::nullopt; // Network-level error, probably no use retrying.
253 : : }
254 : :
255 : : // Wait for response(s) until we get a valid response, a network error, or time out.
256 : 16 : auto cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now());
257 : 16 : auto deadline = cur_time + timeout_per_try;
258 [ + - ]: 16 : while ((cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now())) < deadline) {
259 [ - + ]: 16 : if (interrupt) return std::nullopt;
260 : 16 : Sock::Event occurred = 0;
261 [ - + ]: 16 : if (!sock.Wait(deadline - cur_time, Sock::RecvEvent, &occurred)) {
262 [ # # ]: 0 : LogWarning("%s: Could not wait on socket: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
263 : 0 : return std::nullopt; // Network-level error, probably no use retrying.
264 : : }
265 [ + + ]: 16 : if (!occurred) {
266 [ + - ]: 4 : LogDebug(BCLog::NET, "%s: Timeout\n", protocol);
267 : : break; // Retry.
268 : : }
269 : :
270 : : // Receive response.
271 : 12 : recvsz = sock.Recv(response, sizeof(response), MSG_DONTWAIT);
272 [ + + ]: 12 : if (recvsz < 0) {
273 [ + - + - ]: 1 : LogDebug(BCLog::NET, "%s: Could not receive response: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
274 : 1 : return std::nullopt; // Network-level error, probably no use retrying.
275 : : }
276 [ + - + - ]: 11 : LogDebug(BCLog::NET, "%s: Received response of %d bytes: %s\n", protocol, recvsz, HexStr(std::span(response, recvsz)));
277 : :
278 [ - + ]: 11 : if (check_packet(std::span<uint8_t>(response, recvsz))) {
279 : : got_response = true; // Got expected response, break from receive loop as well as from retry loop.
280 : : break;
281 : : }
282 : : }
283 : : }
284 [ + + ]: 12 : if (!got_response) {
285 [ + - ]: 1 : LogDebug(BCLog::NET, "%s: Giving up after %d tries\n", protocol, num_tries);
286 : 1 : return std::nullopt;
287 : : }
288 : 11 : return std::vector<uint8_t>(response, response + recvsz);
289 : : }
290 : :
291 : : }
292 : :
293 : 3 : std::variant<MappingResult, MappingError> NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime, CThreadInterrupt& interrupt, int num_tries, std::chrono::milliseconds timeout_per_try)
294 : : {
295 : 3 : struct sockaddr_storage dest_addr;
296 : 3 : socklen_t dest_addrlen = sizeof(struct sockaddr_storage);
297 : :
298 [ + - + - ]: 3 : LogDebug(BCLog::NET, "natpmp: Requesting port mapping port %d from gateway %s\n", port, gateway.ToStringAddr());
299 : :
300 : : // Validate gateway, make sure it's IPv4. NAT-PMP does not support IPv6.
301 [ + - - + ]: 3 : if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR;
302 [ - + ]: 3 : if (dest_addr.ss_family != AF_INET) return MappingError::NETWORK_ERROR;
303 : :
304 : : // Create IPv4 UDP socket
305 : 3 : auto sock{CreateSock(AF_INET, SOCK_DGRAM, IPPROTO_UDP)};
306 [ - + ]: 3 : if (!sock) {
307 [ # # # # ]: 0 : LogWarning("natpmp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
308 : 0 : return MappingError::NETWORK_ERROR;
309 : : }
310 : :
311 : : // Associate UDP socket to gateway.
312 [ + - - + ]: 3 : if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
313 [ # # # # ]: 0 : LogWarning("natpmp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
314 : 0 : return MappingError::NETWORK_ERROR;
315 : : }
316 : :
317 : : // Use getsockname to get the address toward the default gateway (the internal address).
318 : 3 : struct sockaddr_in internal;
319 : 3 : socklen_t internal_addrlen = sizeof(struct sockaddr_in);
320 [ + - - + ]: 3 : if (sock->GetSockName((struct sockaddr*)&internal, &internal_addrlen) != 0) {
321 [ # # # # ]: 0 : LogWarning("natpmp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
322 : 0 : return MappingError::NETWORK_ERROR;
323 : : }
324 : :
325 : : // Request external IP address (RFC6886 section 3.2).
326 [ + - ]: 3 : std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE);
327 [ - + ]: 3 : request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
328 : 3 : request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_GETEXTERNAL;
329 : :
330 [ - + + - ]: 6 : auto recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
331 [ - + ]: 3 : [&](const std::span<const uint8_t> response) -> bool {
332 [ - + ]: 3 : if (response.size() < NATPMP_GETEXTERNAL_RESPONSE_SIZE) {
333 : 0 : LogWarning("natpmp: Response too small\n");
334 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
335 : : }
336 [ + - - + ]: 3 : if (response[NATPMP_HDR_VERSION_OFS] != NATPMP_VERSION || response[NATPMP_HDR_OP_OFS] != (NATPMP_RESPONSE | NATPMP_OP_GETEXTERNAL)) {
337 : 0 : LogWarning("natpmp: Response to wrong command\n");
338 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
339 : : }
340 : : return true;
341 : : },
342 [ + - ]: 3 : interrupt);
343 : :
344 : 3 : struct in_addr external_addr;
345 [ + - ]: 3 : if (recv_res) {
346 [ - + ]: 3 : const std::span<const uint8_t> response = *recv_res;
347 : :
348 [ + + ]: 3 : Assume(response.size() >= NATPMP_GETEXTERNAL_RESPONSE_SIZE);
349 [ + + ]: 3 : uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
350 [ + + ]: 3 : if (result_code != NATPMP_RESULT_SUCCESS) {
351 [ + - + - ]: 1 : LogWarning("natpmp: Getting external address failed with result %s\n", NATPMPResultString(result_code));
352 : 1 : return MappingError::PROTOCOL_ERROR;
353 : : }
354 : :
355 [ + - ]: 2 : std::memcpy(&external_addr, response.data() + NATPMP_GETEXTERNAL_RESPONSE_IP_OFS, ADDR_IPV4_SIZE);
356 : : } else {
357 : 0 : return MappingError::NETWORK_ERROR;
358 : : }
359 : :
360 : : // Create TCP mapping request (RFC6886 section 3.3).
361 [ + - ]: 4 : request = std::vector<uint8_t>(NATPMP_MAP_REQUEST_SIZE);
362 : 2 : request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
363 : 2 : request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_MAP_TCP;
364 : 2 : WriteBE16(request.data() + NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS, port);
365 : 2 : WriteBE16(request.data() + NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS, port);
366 : 2 : WriteBE32(request.data() + NATPMP_MAP_REQUEST_LIFETIME_OFS, lifetime);
367 : :
368 [ + - + - ]: 4 : recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
369 [ - + ]: 2 : [&](const std::span<const uint8_t> response) -> bool {
370 [ - + ]: 2 : if (response.size() < NATPMP_MAP_RESPONSE_SIZE) {
371 : 0 : LogWarning("natpmp: Response too small\n");
372 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
373 : : }
374 [ + - - + ]: 2 : if (response[0] != NATPMP_VERSION || response[1] != (NATPMP_RESPONSE | NATPMP_OP_MAP_TCP)) {
375 : 0 : LogWarning("natpmp: Response to wrong command\n");
376 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
377 : : }
378 [ - + ]: 2 : uint16_t internal_port = ReadBE16(response.data() + NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS);
379 [ - + ]: 2 : if (internal_port != port) {
380 : 0 : LogWarning("natpmp: Response port doesn't match request\n");
381 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
382 : : }
383 : : return true;
384 : : },
385 : 2 : interrupt);
386 : :
387 [ + - ]: 2 : if (recv_res) {
388 [ - + ]: 2 : const std::span<uint8_t> response = *recv_res;
389 : :
390 [ + + ]: 2 : Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE);
391 [ + + ]: 2 : uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
392 [ + + ]: 2 : if (result_code != NATPMP_RESULT_SUCCESS) {
393 [ - + ]: 1 : if (result_code == NATPMP_RESULT_NOT_AUTHORIZED) {
394 : 0 : static std::atomic<bool> warned{false};
395 [ # # ]: 0 : if (!warned.exchange(true)) {
396 [ # # # # ]: 0 : LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
397 : : } else {
398 [ # # # # : 0 : LogDebug(BCLog::NET, "natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
# # # # ]
399 : : }
400 : : } else {
401 [ + - + - ]: 1 : LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
402 : : }
403 [ - + ]: 1 : if (result_code == NATPMP_RESULT_NO_RESOURCES) {
404 : 0 : return MappingError::NO_RESOURCES;
405 : : }
406 : 1 : return MappingError::PROTOCOL_ERROR;
407 : : }
408 : :
409 [ + - ]: 1 : uint32_t lifetime_ret = ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS);
410 [ + - ]: 1 : uint16_t external_port = ReadBE16(response.data() + NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS);
411 [ + - + - ]: 2 : return MappingResult(NATPMP_VERSION, CService(internal.sin_addr, port), CService(external_addr, external_port), lifetime_ret);
412 : : } else {
413 : 0 : return MappingError::NETWORK_ERROR;
414 : : }
415 : 3 : }
416 : :
417 : 8 : std::variant<MappingResult, MappingError> PCPRequestPortMap(const PCPMappingNonce &nonce, const CNetAddr &gateway, const CNetAddr &bind, uint16_t port, uint32_t lifetime, CThreadInterrupt& interrupt, int num_tries, std::chrono::milliseconds timeout_per_try)
418 : : {
419 : 8 : struct sockaddr_storage dest_addr, bind_addr;
420 : 8 : socklen_t dest_addrlen = sizeof(struct sockaddr_storage), bind_addrlen = sizeof(struct sockaddr_storage);
421 : :
422 [ + - + - : 8 : LogDebug(BCLog::NET, "pcp: Requesting port mapping for addr %s port %d from gateway %s\n", bind.ToStringAddr(), port, gateway.ToStringAddr());
+ - ]
423 : :
424 : : // Validate addresses, make sure they're the same network family.
425 [ + - - + ]: 8 : if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR;
426 [ + - - + ]: 8 : if (!CService(bind, 0).GetSockAddr((struct sockaddr*)&bind_addr, &bind_addrlen)) return MappingError::NETWORK_ERROR;
427 [ - + ]: 8 : if (dest_addr.ss_family != bind_addr.ss_family) return MappingError::NETWORK_ERROR;
428 : :
429 : : // Create UDP socket (IPv4 or IPv6 based on provided gateway).
430 : 8 : auto sock{CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)};
431 [ - + ]: 8 : if (!sock) {
432 [ # # # # ]: 0 : LogWarning("pcp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
433 : 0 : return MappingError::NETWORK_ERROR;
434 : : }
435 : :
436 : : // Make sure that we send from requested destination address, anything else will be
437 : : // rejected by a security-conscious router.
438 [ + - - + ]: 8 : if (sock->Bind((struct sockaddr*)&bind_addr, bind_addrlen) != 0) {
439 [ # # # # ]: 0 : LogWarning("pcp: Could not bind to address: %s\n", NetworkErrorString(WSAGetLastError()));
440 : 0 : return MappingError::NETWORK_ERROR;
441 : : }
442 : :
443 : : // Associate UDP socket to gateway.
444 [ + - - + ]: 8 : if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
445 [ # # # # ]: 0 : LogWarning("pcp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
446 : 0 : return MappingError::NETWORK_ERROR;
447 : : }
448 : :
449 : : // Use getsockname to get the address toward the default gateway (the internal address),
450 : : // in case we don't know what address to map
451 : : // (this is only needed if bind is INADDR_ANY, but it doesn't hurt as an extra check).
452 : 8 : struct sockaddr_storage internal_addr;
453 : 8 : socklen_t internal_addrlen = sizeof(struct sockaddr_storage);
454 [ + - - + ]: 8 : if (sock->GetSockName((struct sockaddr*)&internal_addr, &internal_addrlen) != 0) {
455 [ # # # # ]: 0 : LogWarning("pcp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
456 : 0 : return MappingError::NETWORK_ERROR;
457 : : }
458 [ + - ]: 8 : CService internal;
459 [ + - - + ]: 8 : if (!internal.SetSockAddr((struct sockaddr*)&internal_addr, internal_addrlen)) return MappingError::NETWORK_ERROR;
460 [ + - + - : 8 : LogDebug(BCLog::NET, "pcp: Internal address after connect: %s\n", internal.ToStringAddr());
+ - + - ]
461 : :
462 : : // Build request packet. Make sure the packet is zeroed so that reserved fields are zero
463 : : // as required by the spec (and not potentially leak data).
464 : : // Make sure there's space for the request header and MAP specific request data.
465 [ + - ]: 8 : std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE);
466 : : // Fill in request header, See RFC6887 Figure 2.
467 : 8 : size_t ofs = 0;
468 : 8 : request[ofs + PCP_HDR_VERSION_OFS] = PCP_VERSION;
469 : 8 : request[ofs + PCP_HDR_OP_OFS] = PCP_REQUEST | PCP_OP_MAP;
470 : 8 : WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime);
471 [ - + + - : 8 : if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_REQUEST_HDR_IP_OFS, ADDR_IPV6_SIZE), internal)) return MappingError::NETWORK_ERROR;
- + ]
472 : :
473 : 8 : ofs += PCP_HDR_SIZE;
474 : :
475 : : // Fill in MAP request packet, See RFC6887 Figure 9.
476 : : // Randomize mapping nonce (this is repeated in the response, to be able to
477 : : // correlate requests and responses, and used to authenticate changes to the mapping).
478 : 8 : std::memcpy(request.data() + ofs + PCP_MAP_NONCE_OFS, nonce.data(), PCP_MAP_NONCE_SIZE);
479 : 8 : request[ofs + PCP_MAP_PROTOCOL_OFS] = PCP_PROTOCOL_TCP;
480 : 8 : WriteBE16(request.data() + ofs + PCP_MAP_INTERNAL_PORT_OFS, port);
481 : 8 : WriteBE16(request.data() + ofs + PCP_MAP_EXTERNAL_PORT_OFS, port);
482 [ - + + - : 8 : if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE), bind)) return MappingError::NETWORK_ERROR;
- + ]
483 : :
484 : 8 : ofs += PCP_MAP_SIZE;
485 [ - + + - ]: 8 : Assume(ofs == request.size());
486 : :
487 : : // Receive loop.
488 : 8 : bool is_natpmp = false;
489 [ + - ]: 16 : auto recv_res = PCPSendRecv(*sock, "pcp", request, num_tries, timeout_per_try,
490 [ + - ]: 8 : [&](const std::span<const uint8_t> response) -> bool {
491 : : // Unsupported version according to RFC6887 appendix A and RFC6886 section 3.5, can fall back to NAT-PMP.
492 [ + + + - : 6 : if (response.size() == NATPMP_RESPONSE_HDR_SIZE && response[PCP_HDR_VERSION_OFS] == NATPMP_VERSION && response[PCP_RESPONSE_HDR_RESULT_OFS] == NATPMP_RESULT_UNSUPP_VERSION) {
+ - ]
493 : 1 : is_natpmp = true;
494 : 1 : return true; // Let it through to caller.
495 : : }
496 [ - + ]: 5 : if (response.size() < (PCP_HDR_SIZE + PCP_MAP_SIZE)) {
497 : 0 : LogWarning("pcp: Response too small\n");
498 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
499 : : }
500 [ + - + - ]: 5 : if (response[PCP_HDR_VERSION_OFS] != PCP_VERSION || response[PCP_HDR_OP_OFS] != (PCP_RESPONSE | PCP_OP_MAP)) {
501 : 0 : LogWarning("pcp: Response to wrong command\n");
502 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
503 : : }
504 : : // Handle MAP opcode response. See RFC6887 Figure 10.
505 : : // Check that returned mapping nonce matches our request.
506 [ - + ]: 5 : if (!std::ranges::equal(response.subspan(PCP_HDR_SIZE + PCP_MAP_NONCE_OFS, PCP_MAP_NONCE_SIZE), nonce)) {
507 : 0 : LogWarning("pcp: Mapping nonce mismatch\n");
508 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
509 : : }
510 : 5 : uint8_t protocol = response[PCP_HDR_SIZE + 12];
511 [ + - ]: 5 : uint16_t internal_port = ReadBE16(response.data() + PCP_HDR_SIZE + 16);
512 [ + - - + ]: 5 : if (protocol != PCP_PROTOCOL_TCP || internal_port != port) {
513 : 0 : LogWarning("pcp: Response protocol or port doesn't match request\n");
514 : 0 : return false; // Wasn't response to what we expected, try receiving next packet.
515 : : }
516 : : return true;
517 : : },
518 [ + - ]: 8 : interrupt);
519 : :
520 [ + + ]: 8 : if (!recv_res) {
521 : 2 : return MappingError::NETWORK_ERROR;
522 : : }
523 [ + + ]: 6 : if (is_natpmp) {
524 : 1 : return MappingError::UNSUPP_VERSION;
525 : : }
526 : :
527 [ - + ]: 5 : const std::span<const uint8_t> response = *recv_res;
528 : : // If we get here, we got a valid MAP response to our request.
529 : : // Check to see if we got the result we expected.
530 [ + - ]: 5 : Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE));
531 : 5 : uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS];
532 [ + - ]: 5 : uint32_t lifetime_ret = ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS);
533 [ + - ]: 5 : uint16_t external_port = ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS);
534 [ + - ]: 5 : CNetAddr external_addr{PCPUnwrapAddress(response.subspan(PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE))};
535 [ + + ]: 5 : if (result_code != PCP_RESULT_SUCCESS) {
536 [ - + ]: 2 : if (result_code == PCP_RESULT_NOT_AUTHORIZED) {
537 : 0 : static std::atomic<bool> warned{false};
538 [ # # ]: 0 : if (!warned.exchange(true)) {
539 [ # # # # ]: 0 : LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code));
540 : : } else {
541 [ # # # # : 0 : LogDebug(BCLog::NET, "pcp: Mapping failed with result %s\n", PCPResultString(result_code));
# # # # ]
542 : : }
543 : : } else {
544 [ + - + - ]: 2 : LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code));
545 : : }
546 [ + + ]: 2 : if (result_code == PCP_RESULT_NO_RESOURCES) {
547 : 1 : return MappingError::NO_RESOURCES;
548 : : }
549 : 1 : return MappingError::PROTOCOL_ERROR;
550 : : }
551 : :
552 [ + - + - ]: 6 : return MappingResult(PCP_VERSION, CService(internal, port), CService(external_addr, external_port), lifetime_ret);
553 : 29 : }
554 : :
555 : 0 : std::string MappingResult::ToString() const
556 : : {
557 : 0 : Assume(version == NATPMP_VERSION || version == PCP_VERSION);
558 : 0 : return strprintf("%s:%s -> %s (for %ds)",
559 [ # # ]: 0 : version == NATPMP_VERSION ? "natpmp" : "pcp",
560 [ # # ]: 0 : external.ToStringAddrPort(),
561 : 0 : internal.ToStringAddrPort(),
562 : 0 : lifetime
563 [ # # ]: 0 : );
564 : : }
|