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