LCOV - code coverage report
Current view: top level - src/common - pcp.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 75.8 % 227 172
Test Date: 2026-08-07 06:33:33 Functions: 90.9 % 11 10
Branches: 46.2 % 344 159

             Branch data     Line data    Source code
       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                 :             : 
      32                 :             : namespace {
      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.
      38                 :             : //! NAT-PMP uses a fixed server port number (RFC6887 section 1.1).
      39                 :             : constexpr uint16_t NATPMP_SERVER_PORT = 5351;
      40                 :             : //! Version byte for NATPMP (RFC6886 1.1)
      41                 :             : constexpr uint8_t NATPMP_VERSION = 0;
      42                 :             : //! Request opcode base (RFC6886 3).
      43                 :             : constexpr uint8_t NATPMP_REQUEST = 0x00;
      44                 :             : //! Response opcode base (RFC6886 3).
      45                 :             : constexpr uint8_t NATPMP_RESPONSE = 0x80;
      46                 :             : //! Get external address (RFC6886 3.2)
      47                 :             : constexpr uint8_t NATPMP_OP_GETEXTERNAL = 0x00;
      48                 :             : //! Map TCP port (RFC6886 3.3)
      49                 :             : constexpr uint8_t NATPMP_OP_MAP_TCP = 0x02;
      50                 :             : //! Shared request header size in bytes.
      51                 :             : constexpr size_t NATPMP_REQUEST_HDR_SIZE = 2;
      52                 :             : //! Shared response header (minimum) size in bytes.
      53                 :             : constexpr size_t NATPMP_RESPONSE_HDR_SIZE = 8;
      54                 :             : //! GETEXTERNAL request size in bytes, including header (RFC6886 3.2).
      55                 :             : constexpr size_t NATPMP_GETEXTERNAL_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 0;
      56                 :             : //! GETEXTERNAL response size in bytes, including header (RFC6886 3.2).
      57                 :             : constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 4;
      58                 :             : //! MAP request size in bytes, including header (RFC6886 3.3).
      59                 :             : constexpr size_t NATPMP_MAP_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 10;
      60                 :             : //! MAP response size in bytes, including header (RFC6886 3.3).
      61                 :             : constexpr 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.
      64                 :             : //!  Offset of version field in packets.
      65                 :             : constexpr size_t NATPMP_HDR_VERSION_OFS = 0;
      66                 :             : //!  Offset of opcode field in packets
      67                 :             : constexpr size_t NATPMP_HDR_OP_OFS = 1;
      68                 :             : //!  Offset of result code in packets. Result codes are 16 bit in NAT-PMP instead of 8 bit in PCP.
      69                 :             : constexpr size_t NATPMP_RESPONSE_HDR_RESULT_OFS = 2;
      70                 :             : 
      71                 :             : // GETEXTERNAL response offsets (RFC6886 3.2), relative to start of packet.
      72                 :             : //!  Returned external address
      73                 :             : constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_IP_OFS = 8;
      74                 :             : 
      75                 :             : // MAP request offsets (RFC6886 3.3), relative to start of packet.
      76                 :             : //!  Internal port to be mapped.
      77                 :             : constexpr size_t NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS = 4;
      78                 :             : //!  Suggested external port for mapping.
      79                 :             : constexpr size_t NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS = 6;
      80                 :             : //!  Requested port mapping lifetime in seconds.
      81                 :             : constexpr size_t NATPMP_MAP_REQUEST_LIFETIME_OFS = 8;
      82                 :             : 
      83                 :             : // MAP response offsets (RFC6886 3.3), relative to start of packet.
      84                 :             : //!  Internal port for mapping (will match internal port of request).
      85                 :             : constexpr size_t NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS = 8;
      86                 :             : //!  External port for mapping.
      87                 :             : constexpr size_t NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS = 10;
      88                 :             : //!  Created port mapping lifetime in seconds.
      89                 :             : constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12;
      90                 :             : 
      91                 :             : // Relevant NETPMP result codes (RFC6886 3.5).
      92                 :             : //! Result code representing success status.
      93                 :             : constexpr uint8_t NATPMP_RESULT_SUCCESS = 0;
      94                 :             : //! Result code representing unsupported version.
      95                 :             : constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1;
      96                 :             : //! Result code representing not authorized (router doesn't support port mapping).
      97                 :             : constexpr uint8_t NATPMP_RESULT_NOT_AUTHORIZED = 2;
      98                 :             : //! Result code representing lack of resources.
      99                 :             : constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4;
     100                 :             : 
     101                 :             : //! Mapping of NATPMP result code to string (RFC6886 3.5). Result codes <=2 match PCP.
     102                 :             : const 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.
     112                 :             : //! Maximum packet size in bytes (RFC6887 section 7).
     113                 :             : constexpr size_t PCP_MAX_SIZE = 1100;
     114                 :             : //! PCP uses a fixed server port number (RFC6887 section 19.1). Shared with NAT-PMP.
     115                 :             : constexpr uint16_t PCP_SERVER_PORT = NATPMP_SERVER_PORT;
     116                 :             : //! Version byte. 0 is NAT-PMP (RFC6886), 1 is forbidden, 2 for PCP (RFC6887).
     117                 :             : constexpr uint8_t PCP_VERSION = 2;
     118                 :             : //! PCP Request Header. See RFC6887 section 7.1. Shared with NAT-PMP.
     119                 :             : constexpr uint8_t PCP_REQUEST = NATPMP_REQUEST; // R = 0
     120                 :             : //! PCP Response Header. See RFC6887 section 7.2. Shared with NAT-PMP.
     121                 :             : constexpr uint8_t PCP_RESPONSE = NATPMP_RESPONSE; // R = 1
     122                 :             : //! Map opcode. See RFC6887 section 19.2
     123                 :             : constexpr uint8_t PCP_OP_MAP = 0x01;
     124                 :             : //! TCP protocol number (IANA).
     125                 :             : constexpr uint16_t PCP_PROTOCOL_TCP = 6;
     126                 :             : //! Request and response header size in bytes (RFC6887 section 7.1).
     127                 :             : constexpr size_t PCP_HDR_SIZE = 24;
     128                 :             : //! Map request and response size in bytes (RFC6887 section 11.1).
     129                 :             : constexpr 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.
     132                 :             : //!  Version field (1 byte).
     133                 :             : constexpr size_t PCP_HDR_VERSION_OFS = NATPMP_HDR_VERSION_OFS;
     134                 :             : //!  Opcode field (1 byte).
     135                 :             : constexpr size_t PCP_HDR_OP_OFS = NATPMP_HDR_OP_OFS;
     136                 :             : //!  Requested lifetime (request), granted lifetime (response) (4 bytes).
     137                 :             : constexpr size_t PCP_HDR_LIFETIME_OFS = 4;
     138                 :             : 
     139                 :             : // Request header offsets (RFC6887 7.1), relative to start of packet.
     140                 :             : //!  PCP client's IP address (16 bytes).
     141                 :             : constexpr size_t PCP_REQUEST_HDR_IP_OFS = 8;
     142                 :             : 
     143                 :             : // Response header offsets (RFC6887 7.2), relative to start of packet.
     144                 :             : //!  Result code (1 byte).
     145                 :             : constexpr size_t PCP_RESPONSE_HDR_RESULT_OFS = 3;
     146                 :             : 
     147                 :             : // MAP request/response offsets (RFC6887 11.1), relative to start of opcode-specific data.
     148                 :             : //!  Mapping nonce (12 bytes).
     149                 :             : constexpr size_t PCP_MAP_NONCE_OFS = 0;
     150                 :             : //!  Protocol (1 byte).
     151                 :             : constexpr size_t PCP_MAP_PROTOCOL_OFS = 12;
     152                 :             : //!  Internal port for mapping (2 bytes).
     153                 :             : constexpr size_t PCP_MAP_INTERNAL_PORT_OFS = 16;
     154                 :             : //!  Suggested external port (request), assigned external port (response) (2 bytes).
     155                 :             : constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18;
     156                 :             : //!  Suggested external IP (request), assigned external IP (response) (16 bytes).
     157                 :             : constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20;
     158                 :             : 
     159                 :             : //! Result code representing success (RFC6887 7.4), shared with NAT-PMP.
     160                 :             : constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS;
     161                 :             : //! Result code representing not authorized (RFC6887 7.4), shared with NAT-PMP.
     162                 :             : constexpr uint8_t PCP_RESULT_NOT_AUTHORIZED = NATPMP_RESULT_NOT_AUTHORIZED;
     163                 :             : //! Result code representing lack of resources (RFC6887 7.4).
     164                 :             : constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8;
     165                 :             : 
     166                 :             : //! Mapping of PCP result code to string (RFC6887 7.4). Result codes <=2 match NAT-PMP.
     167                 :             : const 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                 :             : 
     184                 :             : //! Return human-readable string from NATPMP result code.
     185                 :           2 : std::string NATPMPResultString(uint16_t result_code)
     186                 :             : {
     187                 :           2 :     auto result_i = NATPMP_RESULT_STR.find(result_code);
     188   [ +  -  -  -  :           4 :     return strprintf("%s (code %d)", result_i == NATPMP_RESULT_STR.end() ? "(unknown)" : result_i->second,  result_code);
                   +  - ]
     189                 :             : }
     190                 :             : 
     191                 :             : //! Return human-readable string from PCP result code.
     192                 :           2 : std::string PCPResultString(uint8_t result_code)
     193                 :             : {
     194                 :           2 :     auto result_i = PCP_RESULT_STR.find(result_code);
     195   [ +  +  -  +  :           5 :     return strprintf("%s (code %d)", result_i == PCP_RESULT_STR.end() ? "(unknown)" : result_i->second,  result_code);
                   +  - ]
     196                 :             : }
     197                 :             : 
     198                 :             : //! Wrap address in IPv6 according to RFC6887. wrapped_addr needs to be able to store 16 bytes.
     199                 :          16 : [[nodiscard]] bool PCPWrapAddress(std::span<uint8_t> wrapped_addr, const CNetAddr &addr)
     200                 :             : {
     201         [ +  + ]:          16 :     Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
     202         [ +  + ]:          16 :     if (addr.IsIPv4()) {
     203                 :          12 :         struct in_addr addr4;
     204         [ +  - ]:          12 :         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                 :          12 :         std::memcpy(wrapped_addr.data(), IPV4_IN_IPV6_PREFIX.data(), IPV4_IN_IPV6_PREFIX.size());
     207                 :          12 :         std::memcpy(wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), &addr4, ADDR_IPV4_SIZE);
     208                 :          12 :         return true;
     209         [ +  - ]:           4 :     } else if (addr.IsIPv6()) {
     210                 :           4 :         struct in6_addr addr6;
     211         [ +  - ]:           4 :         if (!addr.GetIn6Addr(&addr6)) return false;
     212                 :           4 :         std::memcpy(wrapped_addr.data(), &addr6, ADDR_IPV6_SIZE);
     213                 :           4 :         return true;
     214                 :             :     } else {
     215                 :             :         return false;
     216                 :             :     }
     217                 :             : }
     218                 :             : 
     219                 :             : //! Unwrap PCP-encoded address according to RFC6887.
     220                 :           5 : CNetAddr PCPUnwrapAddress(std::span<const uint8_t> wrapped_addr)
     221                 :             : {
     222         [ +  + ]:           5 :     Assume(wrapped_addr.size() == ADDR_IPV6_SIZE);
     223         [ +  + ]:           5 :     if (util::HasPrefix(wrapped_addr, IPV4_IN_IPV6_PREFIX)) {
     224                 :           2 :         struct in_addr addr4;
     225                 :           2 :         std::memcpy(&addr4, wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), ADDR_IPV4_SIZE);
     226                 :           2 :         return CNetAddr(addr4);
     227                 :             :     } else {
     228                 :           3 :         struct in6_addr addr6;
     229                 :           3 :         std::memcpy(&addr6, wrapped_addr.data(), ADDR_IPV6_SIZE);
     230                 :           3 :         return CNetAddr(addr6);
     231                 :             :     }
     232                 :             : }
     233                 :             : 
     234                 :             : //! PCP or NAT-PMP send-receive loop.
     235                 :          13 : std::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                 :          13 :     using namespace std::chrono;
     241                 :             :     // UDP is a potentially lossy protocol, so we try to send again a few times.
     242                 :          13 :     uint8_t response[PCP_MAX_SIZE];
     243                 :          13 :     bool got_response = false;
     244                 :          13 :     int recvsz = 0;
     245   [ +  +  +  + ]:          28 :     for (int ntry = 0; !got_response && ntry < num_tries; ++ntry) {
     246         [ +  + ]:          16 :         if (ntry > 0) {
     247         [ +  - ]:           3 :             LogDebug(BCLog::NET, "%s: Retrying (%d)\n", protocol, ntry);
     248                 :             :         }
     249                 :             :         // Dispatch packet to gateway.
     250         [ -  + ]:          16 :         if (sock.Send(request.data(), request.size(), 0) != static_cast<ssize_t>(request.size())) {
     251   [ #  #  #  # ]:           0 :             LogDebug(BCLog::NET, "%s: Could not send request: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
     252                 :           0 :             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                 :          16 :         auto cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now());
     257                 :          16 :         auto deadline = cur_time + timeout_per_try;
     258         [ +  - ]:          16 :         while ((cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now())) < deadline) {
     259         [ -  + ]:          16 :             if (interrupt) return std::nullopt;
     260                 :          16 :             Sock::Event occurred = 0;
     261         [ -  + ]:          16 :             if (!sock.Wait(deadline - cur_time, Sock::RecvEvent, &occurred)) {
     262         [ #  # ]:           0 :                 LogWarning("%s: Could not wait on socket: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
     263                 :           0 :                 return std::nullopt; // Network-level error, probably no use retrying.
     264                 :             :             }
     265         [ +  + ]:          16 :             if (!occurred) {
     266         [ +  - ]:           4 :                 LogDebug(BCLog::NET, "%s: Timeout\n", protocol);
     267                 :             :                 break; // Retry.
     268                 :             :             }
     269                 :             : 
     270                 :             :             // Receive response.
     271                 :          12 :             recvsz = sock.Recv(response, sizeof(response), MSG_DONTWAIT);
     272         [ +  + ]:          12 :             if (recvsz < 0) {
     273   [ +  -  +  - ]:           1 :                 LogDebug(BCLog::NET, "%s: Could not receive response: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
     274                 :           1 :                 return std::nullopt; // Network-level error, probably no use retrying.
     275                 :             :             }
     276   [ +  -  +  - ]:          11 :             LogDebug(BCLog::NET, "%s: Received response of %d bytes: %s\n", protocol, recvsz, HexStr(std::span(response, recvsz)));
     277                 :             : 
     278         [ -  + ]:          11 :             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         [ +  + ]:          12 :     if (!got_response) {
     285         [ +  - ]:           1 :         LogDebug(BCLog::NET, "%s: Giving up after %d tries\n", protocol, num_tries);
     286                 :           1 :         return std::nullopt;
     287                 :             :     }
     288                 :          11 :     return std::vector<uint8_t>(response, response + recvsz);
     289                 :             : }
     290                 :             : 
     291                 :             : }
     292                 :             : 
     293                 :           3 : 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)
     294                 :             : {
     295                 :           3 :     struct sockaddr_storage dest_addr;
     296                 :           3 :     socklen_t dest_addrlen = sizeof(struct sockaddr_storage);
     297                 :             : 
     298   [ +  -  +  - ]:           3 :     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   [ +  -  -  + ]:           3 :     if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR;
     302         [ -  + ]:           3 :     if (dest_addr.ss_family != AF_INET) return MappingError::NETWORK_ERROR;
     303                 :             : 
     304                 :             :     // Create IPv4 UDP socket
     305                 :           3 :     auto sock{CreateSock(AF_INET, SOCK_DGRAM, IPPROTO_UDP)};
     306         [ -  + ]:           3 :     if (!sock) {
     307   [ #  #  #  # ]:           0 :         LogWarning("natpmp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
     308                 :           0 :         return MappingError::NETWORK_ERROR;
     309                 :             :     }
     310                 :             : 
     311                 :             :     // Associate UDP socket to gateway.
     312   [ +  -  -  + ]:           3 :     if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
     313   [ #  #  #  # ]:           0 :         LogWarning("natpmp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
     314                 :           0 :         return MappingError::NETWORK_ERROR;
     315                 :             :     }
     316                 :             : 
     317                 :             :     // Use getsockname to get the address toward the default gateway (the internal address).
     318                 :           3 :     struct sockaddr_in internal;
     319                 :           3 :     socklen_t internal_addrlen = sizeof(struct sockaddr_in);
     320   [ +  -  -  + ]:           3 :     if (sock->GetSockName((struct sockaddr*)&internal, &internal_addrlen) != 0) {
     321   [ #  #  #  # ]:           0 :         LogWarning("natpmp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
     322                 :           0 :         return MappingError::NETWORK_ERROR;
     323                 :             :     }
     324                 :             : 
     325                 :             :     // Request external IP address (RFC6886 section 3.2).
     326         [ +  - ]:           3 :     std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE);
     327         [ -  + ]:           3 :     request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
     328                 :           3 :     request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_GETEXTERNAL;
     329                 :             : 
     330   [ -  +  +  - ]:           6 :     auto recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
     331         [ -  + ]:           3 :         [&](const std::span<const uint8_t> response) -> bool {
     332         [ -  + ]:           3 :             if (response.size() < NATPMP_GETEXTERNAL_RESPONSE_SIZE) {
     333                 :           0 :                 LogWarning("natpmp: Response too small\n");
     334                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     335                 :             :             }
     336   [ +  -  -  + ]:           3 :             if (response[NATPMP_HDR_VERSION_OFS] != NATPMP_VERSION || response[NATPMP_HDR_OP_OFS] != (NATPMP_RESPONSE | NATPMP_OP_GETEXTERNAL)) {
     337                 :           0 :                 LogWarning("natpmp: Response to wrong command\n");
     338                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     339                 :             :             }
     340                 :             :             return true;
     341                 :             :         },
     342         [ +  - ]:           3 :         interrupt);
     343                 :             : 
     344                 :           3 :     struct in_addr external_addr;
     345         [ +  - ]:           3 :     if (recv_res) {
     346         [ -  + ]:           3 :         const std::span<const uint8_t> response = *recv_res;
     347                 :             : 
     348         [ +  + ]:           3 :         Assume(response.size() >= NATPMP_GETEXTERNAL_RESPONSE_SIZE);
     349         [ +  + ]:           3 :         uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
     350         [ +  + ]:           3 :         if (result_code != NATPMP_RESULT_SUCCESS) {
     351   [ +  -  +  - ]:           1 :             LogWarning("natpmp: Getting external address failed with result %s\n", NATPMPResultString(result_code));
     352                 :           1 :             return MappingError::PROTOCOL_ERROR;
     353                 :             :         }
     354                 :             : 
     355         [ +  - ]:           2 :         std::memcpy(&external_addr, response.data() + NATPMP_GETEXTERNAL_RESPONSE_IP_OFS, ADDR_IPV4_SIZE);
     356                 :             :     } else {
     357                 :           0 :         return MappingError::NETWORK_ERROR;
     358                 :             :     }
     359                 :             : 
     360                 :             :     // Create TCP mapping request (RFC6886 section 3.3).
     361         [ +  - ]:           4 :     request = std::vector<uint8_t>(NATPMP_MAP_REQUEST_SIZE);
     362                 :           2 :     request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
     363                 :           2 :     request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_MAP_TCP;
     364                 :           2 :     WriteBE16(request.data() + NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS, port);
     365                 :           2 :     WriteBE16(request.data() + NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS, port);
     366                 :           2 :     WriteBE32(request.data() + NATPMP_MAP_REQUEST_LIFETIME_OFS, lifetime);
     367                 :             : 
     368   [ +  -  +  - ]:           4 :     recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
     369         [ -  + ]:           2 :         [&](const std::span<const uint8_t> response) -> bool {
     370         [ -  + ]:           2 :             if (response.size() < NATPMP_MAP_RESPONSE_SIZE) {
     371                 :           0 :                 LogWarning("natpmp: Response too small\n");
     372                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     373                 :             :             }
     374   [ +  -  -  + ]:           2 :             if (response[0] != NATPMP_VERSION || response[1] != (NATPMP_RESPONSE | NATPMP_OP_MAP_TCP)) {
     375                 :           0 :                 LogWarning("natpmp: Response to wrong command\n");
     376                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     377                 :             :             }
     378         [ -  + ]:           2 :             uint16_t internal_port = ReadBE16(response.data() + NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS);
     379         [ -  + ]:           2 :             if (internal_port != port) {
     380                 :           0 :                 LogWarning("natpmp: Response port doesn't match request\n");
     381                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     382                 :             :             }
     383                 :             :             return true;
     384                 :             :         },
     385                 :           2 :         interrupt);
     386                 :             : 
     387         [ +  - ]:           2 :     if (recv_res) {
     388         [ -  + ]:           2 :         const std::span<uint8_t> response = *recv_res;
     389                 :             : 
     390         [ +  + ]:           2 :         Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE);
     391         [ +  + ]:           2 :         uint16_t result_code = ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
     392         [ +  + ]:           2 :         if (result_code != NATPMP_RESULT_SUCCESS) {
     393         [ -  + ]:           1 :             if (result_code == NATPMP_RESULT_NOT_AUTHORIZED) {
     394                 :           0 :                 static std::atomic<bool> warned{false};
     395         [ #  # ]:           0 :                 if (!warned.exchange(true)) {
     396   [ #  #  #  # ]:           0 :                     LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
     397                 :             :                 } else {
     398   [ #  #  #  #  :           0 :                     LogDebug(BCLog::NET, "natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
             #  #  #  # ]
     399                 :             :                 }
     400                 :             :             } else {
     401   [ +  -  +  - ]:           1 :                 LogWarning("natpmp: Port mapping failed with result %s\n", NATPMPResultString(result_code));
     402                 :             :             }
     403         [ -  + ]:           1 :             if (result_code == NATPMP_RESULT_NO_RESOURCES) {
     404                 :           0 :                 return MappingError::NO_RESOURCES;
     405                 :             :             }
     406                 :           1 :             return MappingError::PROTOCOL_ERROR;
     407                 :             :         }
     408                 :             : 
     409         [ +  - ]:           1 :         uint32_t lifetime_ret = ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS);
     410         [ +  - ]:           1 :         uint16_t external_port = ReadBE16(response.data() + NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS);
     411   [ +  -  +  - ]:           2 :         return MappingResult(NATPMP_VERSION, CService(internal.sin_addr, port), CService(external_addr, external_port), lifetime_ret);
     412                 :             :     } else {
     413                 :           0 :         return MappingError::NETWORK_ERROR;
     414                 :             :     }
     415                 :           3 : }
     416                 :             : 
     417                 :           8 : 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)
     418                 :             : {
     419                 :           8 :     struct sockaddr_storage dest_addr, bind_addr;
     420                 :           8 :     socklen_t dest_addrlen = sizeof(struct sockaddr_storage), bind_addrlen = sizeof(struct sockaddr_storage);
     421                 :             : 
     422   [ +  -  +  -  :           8 :     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   [ +  -  -  + ]:           8 :     if (!CService(gateway, PCP_SERVER_PORT).GetSockAddr((struct sockaddr*)&dest_addr, &dest_addrlen)) return MappingError::NETWORK_ERROR;
     426   [ +  -  -  + ]:           8 :     if (!CService(bind, 0).GetSockAddr((struct sockaddr*)&bind_addr, &bind_addrlen)) return MappingError::NETWORK_ERROR;
     427         [ -  + ]:           8 :     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                 :           8 :     auto sock{CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)};
     431         [ -  + ]:           8 :     if (!sock) {
     432   [ #  #  #  # ]:           0 :         LogWarning("pcp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
     433                 :           0 :         return MappingError::NETWORK_ERROR;
     434                 :             :     }
     435                 :             : 
     436                 :             :     // Make sure that we send from requested destination address, anything else will be
     437                 :             :     // rejected by a security-conscious router.
     438   [ +  -  -  + ]:           8 :     if (sock->Bind((struct sockaddr*)&bind_addr, bind_addrlen) != 0) {
     439   [ #  #  #  # ]:           0 :         LogWarning("pcp: Could not bind to address: %s\n", NetworkErrorString(WSAGetLastError()));
     440                 :           0 :         return MappingError::NETWORK_ERROR;
     441                 :             :     }
     442                 :             : 
     443                 :             :     // Associate UDP socket to gateway.
     444   [ +  -  -  + ]:           8 :     if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
     445   [ #  #  #  # ]:           0 :         LogWarning("pcp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
     446                 :           0 :         return MappingError::NETWORK_ERROR;
     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                 :           8 :     struct sockaddr_storage internal_addr;
     453                 :           8 :     socklen_t internal_addrlen = sizeof(struct sockaddr_storage);
     454   [ +  -  -  + ]:           8 :     if (sock->GetSockName((struct sockaddr*)&internal_addr, &internal_addrlen) != 0) {
     455   [ #  #  #  # ]:           0 :         LogWarning("pcp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
     456                 :           0 :         return MappingError::NETWORK_ERROR;
     457                 :             :     }
     458         [ +  - ]:           8 :     CService internal;
     459   [ +  -  -  + ]:           8 :     if (!internal.SetSockAddr((struct sockaddr*)&internal_addr, internal_addrlen)) return MappingError::NETWORK_ERROR;
     460   [ +  -  +  -  :           8 :     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         [ +  - ]:           8 :     std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE);
     466                 :             :     // Fill in request header, See RFC6887 Figure 2.
     467                 :           8 :     size_t ofs = 0;
     468                 :           8 :     request[ofs + PCP_HDR_VERSION_OFS] = PCP_VERSION;
     469                 :           8 :     request[ofs + PCP_HDR_OP_OFS] = PCP_REQUEST | PCP_OP_MAP;
     470                 :           8 :     WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime);
     471   [ -  +  +  -  :           8 :     if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_REQUEST_HDR_IP_OFS, ADDR_IPV6_SIZE), internal)) return MappingError::NETWORK_ERROR;
                   -  + ]
     472                 :             : 
     473                 :           8 :     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                 :           8 :     std::memcpy(request.data() + ofs + PCP_MAP_NONCE_OFS, nonce.data(), PCP_MAP_NONCE_SIZE);
     479                 :           8 :     request[ofs + PCP_MAP_PROTOCOL_OFS] = PCP_PROTOCOL_TCP;
     480                 :           8 :     WriteBE16(request.data() + ofs + PCP_MAP_INTERNAL_PORT_OFS, port);
     481                 :           8 :     WriteBE16(request.data() + ofs + PCP_MAP_EXTERNAL_PORT_OFS, port);
     482   [ -  +  +  -  :           8 :     if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE), bind)) return MappingError::NETWORK_ERROR;
                   -  + ]
     483                 :             : 
     484                 :           8 :     ofs += PCP_MAP_SIZE;
     485   [ -  +  +  - ]:           8 :     Assume(ofs == request.size());
     486                 :             : 
     487                 :             :     // Receive loop.
     488                 :           8 :     bool is_natpmp = false;
     489         [ +  - ]:          16 :     auto recv_res = PCPSendRecv(*sock, "pcp", request, num_tries, timeout_per_try,
     490         [ +  - ]:           8 :         [&](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   [ +  +  +  -  :           6 :             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                 :           1 :                 is_natpmp = true;
     494                 :           1 :                 return true; // Let it through to caller.
     495                 :             :             }
     496         [ -  + ]:           5 :             if (response.size() < (PCP_HDR_SIZE + PCP_MAP_SIZE)) {
     497                 :           0 :                 LogWarning("pcp: Response too small\n");
     498                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     499                 :             :             }
     500   [ +  -  +  - ]:           5 :             if (response[PCP_HDR_VERSION_OFS] != PCP_VERSION || response[PCP_HDR_OP_OFS] != (PCP_RESPONSE | PCP_OP_MAP)) {
     501                 :           0 :                 LogWarning("pcp: Response to wrong command\n");
     502                 :           0 :                 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         [ -  + ]:           5 :             if (!std::ranges::equal(response.subspan(PCP_HDR_SIZE + PCP_MAP_NONCE_OFS, PCP_MAP_NONCE_SIZE), nonce)) {
     507                 :           0 :                 LogWarning("pcp: Mapping nonce mismatch\n");
     508                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     509                 :             :             }
     510                 :           5 :             uint8_t protocol = response[PCP_HDR_SIZE + 12];
     511         [ +  - ]:           5 :             uint16_t internal_port = ReadBE16(response.data() + PCP_HDR_SIZE + 16);
     512   [ +  -  -  + ]:           5 :             if (protocol != PCP_PROTOCOL_TCP || internal_port != port) {
     513                 :           0 :                 LogWarning("pcp: Response protocol or port doesn't match request\n");
     514                 :           0 :                 return false; // Wasn't response to what we expected, try receiving next packet.
     515                 :             :             }
     516                 :             :             return true;
     517                 :             :         },
     518         [ +  - ]:           8 :         interrupt);
     519                 :             : 
     520         [ +  + ]:           8 :     if (!recv_res) {
     521                 :           2 :         return MappingError::NETWORK_ERROR;
     522                 :             :     }
     523         [ +  + ]:           6 :     if (is_natpmp) {
     524                 :           1 :         return MappingError::UNSUPP_VERSION;
     525                 :             :     }
     526                 :             : 
     527         [ -  + ]:           5 :     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         [ +  - ]:           5 :     Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE));
     531                 :           5 :     uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS];
     532         [ +  - ]:           5 :     uint32_t lifetime_ret = ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS);
     533         [ +  - ]:           5 :     uint16_t external_port = ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS);
     534         [ +  - ]:           5 :     CNetAddr external_addr{PCPUnwrapAddress(response.subspan(PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS, ADDR_IPV6_SIZE))};
     535         [ +  + ]:           5 :     if (result_code != PCP_RESULT_SUCCESS) {
     536         [ -  + ]:           2 :         if (result_code == PCP_RESULT_NOT_AUTHORIZED) {
     537                 :           0 :             static std::atomic<bool> warned{false};
     538         [ #  # ]:           0 :             if (!warned.exchange(true)) {
     539   [ #  #  #  # ]:           0 :                 LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code));
     540                 :             :             } else {
     541   [ #  #  #  #  :           0 :                 LogDebug(BCLog::NET, "pcp: Mapping failed with result %s\n", PCPResultString(result_code));
             #  #  #  # ]
     542                 :             :             }
     543                 :             :         } else {
     544   [ +  -  +  - ]:           2 :             LogWarning("pcp: Mapping failed with result %s\n", PCPResultString(result_code));
     545                 :             :         }
     546         [ +  + ]:           2 :         if (result_code == PCP_RESULT_NO_RESOURCES) {
     547                 :           1 :             return MappingError::NO_RESOURCES;
     548                 :             :         }
     549                 :           1 :         return MappingError::PROTOCOL_ERROR;
     550                 :             :     }
     551                 :             : 
     552   [ +  -  +  - ]:           6 :     return MappingResult(PCP_VERSION, CService(internal, port), CService(external_addr, external_port), lifetime_ret);
     553                 :          29 : }
     554                 :             : 
     555                 :           0 : std::string MappingResult::ToString() const
     556                 :             : {
     557                 :           0 :     Assume(version == NATPMP_VERSION || version == PCP_VERSION);
     558                 :           0 :     return strprintf("%s:%s -> %s (for %ds)",
     559         [ #  # ]:           0 :             version == NATPMP_VERSION ? "natpmp" : "pcp",
     560         [ #  # ]:           0 :             external.ToStringAddrPort(),
     561                 :           0 :             internal.ToStringAddrPort(),
     562                 :           0 :             lifetime
     563         [ #  # ]:           0 :         );
     564                 :             : }
        

Generated by: LCOV version 2.0-1