Bitcoin Core 31.99.0
P2P Digital Currency
pcp.cpp
Go to the documentation of this file.
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
32namespace {
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.
39constexpr uint16_t NATPMP_SERVER_PORT = 5351;
41constexpr uint8_t NATPMP_VERSION = 0;
43constexpr uint8_t NATPMP_REQUEST = 0x00;
45constexpr uint8_t NATPMP_RESPONSE = 0x80;
47constexpr uint8_t NATPMP_OP_GETEXTERNAL = 0x00;
49constexpr uint8_t NATPMP_OP_MAP_TCP = 0x02;
51constexpr size_t NATPMP_REQUEST_HDR_SIZE = 2;
53constexpr size_t NATPMP_RESPONSE_HDR_SIZE = 8;
55constexpr size_t NATPMP_GETEXTERNAL_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 0;
57constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 4;
59constexpr size_t NATPMP_MAP_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 10;
61constexpr 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.
65constexpr size_t NATPMP_HDR_VERSION_OFS = 0;
67constexpr size_t NATPMP_HDR_OP_OFS = 1;
69constexpr size_t NATPMP_RESPONSE_HDR_RESULT_OFS = 2;
70
71// GETEXTERNAL response offsets (RFC6886 3.2), relative to start of packet.
73constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_IP_OFS = 8;
74
75// MAP request offsets (RFC6886 3.3), relative to start of packet.
77constexpr size_t NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS = 4;
79constexpr size_t NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS = 6;
81constexpr size_t NATPMP_MAP_REQUEST_LIFETIME_OFS = 8;
82
83// MAP response offsets (RFC6886 3.3), relative to start of packet.
85constexpr size_t NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS = 8;
87constexpr size_t NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS = 10;
89constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12;
90
91// Relevant NETPMP result codes (RFC6886 3.5).
93constexpr uint8_t NATPMP_RESULT_SUCCESS = 0;
95constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1;
97constexpr uint8_t NATPMP_RESULT_NOT_AUTHORIZED = 2;
99constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4;
100
102const 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.
113constexpr size_t PCP_MAX_SIZE = 1100;
115constexpr uint16_t PCP_SERVER_PORT = NATPMP_SERVER_PORT;
117constexpr uint8_t PCP_VERSION = 2;
119constexpr uint8_t PCP_REQUEST = NATPMP_REQUEST; // R = 0
121constexpr uint8_t PCP_RESPONSE = NATPMP_RESPONSE; // R = 1
123constexpr uint8_t PCP_OP_MAP = 0x01;
125constexpr uint16_t PCP_PROTOCOL_TCP = 6;
127constexpr size_t PCP_HDR_SIZE = 24;
129constexpr 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.
133constexpr size_t PCP_HDR_VERSION_OFS = NATPMP_HDR_VERSION_OFS;
135constexpr size_t PCP_HDR_OP_OFS = NATPMP_HDR_OP_OFS;
137constexpr size_t PCP_HDR_LIFETIME_OFS = 4;
138
139// Request header offsets (RFC6887 7.1), relative to start of packet.
141constexpr size_t PCP_REQUEST_HDR_IP_OFS = 8;
142
143// Response header offsets (RFC6887 7.2), relative to start of packet.
145constexpr size_t PCP_RESPONSE_HDR_RESULT_OFS = 3;
146
147// MAP request/response offsets (RFC6887 11.1), relative to start of opcode-specific data.
149constexpr size_t PCP_MAP_NONCE_OFS = 0;
151constexpr size_t PCP_MAP_PROTOCOL_OFS = 12;
153constexpr size_t PCP_MAP_INTERNAL_PORT_OFS = 16;
155constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18;
157constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20;
158
160constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS;
162constexpr uint8_t PCP_RESULT_NOT_AUTHORIZED = NATPMP_RESULT_NOT_AUTHORIZED;
164constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8;
165
167const 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
185std::string NATPMPResultString(uint16_t result_code)
186{
187 auto result_i = NATPMP_RESULT_STR.find(result_code);
188 return strprintf("%s (code %d)", result_i == NATPMP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code);
189}
190
192std::string PCPResultString(uint8_t result_code)
193{
194 auto result_i = PCP_RESULT_STR.find(result_code);
195 return strprintf("%s (code %d)", result_i == PCP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code);
196}
197
199[[nodiscard]] bool PCPWrapAddress(std::span<uint8_t> wrapped_addr, const CNetAddr &addr)
200{
201 Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
202 if (addr.IsIPv4()) {
203 struct in_addr addr4;
204 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 std::memcpy(wrapped_addr.data(), IPV4_IN_IPV6_PREFIX.data(), IPV4_IN_IPV6_PREFIX.size());
207 std::memcpy(wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), &addr4, ADDR_IPV4_SIZE);
208 return true;
209 } else if (addr.IsIPv6()) {
210 struct in6_addr addr6;
211 if (!addr.GetIn6Addr(&addr6)) return false;
212 std::memcpy(wrapped_addr.data(), &addr6, ADDR_IPV6_SIZE);
213 return true;
214 } else {
215 return false;
216 }
217}
218
220CNetAddr PCPUnwrapAddress(std::span<const uint8_t> wrapped_addr)
221{
222 Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
223 if (util::HasPrefix(wrapped_addr, IPV4_IN_IPV6_PREFIX)) {
224 struct in_addr addr4;
225 std::memcpy(&addr4, wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), ADDR_IPV4_SIZE);
226 return CNetAddr(addr4);
227 } else {
228 struct in6_addr addr6;
229 std::memcpy(&addr6, wrapped_addr.data(), ADDR_IPV6_SIZE);
230 return CNetAddr(addr6);
231 }
232}
233
235std::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 using namespace std::chrono;
241 // UDP is a potentially lossy protocol, so we try to send again a few times.
242 uint8_t response[PCP_MAX_SIZE];
243 bool got_response = false;
244 int recvsz = 0;
245 for (int ntry = 0; !got_response && ntry < num_tries; ++ntry) {
246 if (ntry > 0) {
247 LogDebug(BCLog::NET, "%s: Retrying (%d)\n", protocol, ntry);
248 }
249 // Dispatch packet to gateway.
250 if (sock.Send(request.data(), request.size(), 0) != static_cast<ssize_t>(request.size())) {
251 LogDebug(BCLog::NET, "%s: Could not send request: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
252 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 auto cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now());
257 auto deadline = cur_time + timeout_per_try;
258 while ((cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now())) < deadline) {
259 if (interrupt) return std::nullopt;
260 Sock::Event occurred = 0;
261 if (!sock.Wait(deadline - cur_time, Sock::RecvEvent, &occurred)) {
262 LogWarning("%s: Could not wait on socket: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
263 return std::nullopt; // Network-level error, probably no use retrying.
264 }
265 if (!occurred) {
266 LogDebug(BCLog::NET, "%s: Timeout\n", protocol);
267 break; // Retry.
268 }
269
270 // Receive response.
271 recvsz = sock.Recv(response, sizeof(response), MSG_DONTWAIT);
272 if (recvsz < 0) {
273 LogDebug(BCLog::NET, "%s: Could not receive response: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
274 return std::nullopt; // Network-level error, probably no use retrying.
275 }
276 LogDebug(BCLog::NET, "%s: Received response of %d bytes: %s\n", protocol, recvsz, HexStr(std::span(response, recvsz)));
277
278 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 if (!got_response) {
285 LogDebug(BCLog::NET, "%s: Giving up after %d tries\n", protocol, num_tries);
286 return std::nullopt;
287 }
288 return std::vector<uint8_t>(response, response + recvsz);
289}
290
291}
292
293std::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 struct sockaddr_storage dest_addr;
296 socklen_t dest_addrlen = sizeof(struct sockaddr_storage);
297
298 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 if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR;
302 if (dest_addr.ss_family != AF_INET) return MappingError::NETWORK_ERROR;
303
304 // Create IPv4 UDP socket
305 auto sock{CreateSock(AF_INET, SOCK_DGRAM, IPPROTO_UDP)};
306 if (!sock) {
307 LogWarning("natpmp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
309 }
310
311 // Associate UDP socket to gateway.
312 if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
313 LogWarning("natpmp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
315 }
316
317 // Use getsockname to get the address toward the default gateway (the internal address).
318 struct sockaddr_in internal;
319 socklen_t internal_addrlen = sizeof(struct sockaddr_in);
320 if (sock->GetSockName((struct sockaddr*)&internal, &internal_addrlen) != 0) {
321 LogWarning("natpmp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
323 }
324
325 // Request external IP address (RFC6886 section 3.2).
326 std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE);
327 request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
328 request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_GETEXTERNAL;
329
330 auto recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
331 [&](const std::span<const uint8_t> response) -> bool {
332 if (response.size() < NATPMP_GETEXTERNAL_RESPONSE_SIZE) {
333 LogWarning("natpmp: Response too small\n");
334 return false; // Wasn't response to what we expected, try receiving next packet.
335 }
336 if (response[NATPMP_HDR_VERSION_OFS] != NATPMP_VERSION || response[NATPMP_HDR_OP_OFS] != (NATPMP_RESPONSE | NATPMP_OP_GETEXTERNAL)) {
337 LogWarning("natpmp: Response to wrong command\n");
338 return false; // Wasn't response to what we expected, try receiving next packet.
339 }
340 return true;
341 },
342 interrupt);
343
344 struct in_addr external_addr;
345 if (recv_res) {
346 const std::span<const uint8_t> response = *recv_res;
347
348 Assume(response.size() >= NATPMP_GETEXTERNAL_RESPONSE_SIZE);
349 uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
350 if (result_code != NATPMP_RESULT_SUCCESS) {
351 LogWarning("natpmp: Getting external address failed with result %s\n", NATPMPResultString(result_code));
353 }
354
355 std::memcpy(&external_addr, response.data() + NATPMP_GETEXTERNAL_RESPONSE_IP_OFS, ADDR_IPV4_SIZE);
356 } else {
358 }
359
360 // Create TCP mapping request (RFC6886 section 3.3).
361 request = std::vector<uint8_t>(NATPMP_MAP_REQUEST_SIZE);
362 request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
363 request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_MAP_TCP;
364 WriteBE16(request.data() + NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS, port);
365 WriteBE16(request.data() + NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS, port);
366 WriteBE32(request.data() + NATPMP_MAP_REQUEST_LIFETIME_OFS, lifetime);
367
368 recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
369 [&](const std::span<const uint8_t> response) -> bool {
370 if (response.size() < NATPMP_MAP_RESPONSE_SIZE) {
371 LogWarning("natpmp: Response too small\n");
372 return false; // Wasn't response to what we expected, try receiving next packet.
373 }
374 if (response[0] != NATPMP_VERSION || response[1] != (NATPMP_RESPONSE | NATPMP_OP_MAP_TCP)) {
375 LogWarning("natpmp: Response to wrong command\n");
376 return false; // Wasn't response to what we expected, try receiving next packet.
377 }
378 uint16_t internal_port = ReadBE16(response.data() + NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS);
379 if (internal_port != port) {
380 LogWarning("natpmp: Response port doesn't match request\n");
381 return false; // Wasn't response to what we expected, try receiving next packet.
382 }
383 return true;
384 },
385 interrupt);
386
387 if (recv_res) {
388 const std::span<uint8_t> response = *recv_res;
389
390 Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE);
391 uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
392 if (result_code != NATPMP_RESULT_SUCCESS) {
393 if (result_code == NATPMP_RESULT_NOT_AUTHORIZED) {
394 static std::atomic<bool> warned{false};
395 if (!warned.exchange(true)) {
396 LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
397 } else {
398 LogDebug(BCLog::NET, "natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
399 }
400 } else {
401 LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
402 }
403 if (result_code == NATPMP_RESULT_NO_RESOURCES) {
405 }
407 }
408
409 uint32_t lifetime_ret = ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS);
410 uint16_t external_port = ReadBE16(response.data() + NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS);
411 return MappingResult(NATPMP_VERSION, CService(internal.sin_addr, port), CService(external_addr, external_port), lifetime_ret);
412 } else {
414 }
415}
416
417std::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 struct sockaddr_storage dest_addr, bind_addr;
420 socklen_t dest_addrlen = sizeof(struct sockaddr_storage), bind_addrlen = sizeof(struct sockaddr_storage);
421
422 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 if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR;
426 if (!CService(bind, 0).GetSockAddr((struct sockaddr*)&bind_addr, &bind_addrlen)) return MappingError::NETWORK_ERROR;
427 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 auto sock{CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)};
431 if (!sock) {
432 LogWarning("pcp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
434 }
435
436 // Make sure that we send from requested destination address, anything else will be
437 // rejected by a security-conscious router.
438 if (sock->Bind((struct sockaddr*)&bind_addr, bind_addrlen) != 0) {
439 LogWarning("pcp: Could not bind to address: %s\n", NetworkErrorString(WSAGetLastError()));
441 }
442
443 // Associate UDP socket to gateway.
444 if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
445 LogWarning("pcp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
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 struct sockaddr_storage internal_addr;
453 socklen_t internal_addrlen = sizeof(struct sockaddr_storage);
454 if (sock->GetSockName((struct sockaddr*)&internal_addr, &internal_addrlen) != 0) {
455 LogWarning("pcp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
457 }
458 CService internal;
459 if (!internal.SetSockAddr((struct sockaddr*)&internal_addr, internal_addrlen)) return MappingError::NETWORK_ERROR;
460 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 std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE);
466 // Fill in request header, See RFC6887 Figure 2.
467 size_t ofs = 0;
468 request[ofs + PCP_HDR_VERSION_OFS] = PCP_VERSION;
469 request[ofs + PCP_HDR_OP_OFS] = PCP_REQUEST | PCP_OP_MAP;
470 WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime);
471 if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_REQUEST_HDR_IP_OFS, ADDR_IPV6_SIZE), internal)) return MappingError::NETWORK_ERROR;
472
473 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 std::memcpy(request.data() + ofs + PCP_MAP_NONCE_OFS, nonce.data(), PCP_MAP_NONCE_SIZE);
479 request[ofs + PCP_MAP_PROTOCOL_OFS] = PCP_PROTOCOL_TCP;
480 WriteBE16(request.data() + ofs + PCP_MAP_INTERNAL_PORT_OFS, port);
481 WriteBE16(request.data() + ofs + PCP_MAP_EXTERNAL_PORT_OFS, port);
482 if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE), bind)) return MappingError::NETWORK_ERROR;
483
484 ofs += PCP_MAP_SIZE;
485 Assume(ofs == request.size());
486
487 // Receive loop.
488 bool is_natpmp = false;
489 auto recv_res = PCPSendRecv(*sock, "pcp", request, num_tries, timeout_per_try,
490 [&](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 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 is_natpmp = true;
494 return true; // Let it through to caller.
495 }
496 if (response.size() < (PCP_HDR_SIZE + PCP_MAP_SIZE)) {
497 LogWarning("pcp: Response too small\n");
498 return false; // Wasn't response to what we expected, try receiving next packet.
499 }
500 if (response[PCP_HDR_VERSION_OFS] != PCP_VERSION || response[PCP_HDR_OP_OFS] != (PCP_RESPONSE | PCP_OP_MAP)) {
501 LogWarning("pcp: Response to wrong command\n");
502 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 if (!std::ranges::equal(response.subspan(PCP_HDR_SIZE + PCP_MAP_NONCE_OFS, PCP_MAP_NONCE_SIZE), nonce)) {
507 LogWarning("pcp: Mapping nonce mismatch\n");
508 return false; // Wasn't response to what we expected, try receiving next packet.
509 }
510 uint8_t protocol = response[PCP_HDR_SIZE + 12];
511 uint16_t internal_port = ReadBE16(response.data() + PCP_HDR_SIZE + 16);
512 if (protocol != PCP_PROTOCOL_TCP || internal_port != port) {
513 LogWarning("pcp: Response protocol or port doesn't match request\n");
514 return false; // Wasn't response to what we expected, try receiving next packet.
515 }
516 return true;
517 },
518 interrupt);
519
520 if (!recv_res) {
522 }
523 if (is_natpmp) {
525 }
526
527 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 Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE));
531 uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS];
532 uint32_t lifetime_ret = ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS);
533 uint16_t external_port = ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS);
534 CNetAddr external_addr{PCPUnwrapAddress(response.subspan(PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE))};
535 if (result_code != PCP_RESULT_SUCCESS) {
536 if (result_code == PCP_RESULT_NOT_AUTHORIZED) {
537 static std::atomic<bool> warned{false};
538 if (!warned.exchange(true)) {
539 LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code));
540 } else {
541 LogDebug(BCLog::NET, "pcp: Mapping failed with result %s\n", PCPResultString(result_code));
542 }
543 } else {
544 LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code));
545 }
546 if (result_code == PCP_RESULT_NO_RESOURCES) {
548 }
550 }
551
552 return MappingResult(PCP_VERSION, CService(internal, port), CService(external_addr, external_port), lifetime_ret);
553}
554
555std::string MappingResult::ToString() const
556{
557 Assume(version == NATPMP_VERSION || version == PCP_VERSION);
558 return strprintf("%s:%s -> %s (for %ds)",
559 version == NATPMP_VERSION ? "natpmp" : "pcp",
563 );
564}
if(!SetupNetworking())
#define Assume(val)
Assume is the identity function.
Definition: check.h:128
Network address.
Definition: netaddress.h:113
std::string ToStringAddr() const
Definition: netaddress.cpp:580
bool GetIn6Addr(struct in6_addr *pipv6Addr) const
Try to get our IPv6 (or CJDNS) address.
Definition: netaddress.cpp:642
bool GetInAddr(struct in_addr *pipv4Addr) const
Try to get our IPv4 address.
Definition: netaddress.cpp:623
bool IsIPv4() const
Definition: netaddress.h:158
bool IsIPv6() const
Definition: netaddress.h:159
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:530
bool SetSockAddr(const struct sockaddr *paddr, socklen_t addrlen)
Set CService from a network sockaddr.
Definition: netaddress.cpp:806
std::string ToStringAddrPort() const
Definition: netaddress.cpp:903
A helper class for interruptible sleeps.
RAII helper class that manages a socket and closes it automatically when it goes out of scope.
Definition: sock.h:35
static constexpr Event RecvEvent
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition: sock.h:151
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
Definition: sock.cpp:47
virtual int Bind(const sockaddr *addr, socklen_t addr_len) const
bind(2) wrapper.
Definition: sock.cpp:62
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
Definition: sock.cpp:141
uint8_t Event
Definition: sock.h:146
virtual int GetSockName(sockaddr *name, socklen_t *name_len) const
getsockname(2) wrapper.
Definition: sock.cpp:108
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
Definition: sock.cpp:57
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
Definition: sock.cpp:52
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)
Try to open a port using RFC 6886 NAT-PMP.
Definition: pcp.cpp:293
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)
Try to open a port using RFC 6887 Port Control Protocol (PCP).
Definition: pcp.cpp:417
#define WSAGetLastError()
Definition: compat.h:59
#define MSG_DONTWAIT
Definition: compat.h:115
void WriteBE32(B *ptr, uint32_t x)
Definition: common.h:95
void WriteBE16(B *ptr, uint16_t x)
Definition: common.h:88
uint16_t ReadBE16(const B *ptr)
Definition: common.h:64
uint32_t ReadBE32(const B *ptr)
Definition: common.h:72
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
#define LogWarning(...)
Definition: log.h:126
#define LogDebug(category,...)
Definition: log.h:143
unsigned int nonce
@ NET
Definition: categories.h:16
bool HasPrefix(const T1 &obj, const std::array< uint8_t, PREFIX_LEN > &prefix)
Check whether a container begins with the given prefix.
Definition: string.h:261
constexpr size_t ADDR_IPV4_SIZE
Size of IPv4 address (in bytes).
Definition: netaddress.h:86
constexpr size_t ADDR_IPV6_SIZE
Size of IPv6 address (in bytes).
Definition: netaddress.h:89
constexpr std::array< uint8_t, 12 > IPV4_IN_IPV6_PREFIX
Prefix of an IPv6 address when it contains an embedded IPv4 address.
Definition: netaddress.h:62
std::function< std::unique_ptr< Sock >(int, int, int)> CreateSock
Socket factory.
Definition: netbase.cpp:577
std::array< uint8_t, PCP_MAP_NONCE_SIZE > PCPMappingNonce
PCP mapping nonce. Arbitrary data chosen by the client to identify a mapping.
Definition: pcp.h:26
constexpr size_t PCP_MAP_NONCE_SIZE
Mapping nonce size in bytes (see RFC6887 section 11.1).
Definition: pcp.h:23
@ PROTOCOL_ERROR
Any kind of protocol-level error, except unsupported version or no resources.
@ NO_RESOURCES
No resources available (port probably already mapped).
@ UNSUPP_VERSION
Unsupported protocol version.
@ NETWORK_ERROR
Any kind of network-level error.
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition: sock.cpp:426
Successful response to a port mapping.
Definition: pcp.h:37
CService external
External host:port.
Definition: pcp.h:45
uint32_t lifetime
Granted lifetime of binding (seconds).
Definition: pcp.h:47
CService internal
Internal host:port.
Definition: pcp.h:43
std::string ToString() const
Format mapping as string for logging.
Definition: pcp.cpp:555
uint8_t version
Protocol version, one of NATPMP_VERSION or PCP_VERSION.
Definition: pcp.h:41
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition: time.cpp:65
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172