* Network selector * Make sure network option is checked before working path is called (migration). Also remove bool assignment from error. * Formatting * Fix merge error * Use network_params for RPC port config (rebase) * Formatting * Rebase * Rebase (debug_opencl, merge fix) * Rebase fix * post-rebase update
171 lines
5.7 KiB
C++
171 lines
5.7 KiB
C++
#include <nano/node/node.hpp>
|
|
#include <nano/node/portmapping.hpp>
|
|
#include <upnpcommands.h>
|
|
|
|
nano::port_mapping::port_mapping (nano::node & node_a) :
|
|
node (node_a),
|
|
devices (nullptr),
|
|
protocols ({ { { "TCP", 0, boost::asio::ip::address_v4::any (), 0 }, { "UDP", 0, boost::asio::ip::address_v4::any (), 0 } } }),
|
|
check_count (0),
|
|
on (false)
|
|
{
|
|
urls = { 0 };
|
|
data = { { 0 } };
|
|
}
|
|
|
|
void nano::port_mapping::start ()
|
|
{
|
|
check_mapping_loop ();
|
|
}
|
|
|
|
void nano::port_mapping::refresh_devices ()
|
|
{
|
|
if (!network_params.is_test_network ())
|
|
{
|
|
std::lock_guard<std::mutex> lock (mutex);
|
|
int discover_error = 0;
|
|
freeUPNPDevlist (devices);
|
|
devices = upnpDiscover (2000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, false, 2, &discover_error);
|
|
std::array<char, 64> local_address;
|
|
local_address.fill (0);
|
|
auto igd_error (UPNP_GetValidIGD (devices, &urls, &data, local_address.data (), sizeof (local_address)));
|
|
if (igd_error == 1 || igd_error == 2)
|
|
{
|
|
boost::system::error_code ec;
|
|
address = boost::asio::ip::address_v4::from_string (local_address.data (), ec);
|
|
}
|
|
if (check_count % 15 == 0)
|
|
{
|
|
node.logger.always_log (boost::str (boost::format ("UPnP local address: %1%, discovery: %2%, IGD search: %3%") % local_address.data () % discover_error % igd_error));
|
|
if (node.config.logging.upnp_details_logging ())
|
|
{
|
|
for (auto i (devices); i != nullptr; i = i->pNext)
|
|
{
|
|
node.logger.always_log (boost::str (boost::format ("UPnP device url: %1% st: %2% usn: %3%") % i->descURL % i->st % i->usn));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nano::port_mapping::refresh_mapping ()
|
|
{
|
|
if (!network_params.is_test_network ())
|
|
{
|
|
std::lock_guard<std::mutex> lock (mutex);
|
|
auto node_port (std::to_string (node.network.endpoint ().port ()));
|
|
|
|
// We don't map the RPC port because, unless RPC authentication was added, this would almost always be a security risk
|
|
for (auto & protocol : protocols)
|
|
{
|
|
std::array<char, 6> actual_external_port;
|
|
actual_external_port.fill (0);
|
|
auto add_port_mapping_error (UPNP_AddAnyPortMapping (urls.controlURL, data.first.servicetype, node_port.c_str (), node_port.c_str (), address.to_string ().c_str (), nullptr, protocol.name, nullptr, std::to_string (network_params.portmapping.mapping_timeout).c_str (), actual_external_port.data ()));
|
|
if (check_count % 15 == 0)
|
|
{
|
|
node.logger.always_log (boost::str (boost::format ("UPnP %1% port mapping response: %2%, actual external port %3%") % protocol.name % add_port_mapping_error % actual_external_port.data ()));
|
|
}
|
|
if (add_port_mapping_error == UPNPCOMMAND_SUCCESS)
|
|
{
|
|
protocol.external_port = std::atoi (actual_external_port.data ());
|
|
}
|
|
else
|
|
{
|
|
protocol.external_port = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int nano::port_mapping::check_mapping ()
|
|
{
|
|
int result (3600);
|
|
if (!network_params.is_test_network ())
|
|
{
|
|
// Long discovery time and fast setup/teardown make this impractical for testing
|
|
std::lock_guard<std::mutex> lock (mutex);
|
|
auto node_port (std::to_string (node.network.endpoint ().port ()));
|
|
for (auto & protocol : protocols)
|
|
{
|
|
std::array<char, 64> int_client;
|
|
std::array<char, 6> int_port;
|
|
std::array<char, 16> remaining_mapping_duration;
|
|
remaining_mapping_duration.fill (0);
|
|
auto verify_port_mapping_error (UPNP_GetSpecificPortMappingEntry (urls.controlURL, data.first.servicetype, node_port.c_str (), protocol.name, nullptr, int_client.data (), int_port.data (), nullptr, nullptr, remaining_mapping_duration.data ()));
|
|
if (verify_port_mapping_error == UPNPCOMMAND_SUCCESS)
|
|
{
|
|
protocol.remaining = result;
|
|
}
|
|
else
|
|
{
|
|
protocol.remaining = 0;
|
|
}
|
|
result = std::min (result, protocol.remaining);
|
|
std::array<char, 64> external_address;
|
|
external_address.fill (0);
|
|
auto external_ip_error (UPNP_GetExternalIPAddress (urls.controlURL, data.first.servicetype, external_address.data ()));
|
|
if (external_ip_error == UPNPCOMMAND_SUCCESS)
|
|
{
|
|
boost::system::error_code ec;
|
|
protocol.external_address = boost::asio::ip::address_v4::from_string (external_address.data (), ec);
|
|
}
|
|
else
|
|
{
|
|
protocol.external_address = boost::asio::ip::address_v4::any ();
|
|
}
|
|
if (check_count % 15 == 0)
|
|
{
|
|
node.logger.always_log (boost::str (boost::format ("UPnP %1% mapping verification response: %2%, external ip response: %3%, external ip: %4%, internal ip: %5%, remaining lease: %6%") % protocol.name % verify_port_mapping_error % external_ip_error % external_address.data () % address.to_string () % remaining_mapping_duration.data ()));
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void nano::port_mapping::check_mapping_loop ()
|
|
{
|
|
int wait_duration = network_params.portmapping.check_timeout;
|
|
refresh_devices ();
|
|
if (devices != nullptr)
|
|
{
|
|
auto remaining (check_mapping ());
|
|
// If the mapping is lost, refresh it
|
|
if (remaining == 0)
|
|
{
|
|
refresh_mapping ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wait_duration = 300;
|
|
if (check_count < 10)
|
|
{
|
|
node.logger.always_log (boost::str (boost::format ("UPnP No IGD devices found")));
|
|
}
|
|
}
|
|
++check_count;
|
|
if (on)
|
|
{
|
|
auto node_l (node.shared ());
|
|
node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (wait_duration), [node_l]() {
|
|
node_l->port_mapping.check_mapping_loop ();
|
|
});
|
|
}
|
|
}
|
|
|
|
void nano::port_mapping::stop ()
|
|
{
|
|
on = false;
|
|
std::lock_guard<std::mutex> lock (mutex);
|
|
for (auto & protocol : protocols)
|
|
{
|
|
if (protocol.external_port != 0)
|
|
{
|
|
// Be a good citizen for the router and shut down our mapping
|
|
auto delete_error (UPNP_DeletePortMapping (urls.controlURL, data.first.servicetype, std::to_string (protocol.external_port).c_str (), protocol.name, address.to_string ().c_str ()));
|
|
node.logger.always_log (boost::str (boost::format ("Shutdown port mapping response: %1%") % delete_error));
|
|
}
|
|
}
|
|
freeUPNPDevlist (devices);
|
|
devices = nullptr;
|
|
}
|