forked from nanocurrency/nano-node
/
portmapping.cpp
171 lines (163 loc) · 5.58 KB
/
portmapping.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#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 (!nano::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)
{
BOOST_LOG (node.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)
{
BOOST_LOG (node.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 (!nano::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 (mapping_timeout).c_str (), actual_external_port.data ()));
if (check_count % 15 == 0)
{
BOOST_LOG (node.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 (!nano::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)
{
BOOST_LOG (node.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 = 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)
{
BOOST_LOG (node.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 ()));
BOOST_LOG (node.log) << boost::str (boost::format ("Shutdown port mapping response: %1%") % delete_error);
}
}
freeUPNPDevlist (devices);
devices = nullptr;
}