dncurrency/nano/node/portmapping.cpp
cryptocode bab4474274
Network selector (#1729)
* 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
2019-03-11 16:10:33 +01:00

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;
}