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