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