27constexpr uint16_t NATPMP_SERVER_PORT = 5351;
29constexpr uint8_t NATPMP_VERSION = 0;
31constexpr uint8_t NATPMP_REQUEST = 0x00;
33constexpr uint8_t NATPMP_RESPONSE = 0x80;
35constexpr uint8_t NATPMP_OP_GETEXTERNAL = 0x00;
37constexpr uint8_t NATPMP_OP_MAP_TCP = 0x02;
39constexpr size_t NATPMP_REQUEST_HDR_SIZE = 2;
41constexpr size_t NATPMP_RESPONSE_HDR_SIZE = 8;
43constexpr size_t NATPMP_GETEXTERNAL_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 0;
45constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 4;
47constexpr size_t NATPMP_MAP_REQUEST_SIZE = NATPMP_REQUEST_HDR_SIZE + 10;
49constexpr size_t NATPMP_MAP_RESPONSE_SIZE = NATPMP_RESPONSE_HDR_SIZE + 8;
53constexpr size_t NATPMP_HDR_VERSION_OFS = 0;
55constexpr size_t NATPMP_HDR_OP_OFS = 1;
57constexpr size_t NATPMP_RESPONSE_HDR_RESULT_OFS = 2;
61constexpr size_t NATPMP_GETEXTERNAL_RESPONSE_IP_OFS = 8;
65constexpr size_t NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS = 4;
67constexpr size_t NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS = 6;
69constexpr size_t NATPMP_MAP_REQUEST_LIFETIME_OFS = 8;
73constexpr size_t NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS = 8;
75constexpr size_t NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS = 10;
77constexpr size_t NATPMP_MAP_RESPONSE_LIFETIME_OFS = 12;
81constexpr uint8_t NATPMP_RESULT_SUCCESS = 0;
83constexpr uint8_t NATPMP_RESULT_UNSUPP_VERSION = 1;
85constexpr uint8_t NATPMP_RESULT_NO_RESOURCES = 4;
88const std::map<uint16_t, std::string> NATPMP_RESULT_STR{
90 {1,
"UNSUPP_VERSION"},
91 {2,
"NOT_AUTHORIZED"},
92 {3,
"NETWORK_FAILURE"},
99constexpr size_t PCP_MAX_SIZE = 1100;
101constexpr uint16_t PCP_SERVER_PORT = NATPMP_SERVER_PORT;
103constexpr uint8_t PCP_VERSION = 2;
105constexpr uint8_t PCP_REQUEST = NATPMP_REQUEST;
107constexpr uint8_t PCP_RESPONSE = NATPMP_RESPONSE;
109constexpr uint8_t PCP_OP_MAP = 0x01;
111constexpr uint16_t PCP_PROTOCOL_TCP = 6;
113constexpr size_t PCP_HDR_SIZE = 24;
115constexpr size_t PCP_MAP_SIZE = 36;
119constexpr size_t PCP_HDR_VERSION_OFS = NATPMP_HDR_VERSION_OFS;
121constexpr size_t PCP_HDR_OP_OFS = NATPMP_HDR_OP_OFS;
123constexpr size_t PCP_HDR_LIFETIME_OFS = 4;
127constexpr size_t PCP_REQUEST_HDR_IP_OFS = 8;
131constexpr size_t PCP_RESPONSE_HDR_RESULT_OFS = 3;
135constexpr size_t PCP_MAP_NONCE_OFS = 0;
137constexpr size_t PCP_MAP_PROTOCOL_OFS = 12;
139constexpr size_t PCP_MAP_INTERNAL_PORT_OFS = 16;
141constexpr size_t PCP_MAP_EXTERNAL_PORT_OFS = 18;
143constexpr size_t PCP_MAP_EXTERNAL_IP_OFS = 20;
146constexpr uint8_t PCP_RESULT_SUCCESS = NATPMP_RESULT_SUCCESS;
148constexpr uint8_t PCP_RESULT_NO_RESOURCES = 8;
151const std::map<uint8_t, std::string> PCP_RESULT_STR{
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"},
161 {9,
"UNSUPP_PROTOCOL"},
162 {10,
"USER_EX_QUOTA"},
163 {11,
"CANNOT_PROVIDE_EXTERNAL"},
164 {12,
"ADDRESS_MISMATCH"},
165 {13,
"EXCESSIVE_REMOTE_PEER"},
169std::string NATPMPResultString(uint16_t result_code)
171 auto result_i = NATPMP_RESULT_STR.find(result_code);
172 return strprintf(
"%s (code %d)", result_i == NATPMP_RESULT_STR.end() ?
"(unknown)" : result_i->second, result_code);
176std::string PCPResultString(uint8_t result_code)
178 auto result_i = PCP_RESULT_STR.find(result_code);
179 return strprintf(
"%s (code %d)", result_i == PCP_RESULT_STR.end() ?
"(unknown)" : result_i->second, result_code);
183[[nodiscard]]
bool PCPWrapAddress(std::span<uint8_t> wrapped_addr,
const CNetAddr &addr)
187 struct in_addr addr4;
188 if (!addr.
GetInAddr(&addr4))
return false;
193 }
else if (addr.
IsIPv6()) {
194 struct in6_addr addr6;
204CNetAddr PCPUnwrapAddress(std::span<const uint8_t> wrapped_addr)
208 struct in_addr addr4;
212 struct in6_addr addr6;
219std::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,
224 using namespace std::chrono;
226 uint8_t response[PCP_MAX_SIZE];
227 bool got_response =
false;
229 for (
int ntry = 0; !got_response && ntry < num_tries; ++ntry) {
234 if (sock.
Send(request.data(), request.size(), 0) !=
static_cast<ssize_t
>(request.size())) {
241 auto deadline = cur_time + timeout_per_try;
243 if (interrupt)
return std::nullopt;
262 if (check_packet(std::span<uint8_t>(response, recvsz))) {
272 return std::vector<uint8_t>(response, response + recvsz);
279 struct sockaddr_storage dest_addr;
280 socklen_t dest_addrlen =
sizeof(
struct sockaddr_storage);
289 auto sock{
CreateSock(AF_INET, SOCK_DGRAM, IPPROTO_UDP)};
296 if (sock->
Connect((
struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
302 struct sockaddr_in internal;
303 socklen_t internal_addrlen =
sizeof(
struct sockaddr_in);
304 if (sock->
GetSockName((
struct sockaddr*)&internal, &internal_addrlen) != 0) {
310 std::vector<uint8_t> request(NATPMP_GETEXTERNAL_REQUEST_SIZE);
311 request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
312 request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_GETEXTERNAL;
314 auto recv_res = PCPSendRecv(*sock,
"natpmp", request, num_tries, timeout_per_try,
315 [&](
const std::span<const uint8_t> response) ->
bool {
316 if (response.size() < NATPMP_GETEXTERNAL_RESPONSE_SIZE) {
317 LogPrintLevel(BCLog::NET, BCLog::Level::Warning,
"natpmp: Response too small\n");
320 if (response[NATPMP_HDR_VERSION_OFS] != NATPMP_VERSION || response[NATPMP_HDR_OP_OFS] != (NATPMP_RESPONSE | NATPMP_OP_GETEXTERNAL)) {
328 struct in_addr external_addr;
330 const std::span<const uint8_t> response = *recv_res;
332 Assume(response.size() >= NATPMP_GETEXTERNAL_RESPONSE_SIZE);
333 uint16_t result_code =
ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
334 if (result_code != NATPMP_RESULT_SUCCESS) {
339 std::memcpy(&external_addr, response.data() + NATPMP_GETEXTERNAL_RESPONSE_IP_OFS,
ADDR_IPV4_SIZE);
345 request = std::vector<uint8_t>(NATPMP_MAP_REQUEST_SIZE);
346 request[NATPMP_HDR_VERSION_OFS] = NATPMP_VERSION;
347 request[NATPMP_HDR_OP_OFS] = NATPMP_REQUEST | NATPMP_OP_MAP_TCP;
348 WriteBE16(request.data() + NATPMP_MAP_REQUEST_INTERNAL_PORT_OFS, port);
349 WriteBE16(request.data() + NATPMP_MAP_REQUEST_EXTERNAL_PORT_OFS, port);
350 WriteBE32(request.data() + NATPMP_MAP_REQUEST_LIFETIME_OFS, lifetime);
352 recv_res = PCPSendRecv(*sock,
"natpmp", request, num_tries, timeout_per_try,
353 [&](
const std::span<const uint8_t> response) ->
bool {
354 if (response.size() < NATPMP_MAP_RESPONSE_SIZE) {
355 LogPrintLevel(BCLog::NET, BCLog::Level::Warning,
"natpmp: Response too small\n");
358 if (response[0] != NATPMP_VERSION || response[1] != (NATPMP_RESPONSE | NATPMP_OP_MAP_TCP)) {
362 uint16_t internal_port =
ReadBE16(response.data() + NATPMP_MAP_RESPONSE_INTERNAL_PORT_OFS);
363 if (internal_port != port) {
372 const std::span<uint8_t> response = *recv_res;
374 Assume(response.size() >= NATPMP_MAP_RESPONSE_SIZE);
375 uint16_t result_code =
ReadBE16(response.data() + NATPMP_RESPONSE_HDR_RESULT_OFS);
376 if (result_code != NATPMP_RESULT_SUCCESS) {
378 if (result_code == NATPMP_RESULT_NO_RESOURCES) {
384 uint32_t lifetime_ret =
ReadBE32(response.data() + NATPMP_MAP_RESPONSE_LIFETIME_OFS);
385 uint16_t external_port =
ReadBE16(response.data() + NATPMP_MAP_RESPONSE_EXTERNAL_PORT_OFS);
394 struct sockaddr_storage dest_addr, bind_addr;
395 socklen_t dest_addrlen =
sizeof(
struct sockaddr_storage), bind_addrlen =
sizeof(
struct sockaddr_storage);
405 auto sock{
CreateSock(dest_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)};
413 if (sock->
Bind((
struct sockaddr*)&bind_addr, bind_addrlen) != 0) {
419 if (sock->
Connect((
struct sockaddr*)&dest_addr, dest_addrlen) != 0) {
427 struct sockaddr_storage internal_addr;
428 socklen_t internal_addrlen =
sizeof(
struct sockaddr_storage);
429 if (sock->
GetSockName((
struct sockaddr*)&internal_addr, &internal_addrlen) != 0) {
440 std::vector<uint8_t> request(PCP_HDR_SIZE + PCP_MAP_SIZE);
443 request[ofs + PCP_HDR_VERSION_OFS] = PCP_VERSION;
444 request[ofs + PCP_HDR_OP_OFS] = PCP_REQUEST | PCP_OP_MAP;
445 WriteBE32(request.data() + ofs + PCP_HDR_LIFETIME_OFS, lifetime);
454 request[ofs + PCP_MAP_PROTOCOL_OFS] = PCP_PROTOCOL_TCP;
455 WriteBE16(request.data() + ofs + PCP_MAP_INTERNAL_PORT_OFS, port);
456 WriteBE16(request.data() + ofs + PCP_MAP_EXTERNAL_PORT_OFS, port);
460 Assume(ofs == request.size());
463 bool is_natpmp =
false;
464 auto recv_res = PCPSendRecv(*sock,
"pcp", request, num_tries, timeout_per_try,
465 [&](
const std::span<const uint8_t> response) ->
bool {
467 if (response.size() == NATPMP_RESPONSE_HDR_SIZE && response[PCP_HDR_VERSION_OFS] == NATPMP_VERSION && response[PCP_RESPONSE_HDR_RESULT_OFS] == NATPMP_RESULT_UNSUPP_VERSION) {
471 if (response.size() < (PCP_HDR_SIZE + PCP_MAP_SIZE)) {
472 LogPrintLevel(BCLog::NET, BCLog::Level::Warning,
"pcp: Response too small\n");
475 if (response[PCP_HDR_VERSION_OFS] != PCP_VERSION || response[PCP_HDR_OP_OFS] != (PCP_RESPONSE | PCP_OP_MAP)) {
485 uint8_t protocol = response[PCP_HDR_SIZE + 12];
486 uint16_t internal_port =
ReadBE16(response.data() + PCP_HDR_SIZE + 16);
487 if (protocol != PCP_PROTOCOL_TCP || internal_port != port) {
502 const std::span<const uint8_t> response = *recv_res;
505 Assume(response.size() >= (PCP_HDR_SIZE + PCP_MAP_SIZE));
506 uint8_t result_code = response[PCP_RESPONSE_HDR_RESULT_OFS];
507 uint32_t lifetime_ret =
ReadBE32(response.data() + PCP_HDR_LIFETIME_OFS);
508 uint16_t external_port =
ReadBE16(response.data() + PCP_HDR_SIZE + PCP_MAP_EXTERNAL_PORT_OFS);
509 CNetAddr external_addr{PCPUnwrapAddress(response.subspan(PCP_HDR_SIZE + PCP_MAP_EXTERNAL_IP_OFS,
ADDR_IPV6_SIZE))};
510 if (result_code != PCP_RESULT_SUCCESS) {
512 if (result_code == PCP_RESULT_NO_RESOURCES) {
524 return strprintf(
"%s:%s -> %s (for %ds)",
525 version == NATPMP_VERSION ?
"natpmp" :
"pcp",
#define Assume(val)
Assume is the identity function.
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.
A combination of a network address (CNetAddr) and a (TCP) port.
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.
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
virtual int Bind(const sockaddr *addr, socklen_t addr_len) const
bind(2) wrapper.
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
virtual int GetSockName(sockaddr *name, socklen_t *name_len) const
getsockname(2) wrapper.
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
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.
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).
#define WSAGetLastError()
void WriteBE32(B *ptr, uint32_t x)
void WriteBE16(B *ptr, uint16_t x)
uint16_t ReadBE16(const B *ptr)
uint32_t ReadBE32(const B *ptr)
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
#define LogPrintLevel(category, level,...)
bool HasPrefix(const T1 &obj, const std::array< uint8_t, PREFIX_LEN > &prefix)
Check whether a container begins with the given prefix.
static constexpr size_t ADDR_IPV4_SIZE
Size of IPv4 address (in bytes).
static const std::array< uint8_t, 12 > IPV4_IN_IPV6_PREFIX
Prefix of an IPv6 address when it contains an embedded IPv4 address.
static constexpr size_t ADDR_IPV6_SIZE
Size of IPv6 address (in bytes).
std::function< std::unique_ptr< Sock >(int, int, int)> CreateSock
Socket factory.
std::array< uint8_t, PCP_MAP_NONCE_SIZE > PCPMappingNonce
PCP mapping nonce. Arbitrary data chosen by the client to identify a mapping.
constexpr size_t PCP_MAP_NONCE_SIZE
Mapping nonce size in bytes (see RFC6887 section 11.1).
@ 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.
Successful response to a port mapping.
CService external
External host:port.
uint32_t lifetime
Granted lifetime of binding (seconds).
CService internal
Internal host:port.
std::string ToString() const
Format mapping as string for logging.
uint8_t version
Protocol version, one of NATPMP_VERSION or PCP_VERSION.
static time_point now() noexcept
Return current system time or mocked time, if set.