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