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