Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
pcp.cpp
Go to the documentation of this file.
1// Copyright (c) 2024-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://www.opensource.org/licenses/mit-license.php.
4
5#include <common/pcp.h>
6
7#include <compat/compat.h>
8#include <crypto/common.h>
9#include <crypto/hex_base.h>
10#include <netaddress.h>
11#include <netbase.h>
12#include <tinyformat.h>
13#include <util/check.h>
14#include <util/log.h>
15#include <util/sock.h>
16#include <util/string.h>
17#include <util/threadinterrupt.h>
18#include <util/time.h>
19
20#include <algorithm>
21#include <compare>
22#include <cstring>
23#include <functional>
24#include <map>
25#include <memory>
26#include <optional>
27#include <span>
28#include <utility>
29#include <vector>
30
31namespace {
32
33// RFC6886 NAT-PMP and RFC6887 Port Control Protocol (PCP) implementation.
34// NAT-PMP and PCP use network byte order (big-endian).
35
36// NAT-PMP (v0) protocol constants.
38constexpr uint16_t NATPMP_SERVER_PORT = 5351;
40constexpr uint8_t NATPMP_VERSION = 0;
42constexpr uint8_t NATPMP_REQUEST = 0x00;
44constexpr uint8_t NATPMP_RESPONSE = 0x80;
46constexpr uint8_t NATPMP_OP_GETEXTERNAL = 0x00;
48constexpr uint8_t NATPMP_OP_MAP_TCP = 0x02;
50constexpr size_t NATPMP_REQUEST_HDR_SIZE = 2;
52constexpr size_t NATPMP_RESPONSE_HDR_SIZE = 8;
61
62// Shared header offsets (RFC6886 3.2, 3.3), relative to start of packet.
64constexpr size_t NATPMP_HDR_VERSION_OFS = 0;
66constexpr size_t NATPMP_HDR_OP_OFS = 1;
68constexpr size_t NATPMP_RESPONSE_HDR_RESULT_OFS = 2;
69
70// GETEXTERNAL response offsets (RFC6886 3.2), relative to start of packet.
72constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_IP_OFS = 8;
73
74// MAP request offsets (RFC6886 3.3), relative to start of packet.
76constexpr size_t NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS = 4;
78constexpr size_t NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS = 6;
80constexpr size_t NATPMP_MAP_REQUEST_LIFETIME_OFS = 8;
81
82// MAP response offsets (RFC6886 3.3), relative to start of packet.
84constexpr size_t NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS = 8;
86constexpr size_t NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS = 10;
88constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12;
89
90// Relevant NETPMP result codes (RFC6886 3.5).
92constexpr uint8_t NATPMP_RESULT_SUCCESS = 0;
99
101const std::map<uint16_t, std::string> NATPMP_RESULT_STR{
102 {0, "SUCCESS"},
103 {1, "UNSUPP_VERSION"},
104 {2, "NOT_AUTHORIZED"},
105 {3, "NETWORK_FAILURE"},
106 {4, "NO_RESOURCES"},
107 {5, "UNSUPP_OPCODE"},
108};
109
110// PCP (v2) protocol constants.
112constexpr size_t PCP_MAX_SIZE = 1100;
116constexpr uint8_t PCP_VERSION = 2;
118constexpr uint8_t PCP_REQUEST = NATPMP_REQUEST; // R = 0
120constexpr uint8_t PCP_RESPONSE = NATPMP_RESPONSE; // R = 1
122constexpr uint8_t PCP_OP_MAP = 0x01;
124constexpr uint16_t PCP_PROTOCOL_TCP = 6;
126constexpr size_t PCP_HDR_SIZE = 24;
128constexpr size_t PCP_MAP_SIZE = 36;
129
130// Header offsets shared between request and responses (RFC6887 7.1, 7.2), relative to start of packet.
134constexpr size_t PCP_HDR_OP_OFS = NATPMP_HDR_OP_OFS;
136constexpr size_t PCP_HDR_LIFETIME_OFS = 4;
137
138// Request header offsets (RFC6887 7.1), relative to start of packet.
140constexpr size_t PCP_REQUEST_HDR_IP_OFS = 8;
141
142// Response header offsets (RFC6887 7.2), relative to start of packet.
144constexpr size_t PCP_RESPONSE_HDR_RESULT_OFS = 3;
145
146// MAP request/response offsets (RFC6887 11.1), relative to start of opcode-specific data.
148constexpr size_t PCP_MAP_NONCE_OFS = 0;
150constexpr size_t PCP_MAP_PROTOCOL_OFS = 12;
152constexpr size_t PCP_MAP_INTERNAL_PORT_OFS = 16;
154constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18;
156constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20;
157
163constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8;
164
166const std::map<uint8_t, std::string> PCP_RESULT_STR{
167 {0, "SUCCESS"},
168 {1, "UNSUPP_VERSION"},
169 {2, "NOT_AUTHORIZED"},
170 {3, "MALFORMED_REQUEST"},
171 {4, "UNSUPP_OPCODE"},
172 {5, "UNSUPP_OPTION"},
173 {6, "MALFORMED_OPTION"},
174 {7, "NETWORK_FAILURE"},
175 {8, "NO_RESOURCES"},
176 {9, "UNSUPP_PROTOCOL"},
177 {10, "USER_EX_QUOTA"},
178 {11, "CANNOT_PROVIDE_EXTERNAL"},
179 {12, "ADDRESS_MISMATCH"},
180 {13, "EXCESSIVE_REMOTE_PEER"},
181};
182
185{
187 return strprintf("%s (code %d)", result_i == NATPMP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code);
188}
189
192{
194 return strprintf("%s (code %d)", result_i == PCP_RESULT_STR.end() ? "(unknown)" : result_i->second, result_code);
195}
196
198[[nodiscard]] bool PCPWrapAddress(std::span<uint8_t> wrapped_addr, const CNetAddr &addr)
199{
201 if (addr.IsIPv4()) {
202 struct in_addr addr4;
203 if (!addr.GetInAddr(&addr4)) return false;
204 // Section 5: "When the address field holds an IPv4 address, an IPv4-mapped IPv6 address [RFC4291] is used (::ffff:0:0/96)."
205 std::memcpy(wrapped_addr.data(), IPV4_IN_IPV6_PREFIX.data(), IPV4_IN_IPV6_PREFIX.size());
206 std::memcpy(wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), &addr4, ADDR_IPV4_SIZE);
207 return true;
208 } else if (addr.IsIPv6()) {
209 struct in6_addr addr6;
210 if (!addr.GetIn6Addr(&addr6)) return false;
211 std::memcpy(wrapped_addr.data(), &addr6, ADDR_IPV6_SIZE);
212 return true;
213 } else {
214 return false;
215 }
216}
217
219CNetAddr PCPUnwrapAddress(std::span<const uint8_t> wrapped_addr)
220{
223 struct in_addr addr4;
224 std::memcpy(&addr4, wrapped_addr.data() + IPV4_IN_IPV6_PREFIX.size(), ADDR_IPV4_SIZE);
225 return CNetAddr(addr4);
226 } else {
227 struct in6_addr addr6;
228 std::memcpy(&addr6, wrapped_addr.data(), ADDR_IPV6_SIZE);
229 return CNetAddr(addr6);
230 }
231}
232
234std::optional<std::vector<uint8_t>> PCPSendRecv(Sock &sock, const std::string &protocol, std::span<const uint8_t> request, int num_tries,
235 std::chrono::milliseconds timeout_per_try,
236 std::function<bool(std::span<const uint8_t>)> check_packet,
237 CThreadInterrupt& interrupt)
238{
239 using namespace std::chrono;
240 // UDP is a potentially lossy protocol, so we try to send again a few times.
242 bool got_response = false;
243 int recvsz = 0;
244 for (int ntry = 0; !got_response && ntry < num_tries; ++ntry) {
245 if (ntry > 0) {
246 LogDebug(BCLog::NET, "%s: Retrying (%d)\n", protocol, ntry);
247 }
248 // Dispatch packet to gateway.
249 if (sock.Send(request.data(), request.size(), 0) != static_cast<ssize_t>(request.size())) {
250 LogDebug(BCLog::NET, "%s: Could not send request: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
251 return std::nullopt; // Network-level error, probably no use retrying.
252 }
253
254 // Wait for response(s) until we get a valid response, a network error, or time out.
258 if (interrupt) return std::nullopt;
259 Sock::Event occurred = 0;
260 if (!sock.Wait(deadline - cur_time, Sock::RecvEvent, &occurred)) {
261 LogWarning("%s: Could not wait on socket: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
262 return std::nullopt; // Network-level error, probably no use retrying.
263 }
264 if (!occurred) {
265 LogDebug(BCLog::NET, "%s: Timeout\n", protocol);
266 break; // Retry.
267 }
268
269 // Receive response.
270 recvsz = sock.Recv(response, sizeof(response), MSG_DONTWAIT);
271 if (recvsz < 0) {
272 LogDebug(BCLog::NET, "%s: Could not receive response: %s\n", protocol, NetworkErrorString(WSAGetLastError()));
273 return std::nullopt; // Network-level error, probably no use retrying.
274 }
275 LogDebug(BCLog::NET, "%s: Received response of %d bytes: %s\n", protocol, recvsz, HexStr(std::span(response, recvsz)));
276
277 if (check_packet(std::span<uint8_t>(response, recvsz))) {
278 got_response = true; // Got expected response, break from receive loop as well as from retry loop.
279 break;
280 }
281 }
282 }
283 if (!got_response) {
284 LogDebug(BCLog::NET, "%s: Giving up after %d tries\n", protocol, num_tries);
285 return std::nullopt;
286 }
287 return std::vector<uint8_t>(response, response + recvsz);
288}
289
290}
291
292std::variant<MappingResult, MappingError> NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime, CThreadInterrupt& interrupt, int num_tries, std::chrono::milliseconds timeout_per_try)
293{
295 socklen_t dest_addrlen = sizeof(struct sockaddr_storage);
296
297 LogDebug(BCLog::NET, "natpmp: Requesting port mapping port %d from gateway %s\n", port, gateway.ToStringAddr());
298
299 // Validate gateway, make sure it's IPv4. NAT-PMP does not support IPv6.
301 if (dest_addr.ss_family != AF_INET) return MappingError::NETWORK_ERROR;
302
303 // Create IPv4 UDP socket
305 if (!sock) {
306 LogWarning("natpmp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
308 }
309
310 // Associate UDP socket to gateway.
311 if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
312 LogWarning("natpmp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
314 }
315
316 // Use getsockname to get the address toward the default gateway (the internal address).
317 struct sockaddr_in internal;
318 socklen_t internal_addrlen = sizeof(struct sockaddr_in);
319 if (sock->GetSockName((struct sockaddr*)&internal, &internal_addrlen) != 0) {
320 LogWarning("natpmp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
322 }
323
324 // Request external IP address (RFC6886 section 3.2).
325 std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE);
328
329 auto recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
330 [&](const std::span<const uint8_t> response) -> bool {
332 LogWarning("natpmp: Response too small\n");
333 return false; // Wasn't response to what we expected, try receiving next packet.
334 }
336 LogWarning("natpmp: Response to wrong command\n");
337 return false; // Wasn't response to what we expected, try receiving next packet.
338 }
339 return true;
340 },
341 interrupt);
342
343 struct in_addr external_addr;
344 if (recv_res) {
345 const std::span<const uint8_t> response = *recv_res;
346
350 LogWarning("natpmp: Getting external address failed with result %s\n", NATPMPResultString(result_code));
352 }
353
355 } else {
357 }
358
359 // Create TCP mapping request (RFC6886 section 3.3).
360 request = std::vector<uint8_t>(NATPMP_MAP_REQUEST_SIZE);
363 WriteBE16(request.data() + NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS, port);
364 WriteBE16(request.data() + NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS, port);
365 WriteBE32(request.data() + NATPMP_MAP_REQUEST_LIFETIME_OFS, lifetime);
366
367 recv_res = PCPSendRecv(*sock, "natpmp", request, num_tries, timeout_per_try,
368 [&](const std::span<const uint8_t> response) -> bool {
369 if (response.size() < NATPMP_MAP_RESPONSE_SIZE) {
370 LogWarning("natpmp: Response too small\n");
371 return false; // Wasn't response to what we expected, try receiving next packet.
372 }
374 LogWarning("natpmp: Response to wrong command\n");
375 return false; // Wasn't response to what we expected, try receiving next packet.
376 }
378 if (internal_port != port) {
379 LogWarning("natpmp: Response port doesn't match request\n");
380 return false; // Wasn't response to what we expected, try receiving next packet.
381 }
382 return true;
383 },
384 interrupt);
385
386 if (recv_res) {
387 const std::span<uint8_t> response = *recv_res;
388
393 LogWarnThenDebug(BCLog::NET, "natpmp: Port mapping failed with result %s", NATPMPResultString(result_code));
394 } else {
395 LogWarning("natpmp: Port mapping failed with result %s", NATPMPResultString(result_code));
396 }
399 }
401 }
402
406 } else {
408 }
409}
410
411std::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)
412{
414 socklen_t dest_addrlen = sizeof(struct sockaddr_storage), bind_addrlen = sizeof(struct sockaddr_storage);
415
416 LogDebug(BCLog::NET, "pcp: Requesting port mapping for addr %s port %d from gateway %s\n", bind.ToStringAddr(), port, gateway.ToStringAddr());
417
418 // Validate addresses, make sure they're the same network family.
420 if (!CService(bind, 0).GetSockAddr((struct sockaddr*)&bind_addr, &bind_addrlen)) return MappingError::NETWORK_ERROR;
421 if (dest_addr.ss_family != bind_addr.ss_family) return MappingError::NETWORK_ERROR;
422
423 // Create UDP socket (IPv4 or IPv6 based on provided gateway).
424 auto sock{CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)};
425 if (!sock) {
426 LogWarning("pcp: Could not create UDP socket: %s\n", NetworkErrorString(WSAGetLastError()));
428 }
429
430 // Make sure that we send from requested destination address, anything else will be
431 // rejected by a security-conscious router.
432 if (sock->Bind((struct sockaddr*)&bind_addr, bind_addrlen) != 0) {
433 LogWarning("pcp: Could not bind to address: %s\n", NetworkErrorString(WSAGetLastError()));
435 }
436
437 // Associate UDP socket to gateway.
438 if (sock->Connect((struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
439 LogWarning("pcp: Could not connect to gateway: %s\n", NetworkErrorString(WSAGetLastError()));
441 }
442
443 // Use getsockname to get the address toward the default gateway (the internal address),
444 // in case we don't know what address to map
445 // (this is only needed if bind is INADDR_ANY, but it doesn't hurt as an extra check).
448 if (sock->GetSockName((struct sockaddr*)&internal_addr, &internal_addrlen) != 0) {
449 LogWarning("pcp: Could not get sock name: %s\n", NetworkErrorString(WSAGetLastError()));
451 }
452 CService internal;
454 LogDebug(BCLog::NET, "pcp: Internal address after connect: %s\n", internal.ToStringAddr());
455
456 // Build request packet. Make sure the packet is zeroed so that reserved fields are zero
457 // as required by the spec (and not potentially leak data).
458 // Make sure there's space for the request header and MAP specific request data.
459 std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE);
460 // Fill in request header, See RFC6887 Figure 2.
461 size_t ofs = 0;
464 WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime);
465 if (!PCPWrapAddress(std::span(request).subspan(ofs + PCP_REQUEST_HDR_IP_OFS, ADDR_IPV6_SIZE), internal)) return MappingError::NETWORK_ERROR;
466
467 ofs += PCP_HDR_SIZE;
468
469 // Fill in MAP request packet, See RFC6887 Figure 9.
470 // Randomize mapping nonce (this is repeated in the response, to be able to
471 // correlate requests and responses, and used to authenticate changes to the mapping).
472 std::memcpy(request.data() + ofs + PCP_MAP_NONCE_OFS, nonce.data(), PCP_MAP_NONCE_SIZE);
474 WriteBE16(request.data() + ofs + PCP_MAP_INTERNAL_PORT_OFS, port);
475 WriteBE16(request.data() + ofs + PCP_MAP_EXTERNAL_PORT_OFS, port);
477
478 ofs += PCP_MAP_SIZE;
479 Assume(ofs == request.size());
480
481 // Receive loop.
482 bool is_natpmp = false;
483 auto recv_res = PCPSendRecv(*sock, "pcp", request, num_tries, timeout_per_try,
484 [&](const std::span<const uint8_t> response) -> bool {
485 // Unsupported version according to RFC6887 appendix A and RFC6886 section 3.5, can fall back to NAT-PMP.
487 is_natpmp = true;
488 return true; // Let it through to caller.
489 }
490 if (response.size() < (PCP_HDR_SIZE + PCP_MAP_SIZE)) {
491 LogWarning("pcp: Response too small\n");
492 return false; // Wasn't response to what we expected, try receiving next packet.
493 }
495 LogWarning("pcp: Response to wrong command\n");
496 return false; // Wasn't response to what we expected, try receiving next packet.
497 }
498 // Handle MAP opcode response. See RFC6887 Figure 10.
499 // Check that returned mapping nonce matches our request.
500 if (!std::ranges::equal(response.subspan(PCP_HDR_SIZE + PCP_MAP_NONCE_OFS, PCP_MAP_NONCE_SIZE), nonce)) {
501 LogWarning("pcp: Mapping nonce mismatch\n");
502 return false; // Wasn't response to what we expected, try receiving next packet.
503 }
506 if (protocol != PCP_PROTOCOL_TCP || internal_port != port) {
507 LogWarning("pcp: Response protocol or port doesn't match request\n");
508 return false; // Wasn't response to what we expected, try receiving next packet.
509 }
510 return true;
511 },
512 interrupt);
513
514 if (!recv_res) {
516 }
517 if (is_natpmp) {
519 }
520
521 const std::span<const uint8_t> response = *recv_res;
522 // If we get here, we got a valid MAP response to our request.
523 // Check to see if we got the result we expected.
531 LogWarnThenDebug(BCLog::NET, "pcp: Mapping failed with result %s", PCPResultString(result_code));
532 } else {
533 LogWarning("pcp: Mapping failed with result %s", PCPResultString(result_code));
534 }
537 }
539 }
540
542}
543
544std::string MappingResult::ToString() const
545{
547 return strprintf("%s:%s -> %s (for %ds)",
548 version == NATPMP_VERSION ? "natpmp" : "pcp",
552 );
553}
if(!SetupNetworking())
#define Assume(val)
Assume is the identity function.
Definition check.h:131
Network address.
Definition netaddress.h:113
std::string ToStringAddr() const
bool GetIn6Addr(struct in6_addr *pipv6Addr) const
Try to get our IPv6 (or CJDNS) address.
bool GetInAddr(struct in_addr *pipv4Addr) const
Try to get our IPv4 address.
bool IsIPv4() const
Definition netaddress.h:158
bool IsIPv6() const
Definition netaddress.h:159
A combination of a network address (CNetAddr) and a (TCP) port.
Definition netaddress.h:531
bool SetSockAddr(const struct sockaddr *paddr, socklen_t addrlen)
Set CService from a network sockaddr.
std::string ToStringAddrPort() const
A helper class for interruptible sleeps.
RAII helper class that manages a socket and closes it automatically when it goes out of scope.
Definition sock.h:35
static constexpr Event RecvEvent
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition sock.h:151
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
Definition sock.cpp:47
virtual int Bind(const sockaddr *addr, socklen_t addr_len) const
bind(2) wrapper.
Definition sock.cpp:62
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
Definition sock.cpp:141
uint8_t Event
Definition sock.h:146
virtual int GetSockName(sockaddr *name, socklen_t *name_len) const
getsockname(2) wrapper.
Definition sock.cpp:108
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
Definition sock.cpp:57
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
Definition sock.cpp:52
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)
Try to open a port using RFC 6886 NAT-PMP.
Definition pcp.cpp:292
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)
Try to open a port using RFC 6887 Port Control Protocol (PCP).
Definition pcp.cpp:411
#define WSAGetLastError()
Definition compat.h:59
#define MSG_DONTWAIT
Definition compat.h:115
void WriteBE32(B *ptr, uint32_t x)
Definition common.h:95
void WriteBE16(B *ptr, uint16_t x)
Definition common.h:88
uint16_t ReadBE16(const B *ptr)
Definition common.h:64
uint32_t ReadBE32(const B *ptr)
Definition common.h:72
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition hex_base.cpp:30
#define LogWarning(...)
Definition log.h:137
#define LogWarnThenDebug(category,...)
Definition log.h:158
#define LogDebug(category,...)
Definition log.h:154
unsigned int nonce
bool HasPrefix(const T1 &obj, const std::array< uint8_t, PREFIX_LEN > &prefix)
Check whether a container begins with the given prefix.
Definition string.h:262
constexpr size_t ADDR_IPV4_SIZE
Size of IPv4 address (in bytes).
Definition netaddress.h:86
constexpr size_t ADDR_IPV6_SIZE
Size of IPv6 address (in bytes).
Definition netaddress.h:89
constexpr std::array< uint8_t, 12 > IPV4_IN_IPV6_PREFIX
Prefix of an IPv6 address when it contains an embedded IPv4 address.
Definition netaddress.h:62
std::function< std::unique_ptr< Sock >(int, int, int)> CreateSock
Socket factory.
Definition netbase.cpp:577
std::array< uint8_t, PCP_MAP_NONCE_SIZE > PCPMappingNonce
PCP mapping nonce. Arbitrary data chosen by the client to identify a mapping.
Definition pcp.h:26
constexpr size_t PCP_MAP_NONCE_SIZE
Mapping nonce size in bytes (see RFC6887 section 11.1).
Definition pcp.h:23
@ PROTOCOL_ERROR
Any kind of protocol-level error, except unsupported version or no resources.
@ NO_RESOURCES
No resources available (port probably already mapped).
@ UNSUPP_VERSION
Unsupported protocol version.
@ NETWORK_ERROR
Any kind of network-level error.
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition sock.cpp:428
Successful response to a port mapping.
Definition pcp.h:37
CService external
External host:port.
Definition pcp.h:45
uint32_t lifetime
Granted lifetime of binding (seconds).
Definition pcp.h:47
CService internal
Internal host:port.
Definition pcp.h:43
std::string ToString() const
Format mapping as string for logging.
Definition pcp.cpp:544
uint8_t version
Protocol version, one of NATPMP_VERSION or PCP_VERSION.
Definition pcp.h:41
static time_point now() noexcept
Return current system time or mocked time, if set.
Definition time.cpp:64
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82