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