/tmp/bitcoin/src/common/pcp.cpp
Line | Count | Source |
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 | | std::string NATPMPResultString(uint16_t result_code) |
186 | 2 | { |
187 | 2 | auto result_i = NATPMP_RESULT_STR.find(result_code); |
188 | 2 | return strprintf("%s (code %d)", result_i == NATPMP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code); |
189 | 2 | } |
190 | | |
191 | | //! Return human-readable string from PCP result code. |
192 | | std::string PCPResultString(uint8_t result_code) |
193 | 2 | { |
194 | 2 | auto result_i = PCP_RESULT_STR.find(result_code); |
195 | 2 | return strprintf("%s (code %d)", result_i == PCP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code); |
196 | 2 | } |
197 | | |
198 | | //! Wrap address in IPv6 according to RFC6887. wrapped_addr needs to be able to store 16 bytes. |
199 | | [[nodiscard]] bool PCPWrapAddress(std::span<uint8_t> wrapped_addr, const CNetAddr &addr) |
200 | 16 | { |
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 | 12 | } 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 | 4 | } else { |
215 | 0 | return false; |
216 | 0 | } |
217 | 16 | } |
218 | | |
219 | | //! Unwrap PCP-encoded address according to RFC6887. |
220 | | CNetAddr PCPUnwrapAddress(std::span<const uint8_t> wrapped_addr) |
221 | 5 | { |
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 | 3 | } else { |
228 | 3 | struct in6_addr addr6; |
229 | 3 | std::memcpy(&addr6, wrapped_addr.data(), ADDR_IPV6_SIZE); |
230 | 3 | return CNetAddr(addr6); |
231 | 3 | } |
232 | 5 | } |
233 | | |
234 | | //! PCP or NAT-PMP send-receive loop. |
235 | | 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 | 13 | { |
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 | 3 | } |
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 | 0 | } |
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 | 0 | } |
265 | 16 | if (!occurred) { |
266 | 4 | LogDebug(BCLog::NET, "%s: Timeout\n", protocol); |
267 | 4 | break; // Retry. |
268 | 4 | } |
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 | 1 | } |
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 | 11 | got_response = true; // Got expected response, break from receive loop as well as from retry loop. |
280 | 11 | break; |
281 | 11 | } |
282 | 11 | } |
283 | 16 | } |
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 | 1 | } |
288 | 11 | return std::vector<uint8_t>(response, response + recvsz); |
289 | 12 | } |
290 | | |
291 | | } |
292 | | |
293 | | 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 | 3 | { |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 3 | 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 | 0 | } |
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 | 0 | } |
340 | 3 | return true; |
341 | 3 | }, |
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 | 1 | } |
354 | | |
355 | 2 | std::memcpy(&external_addr, response.data() + NATPMP_GETEXTERNAL_RESPONSE_IP_OFS, ADDR_IPV4_SIZE); |
356 | 2 | } else { |
357 | 0 | return MappingError::NETWORK_ERROR; |
358 | 0 | } |
359 | | |
360 | | // Create TCP mapping request (RFC6886 section 3.3). |
361 | 2 | 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 | 2 | 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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
383 | 2 | return true; |
384 | 2 | }, |
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 | 0 | } else { |
398 | 0 | LogDebug(BCLog::NET, "natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code)); |
399 | 0 | } |
400 | 1 | } else { |
401 | 1 | LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code)); |
402 | 1 | } |
403 | 1 | if (result_code == NATPMP_RESULT_NO_RESOURCES) { |
404 | 0 | return MappingError::NO_RESOURCES; |
405 | 0 | } |
406 | 1 | return MappingError::PROTOCOL_ERROR; |
407 | 1 | } |
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 | 1 | return MappingResult(NATPMP_VERSION, CService(internal.sin_addr, port), CService(external_addr, external_port), lifetime_ret); |
412 | 2 | } else { |
413 | 0 | return MappingError::NETWORK_ERROR; |
414 | 0 | } |
415 | 2 | } |
416 | | |
417 | | 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 | 8 | { |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 8 | 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 | 1 | } |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
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 | 0 | } |
516 | 5 | return true; |
517 | 5 | }, |
518 | 8 | interrupt); |
519 | | |
520 | 8 | if (!recv_res) { |
521 | 2 | return MappingError::NETWORK_ERROR; |
522 | 2 | } |
523 | 6 | if (is_natpmp) { |
524 | 1 | return MappingError::UNSUPP_VERSION; |
525 | 1 | } |
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 | 0 | } else { |
541 | 0 | LogDebug(BCLog::NET, "pcp: Mapping failed with result %s\n", PCPResultString(result_code)); |
542 | 0 | } |
543 | 2 | } else { |
544 | 2 | LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code)); |
545 | 2 | } |
546 | 2 | if (result_code == PCP_RESULT_NO_RESOURCES) { |
547 | 1 | return MappingError::NO_RESOURCES; |
548 | 1 | } |
549 | 1 | return MappingError::PROTOCOL_ERROR; |
550 | 2 | } |
551 | | |
552 | 3 | return MappingResult(PCP_VERSION, CService(internal, port), CService(external_addr, external_port), lifetime_ret); |
553 | 5 | } |
554 | | |
555 | | std::string MappingResult::ToString() const |
556 | 0 | { |
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 | 0 | } |