mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-14 05:42:36 -03:00
35fbc97208
c9d548c91f
net: remove CService::ToStringPort() (Vasil Dimov)fd4f0f41e9
gui: simplify OptionsDialog::updateDefaultProxyNets() (Vasil Dimov)96c791dd20
net: remove CService::ToString() use ToStringAddrPort() instead (Vasil Dimov)944a9de08a
net: remove CNetAddr::ToString() and use ToStringAddr() instead (Vasil Dimov)043b9de59a
scripted-diff: rename ToStringIP[Port]() to ToStringAddr[Port]() (Vasil Dimov) Pull request description: Before this PR we had the somewhat confusing combination of methods: `CNetAddr::ToStringIP()` `CNetAddr::ToString()` (duplicate of the above) `CService::ToStringIPPort()` `CService::ToString()` (duplicate of the above, overrides a non-virtual method from `CNetAddr`) `CService::ToStringPort()` Avoid [overriding non-virtual methods](https://github.com/bitcoin/bitcoin/pull/25349/#issuecomment-1185226396). "IP" stands for "Internet Protocol" and while sometimes "IP addresses" are called just "IPs", it is incorrect to call Tor or I2P addresses "IPs". Thus use "Addr" instead of "IP". Change the above to: `CNetAddr::ToStringAddr()` `CService::ToStringAddrPort()` The changes touch a lot of files, but are mostly mechanical. ACKs for top commit: sipa: utACKc9d548c91f
achow101: ACKc9d548c91f
jonatack: re-ACKc9d548c91f
only change since my previous reviews is rebase, but as a sanity check rebased to current master and at each commit quickly re-reviewed and re-verified clean build and green unit tests LarryRuane: ACKc9d548c91f
Tree-SHA512: 633fb044bdecf9f551b5e3314c385bf10e2b78e8027dc51ec324b66b018da35e5b01f3fbe6295bbc455ea1bcd1a3629de1918d28de510693afaf6a52693f2157
335 lines
11 KiB
C++
335 lines
11 KiB
C++
// Copyright (c) 2011-2022 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config/bitcoin-config.h>
|
|
#endif
|
|
|
|
#include <mapport.h>
|
|
|
|
#include <clientversion.h>
|
|
#include <logging.h>
|
|
#include <net.h>
|
|
#include <netaddress.h>
|
|
#include <netbase.h>
|
|
#include <util/syscall_sandbox.h>
|
|
#include <util/system.h>
|
|
#include <util/thread.h>
|
|
#include <util/threadinterrupt.h>
|
|
|
|
#ifdef USE_NATPMP
|
|
#include <compat/compat.h>
|
|
#include <natpmp.h>
|
|
#endif // USE_NATPMP
|
|
|
|
#ifdef USE_UPNP
|
|
#include <miniupnpc/miniupnpc.h>
|
|
#include <miniupnpc/upnpcommands.h>
|
|
#include <miniupnpc/upnperrors.h>
|
|
// The minimum supported miniUPnPc API version is set to 17. This excludes
|
|
// versions with known vulnerabilities.
|
|
static_assert(MINIUPNPC_API_VERSION >= 17, "miniUPnPc API version >= 17 assumed");
|
|
#endif // USE_UPNP
|
|
|
|
#include <atomic>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#if defined(USE_NATPMP) || defined(USE_UPNP)
|
|
static CThreadInterrupt g_mapport_interrupt;
|
|
static std::thread g_mapport_thread;
|
|
static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
|
|
static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
|
|
|
|
using namespace std::chrono_literals;
|
|
static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
|
|
static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
|
|
|
|
#ifdef USE_NATPMP
|
|
static uint16_t g_mapport_external_port = 0;
|
|
static bool NatpmpInit(natpmp_t* natpmp)
|
|
{
|
|
const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0);
|
|
if (r_init == 0) return true;
|
|
LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init);
|
|
return false;
|
|
}
|
|
|
|
static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr)
|
|
{
|
|
const int r_send = sendpublicaddressrequest(natpmp);
|
|
if (r_send == 2 /* OK */) {
|
|
int r_read;
|
|
natpmpresp_t response;
|
|
do {
|
|
r_read = readnatpmpresponseorretry(natpmp, &response);
|
|
} while (r_read == NATPMP_TRYAGAIN);
|
|
|
|
if (r_read == 0) {
|
|
external_ipv4_addr = response.pnu.publicaddress.addr;
|
|
return true;
|
|
} else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
|
|
LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
|
|
} else {
|
|
LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
|
|
}
|
|
} else {
|
|
LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered)
|
|
{
|
|
const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
|
|
const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/);
|
|
if (r_send == 12 /* OK */) {
|
|
int r_read;
|
|
natpmpresp_t response;
|
|
do {
|
|
r_read = readnatpmpresponseorretry(natpmp, &response);
|
|
} while (r_read == NATPMP_TRYAGAIN);
|
|
|
|
if (r_read == 0) {
|
|
auto pm = response.pnu.newportmapping;
|
|
if (private_port == pm.privateport && pm.lifetime > 0) {
|
|
g_mapport_external_port = pm.mappedpublicport;
|
|
const CService external{external_ipv4_addr, pm.mappedpublicport};
|
|
if (!external_ip_discovered && fDiscover) {
|
|
AddLocal(external, LOCAL_MAPPED);
|
|
external_ip_discovered = true;
|
|
}
|
|
LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToStringAddrPort());
|
|
return true;
|
|
} else {
|
|
LogPrintf("natpmp: Port mapping failed.\n");
|
|
}
|
|
} else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
|
|
LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
|
|
} else {
|
|
LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
|
|
}
|
|
} else {
|
|
LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool ProcessNatpmp()
|
|
{
|
|
bool ret = false;
|
|
natpmp_t natpmp;
|
|
struct in_addr external_ipv4_addr;
|
|
if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
|
|
bool external_ip_discovered = false;
|
|
const uint16_t private_port = GetListenPort();
|
|
do {
|
|
ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
|
|
} while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
|
|
g_mapport_interrupt.reset();
|
|
|
|
const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
|
|
g_mapport_external_port = 0;
|
|
if (r_send == 12 /* OK */) {
|
|
LogPrintf("natpmp: Port mapping removed successfully.\n");
|
|
} else {
|
|
LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
|
|
}
|
|
}
|
|
|
|
closenatpmp(&natpmp);
|
|
return ret;
|
|
}
|
|
#endif // USE_NATPMP
|
|
|
|
#ifdef USE_UPNP
|
|
static bool ProcessUpnp()
|
|
{
|
|
bool ret = false;
|
|
std::string port = strprintf("%u", GetListenPort());
|
|
const char * multicastif = nullptr;
|
|
const char * minissdpdpath = nullptr;
|
|
struct UPNPDev * devlist = nullptr;
|
|
char lanaddr[64];
|
|
|
|
int error = 0;
|
|
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
|
|
|
|
struct UPNPUrls urls;
|
|
struct IGDdatas data;
|
|
int r;
|
|
|
|
r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
|
|
if (r == 1)
|
|
{
|
|
if (fDiscover) {
|
|
char externalIPAddress[40];
|
|
r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
|
|
if (r != UPNPCOMMAND_SUCCESS) {
|
|
LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
|
|
} else {
|
|
if (externalIPAddress[0]) {
|
|
CNetAddr resolved;
|
|
if (LookupHost(externalIPAddress, resolved, false)) {
|
|
LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToStringAddr());
|
|
AddLocal(resolved, LOCAL_MAPPED);
|
|
}
|
|
} else {
|
|
LogPrintf("UPnP: GetExternalIPAddress failed.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
|
|
|
|
do {
|
|
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0");
|
|
|
|
if (r != UPNPCOMMAND_SUCCESS) {
|
|
ret = false;
|
|
LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
|
|
break;
|
|
} else {
|
|
ret = true;
|
|
LogPrintf("UPnP Port Mapping successful.\n");
|
|
}
|
|
} while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
|
|
g_mapport_interrupt.reset();
|
|
|
|
r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr);
|
|
LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
|
|
freeUPNPDevlist(devlist); devlist = nullptr;
|
|
FreeUPNPUrls(&urls);
|
|
} else {
|
|
LogPrintf("No valid UPnP IGDs found\n");
|
|
freeUPNPDevlist(devlist); devlist = nullptr;
|
|
if (r != 0)
|
|
FreeUPNPUrls(&urls);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif // USE_UPNP
|
|
|
|
static void ThreadMapPort()
|
|
{
|
|
SetSyscallSandboxPolicy(SyscallSandboxPolicy::INITIALIZATION_MAP_PORT);
|
|
bool ok;
|
|
do {
|
|
ok = false;
|
|
|
|
#ifdef USE_UPNP
|
|
// High priority protocol.
|
|
if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
|
|
g_mapport_current_proto = MapPortProtoFlag::UPNP;
|
|
ok = ProcessUpnp();
|
|
if (ok) continue;
|
|
}
|
|
#endif // USE_UPNP
|
|
|
|
#ifdef USE_NATPMP
|
|
// Low priority protocol.
|
|
if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
|
|
g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
|
|
ok = ProcessNatpmp();
|
|
if (ok) continue;
|
|
}
|
|
#endif // USE_NATPMP
|
|
|
|
g_mapport_current_proto = MapPortProtoFlag::NONE;
|
|
if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
|
|
return;
|
|
}
|
|
|
|
} while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
|
|
}
|
|
|
|
void StartThreadMapPort()
|
|
{
|
|
if (!g_mapport_thread.joinable()) {
|
|
assert(!g_mapport_interrupt);
|
|
g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
|
|
}
|
|
}
|
|
|
|
static void DispatchMapPort()
|
|
{
|
|
if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
|
|
return;
|
|
}
|
|
|
|
if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
|
|
StartThreadMapPort();
|
|
return;
|
|
}
|
|
|
|
if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
|
|
InterruptMapPort();
|
|
StopMapPort();
|
|
return;
|
|
}
|
|
|
|
if (g_mapport_enabled_protos & g_mapport_current_proto) {
|
|
// Enabling another protocol does not cause switching from the currently used one.
|
|
return;
|
|
}
|
|
|
|
assert(g_mapport_thread.joinable());
|
|
assert(!g_mapport_interrupt);
|
|
// Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
|
|
// to force trying the next protocol in the ThreadMapPort() loop.
|
|
g_mapport_interrupt();
|
|
}
|
|
|
|
static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
|
|
{
|
|
if (enabled) {
|
|
g_mapport_enabled_protos |= proto;
|
|
} else {
|
|
g_mapport_enabled_protos &= ~proto;
|
|
}
|
|
}
|
|
|
|
void StartMapPort(bool use_upnp, bool use_natpmp)
|
|
{
|
|
MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
|
|
MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
|
|
DispatchMapPort();
|
|
}
|
|
|
|
void InterruptMapPort()
|
|
{
|
|
g_mapport_enabled_protos = MapPortProtoFlag::NONE;
|
|
if (g_mapport_thread.joinable()) {
|
|
g_mapport_interrupt();
|
|
}
|
|
}
|
|
|
|
void StopMapPort()
|
|
{
|
|
if (g_mapport_thread.joinable()) {
|
|
g_mapport_thread.join();
|
|
g_mapport_interrupt.reset();
|
|
}
|
|
}
|
|
|
|
#else // #if defined(USE_NATPMP) || defined(USE_UPNP)
|
|
void StartMapPort(bool use_upnp, bool use_natpmp)
|
|
{
|
|
// Intentionally left blank.
|
|
}
|
|
void InterruptMapPort()
|
|
{
|
|
// Intentionally left blank.
|
|
}
|
|
void StopMapPort()
|
|
{
|
|
// Intentionally left blank.
|
|
}
|
|
#endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
|