2020-02-06 14:00:26 -03:00
|
|
|
// Copyright (c) 2011-2020 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 <threadinterrupt.h>
|
|
|
|
#include <util/system.h>
|
|
|
|
|
|
|
|
#ifdef USE_UPNP
|
|
|
|
#include <miniupnpc/miniupnpc.h>
|
|
|
|
#include <miniupnpc/upnpcommands.h>
|
|
|
|
#include <miniupnpc/upnperrors.h>
|
|
|
|
// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
|
|
|
|
// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
|
|
|
|
static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
|
|
|
|
#endif
|
|
|
|
|
2020-02-22 19:10:48 -03:00
|
|
|
#include <atomic>
|
2020-02-06 14:00:26 -03:00
|
|
|
#include <cassert>
|
|
|
|
#include <chrono>
|
|
|
|
#include <functional>
|
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
#ifdef USE_UPNP
|
|
|
|
static CThreadInterrupt g_upnp_interrupt;
|
|
|
|
static std::thread g_upnp_thread;
|
2020-02-22 19:10:48 -03:00
|
|
|
static std::atomic_uint g_mapport_target_proto{MapPortProtoFlag::NONE};
|
2020-02-16 11:37:46 -03:00
|
|
|
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
|
2020-02-18 18:23:30 -03:00
|
|
|
static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
|
2020-02-16 11:37:46 -03:00
|
|
|
|
2020-02-18 18:23:30 -03:00
|
|
|
static bool ProcessUpnp()
|
2020-02-06 14:00:26 -03:00
|
|
|
{
|
2020-02-18 18:23:30 -03:00
|
|
|
bool ret = false;
|
2020-02-06 14:00:26 -03:00
|
|
|
std::string port = strprintf("%u", GetListenPort());
|
|
|
|
const char * multicastif = nullptr;
|
|
|
|
const char * minissdpdpath = nullptr;
|
|
|
|
struct UPNPDev * devlist = nullptr;
|
|
|
|
char lanaddr[64];
|
|
|
|
|
|
|
|
int error = 0;
|
|
|
|
#if MINIUPNPC_API_VERSION < 14
|
|
|
|
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
|
|
|
|
#else
|
|
|
|
devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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.ToString());
|
|
|
|
AddLocal(resolved, LOCAL_UPNP);
|
|
|
|
}
|
|
|
|
} 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", 0, "0");
|
|
|
|
|
|
|
|
if (r != UPNPCOMMAND_SUCCESS) {
|
2020-02-18 18:23:30 -03:00
|
|
|
ret = false;
|
2020-02-06 14:00:26 -03:00
|
|
|
LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
|
2020-02-18 18:23:30 -03:00
|
|
|
break;
|
2020-02-06 14:00:26 -03:00
|
|
|
} else {
|
2020-02-18 18:23:30 -03:00
|
|
|
ret = true;
|
2020-02-06 14:00:26 -03:00
|
|
|
LogPrintf("UPnP Port Mapping successful.\n");
|
|
|
|
}
|
2020-02-16 11:37:46 -03:00
|
|
|
} while (g_upnp_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
|
2020-02-18 18:23:30 -03:00
|
|
|
g_upnp_interrupt.reset();
|
2020-02-06 14:00:26 -03:00
|
|
|
|
|
|
|
r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
|
|
|
|
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);
|
|
|
|
}
|
2020-02-18 18:23:30 -03:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ThreadMapPort()
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
if (ProcessUpnp()) return;
|
|
|
|
} while (g_upnp_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
|
2020-02-06 14:00:26 -03:00
|
|
|
}
|
|
|
|
|
2020-02-22 19:10:48 -03:00
|
|
|
void StartThreadMapPort()
|
2020-02-06 14:00:26 -03:00
|
|
|
{
|
|
|
|
if (!g_upnp_thread.joinable()) {
|
|
|
|
assert(!g_upnp_interrupt);
|
|
|
|
g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 19:10:48 -03:00
|
|
|
static void DispatchMapPort()
|
|
|
|
{
|
|
|
|
if (g_mapport_target_proto == MapPortProtoFlag::UPNP) {
|
|
|
|
StartThreadMapPort();
|
|
|
|
} else {
|
|
|
|
InterruptMapPort();
|
|
|
|
StopMapPort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
|
|
|
|
{
|
|
|
|
if (enabled) {
|
|
|
|
g_mapport_target_proto |= proto;
|
|
|
|
} else {
|
|
|
|
g_mapport_target_proto &= ~proto;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StartMapPort(bool use_upnp)
|
|
|
|
{
|
|
|
|
MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
|
|
|
|
DispatchMapPort();
|
|
|
|
}
|
|
|
|
|
2020-02-06 14:00:26 -03:00
|
|
|
void InterruptMapPort()
|
|
|
|
{
|
|
|
|
if(g_upnp_thread.joinable()) {
|
|
|
|
g_upnp_interrupt();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StopMapPort()
|
|
|
|
{
|
|
|
|
if(g_upnp_thread.joinable()) {
|
|
|
|
g_upnp_thread.join();
|
|
|
|
g_upnp_interrupt.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2020-02-22 19:10:48 -03:00
|
|
|
void StartMapPort(bool use_upnp)
|
2020-02-06 14:00:26 -03:00
|
|
|
{
|
|
|
|
// Intentionally left blank.
|
|
|
|
}
|
|
|
|
void InterruptMapPort()
|
|
|
|
{
|
|
|
|
// Intentionally left blank.
|
|
|
|
}
|
|
|
|
void StopMapPort()
|
|
|
|
{
|
|
|
|
// Intentionally left blank.
|
|
|
|
}
|
|
|
|
#endif
|