Line data Source code
1 : // Copyright (c) 2011-2020 The Bitcoin Core developers
2 : // Copyright (c) 2021 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #if defined(HAVE_CONFIG_H)
7 : #include "config/pivx-config.h"
8 : #endif
9 :
10 : #include "mapport.h"
11 :
12 : #include "clientversion.h"
13 : #include "logging.h"
14 : #include "net.h"
15 : #include "netaddress.h"
16 : #include "netbase.h"
17 : #include "threadinterrupt.h"
18 : #include "util/system.h"
19 :
20 : #ifdef USE_NATPMP
21 : #include <compat.h>
22 : #include <natpmp.h>
23 : #endif // USE_NATPMP
24 :
25 : #ifdef USE_UPNP
26 : #include <miniupnpc/miniupnpc.h>
27 : #include <miniupnpc/upnpcommands.h>
28 : #include <miniupnpc/upnperrors.h>
29 : // The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
30 : // with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
31 : static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
32 : #endif // USE_UPNP
33 :
34 : #include <atomic>
35 : #include <cassert>
36 : #include <chrono>
37 : #include <functional>
38 : #include <string>
39 : #include <thread>
40 :
41 : #if defined(USE_NATPMP) || defined(USE_UPNP)
42 : static CThreadInterrupt g_mapport_interrupt;
43 : static std::thread g_mapport_thread;
44 : static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
45 : static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
46 :
47 : static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD = std::chrono::minutes(20);
48 : static constexpr auto PORT_MAPPING_RETRY_PERIOD = std::chrono::minutes(5);
49 :
50 : #ifdef USE_NATPMP
51 : static uint16_t g_mapport_external_port = 0;
52 0 : static bool NatpmpInit(natpmp_t* natpmp)
53 : {
54 0 : const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0);
55 0 : if (r_init == 0) return true;
56 0 : LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init);
57 : return false;
58 : }
59 :
60 0 : static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr)
61 : {
62 0 : const int r_send = sendpublicaddressrequest(natpmp);
63 0 : if (r_send == 2 /* OK */) {
64 0 : int r_read;
65 0 : natpmpresp_t response;
66 0 : do {
67 0 : r_read = readnatpmpresponseorretry(natpmp, &response);
68 0 : } while (r_read == NATPMP_TRYAGAIN);
69 :
70 0 : if (r_read == 0) {
71 0 : external_ipv4_addr = response.pnu.publicaddress.addr;
72 0 : return true;
73 0 : } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
74 0 : LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
75 : } else {
76 0 : LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
77 : }
78 : } else {
79 0 : LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
80 : }
81 :
82 : return false;
83 : }
84 :
85 0 : static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered)
86 : {
87 0 : const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
88 0 : const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/);
89 0 : if (r_send == 12 /* OK */) {
90 0 : int r_read;
91 0 : natpmpresp_t response;
92 0 : do {
93 0 : r_read = readnatpmpresponseorretry(natpmp, &response);
94 0 : } while (r_read == NATPMP_TRYAGAIN);
95 :
96 0 : if (r_read == 0) {
97 0 : auto pm = response.pnu.newportmapping;
98 0 : if (private_port == pm.privateport && pm.lifetime > 0) {
99 0 : g_mapport_external_port = pm.mappedpublicport;
100 0 : const CService external{external_ipv4_addr, pm.mappedpublicport};
101 0 : if (!external_ip_discovered && fDiscover) {
102 0 : AddLocal(external, LOCAL_MAPPED);
103 0 : external_ip_discovered = true;
104 : }
105 0 : LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString());
106 0 : return true;
107 : } else {
108 0 : LogPrintf("natpmp: Port mapping failed.\n");
109 : }
110 0 : } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
111 0 : LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
112 : } else {
113 0 : LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
114 : }
115 : } else {
116 0 : LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
117 : }
118 :
119 : return false;
120 : }
121 :
122 0 : static bool ProcessNatpmp()
123 : {
124 0 : bool ret = false;
125 0 : natpmp_t natpmp;
126 0 : struct in_addr external_ipv4_addr{};
127 0 : if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
128 0 : bool external_ip_discovered = false;
129 0 : const uint16_t private_port = GetListenPort();
130 0 : do {
131 0 : ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
132 0 : } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
133 0 : g_mapport_interrupt.reset();
134 :
135 0 : const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
136 0 : g_mapport_external_port = 0;
137 0 : if (r_send == 12 /* OK */) {
138 0 : LogPrintf("natpmp: Port mapping removed successfully.\n");
139 : } else {
140 0 : LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
141 : }
142 : }
143 :
144 0 : closenatpmp(&natpmp);
145 0 : return ret;
146 : }
147 : #endif // USE_NATPMP
148 :
149 : #ifdef USE_UPNP
150 0 : static bool ProcessUpnp()
151 : {
152 0 : bool ret = false;
153 0 : std::string port = strprintf("%u", GetListenPort());
154 0 : const char* multicastif = nullptr;
155 0 : const char* minissdpdpath = nullptr;
156 0 : struct UPNPDev* devlist = nullptr;
157 0 : char lanaddr[64];
158 :
159 0 : int error = 0;
160 : #if MINIUPNPC_API_VERSION < 14
161 : devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
162 : #else
163 0 : devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
164 : #endif
165 :
166 0 : struct UPNPUrls urls{};
167 0 : struct IGDdatas data{};
168 0 : int r;
169 : #if MINIUPNPC_API_VERSION <= 17
170 0 : r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
171 : #else
172 : r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), nullptr, 0);
173 : #endif
174 0 : if (r == 1) {
175 0 : if (fDiscover) {
176 0 : char externalIPAddress[40];
177 0 : r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
178 0 : if (r != UPNPCOMMAND_SUCCESS) {
179 0 : LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
180 : } else {
181 0 : if (externalIPAddress[0]) {
182 0 : CNetAddr resolved;
183 0 : if (LookupHost(externalIPAddress, resolved, false)) {
184 0 : LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str());
185 0 : AddLocal(resolved, LOCAL_MAPPED);
186 : }
187 : } else {
188 0 : LogPrintf("UPnP: GetExternalIPAddress failed.\n");
189 : }
190 : }
191 : }
192 :
193 0 : std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
194 :
195 0 : do {
196 0 : r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0");
197 :
198 0 : if (r != UPNPCOMMAND_SUCCESS) {
199 0 : ret = false;
200 0 : LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
201 0 : break;
202 : } else {
203 0 : ret = true;
204 0 : LogPrintf("UPnP Port Mapping successful.\n");
205 : }
206 0 : } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
207 0 : g_mapport_interrupt.reset();
208 :
209 0 : r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr);
210 0 : LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
211 0 : freeUPNPDevlist(devlist); devlist = nullptr;
212 0 : FreeUPNPUrls(&urls);
213 : } else {
214 0 : LogPrintf("No valid UPnP IGDs found\n");
215 0 : freeUPNPDevlist(devlist);
216 0 : devlist = nullptr;
217 0 : if (r != 0)
218 0 : FreeUPNPUrls(&urls);
219 : }
220 :
221 0 : return ret;
222 : }
223 : #endif // USE_UPNP
224 :
225 0 : static void ThreadMapPort()
226 : {
227 0 : bool ok;
228 0 : do {
229 0 : ok = false;
230 :
231 : #ifdef USE_UPNP
232 : // High priority protocol.
233 0 : if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
234 0 : g_mapport_current_proto = MapPortProtoFlag::UPNP;
235 0 : ok = ProcessUpnp();
236 0 : if (ok) continue;
237 : }
238 : #endif // USE_UPNP
239 :
240 : #ifdef USE_NATPMP
241 : // Low priority protocol.
242 0 : if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
243 0 : g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
244 0 : ok = ProcessNatpmp();
245 0 : if (ok) continue;
246 : }
247 : #endif // USE_NATPMP
248 :
249 0 : g_mapport_current_proto = MapPortProtoFlag::NONE;
250 0 : if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
251 : return;
252 : }
253 :
254 0 : } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
255 : }
256 :
257 0 : void StartThreadMapPort()
258 : {
259 0 : if (!g_mapport_thread.joinable()) {
260 0 : assert(!g_mapport_interrupt);
261 0 : g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort));
262 : }
263 0 : }
264 :
265 355 : static void DispatchMapPort()
266 : {
267 355 : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
268 : return;
269 : }
270 :
271 0 : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
272 0 : StartThreadMapPort();
273 0 : return;
274 : }
275 :
276 0 : if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
277 0 : InterruptMapPort();
278 0 : StopMapPort();
279 0 : return;
280 : }
281 :
282 0 : if (g_mapport_enabled_protos & g_mapport_current_proto) {
283 : // Enabling another protocol does not cause switching from the currently used one.
284 : return;
285 : }
286 :
287 0 : assert(g_mapport_thread.joinable());
288 0 : assert(!g_mapport_interrupt);
289 : // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
290 : // to force trying the next protocol in the ThreadMapPort() loop.
291 0 : g_mapport_interrupt();
292 : }
293 :
294 710 : static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
295 : {
296 710 : if (enabled) {
297 0 : g_mapport_enabled_protos |= proto;
298 : } else {
299 710 : g_mapport_enabled_protos &= ~proto;
300 : }
301 710 : }
302 :
303 355 : void StartMapPort(bool use_upnp, bool use_natpmp)
304 : {
305 355 : MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
306 355 : MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
307 355 : DispatchMapPort();
308 355 : }
309 :
310 378 : void InterruptMapPort()
311 : {
312 378 : g_mapport_enabled_protos = MapPortProtoFlag::NONE;
313 378 : if (g_mapport_thread.joinable()) {
314 0 : g_mapport_interrupt();
315 : }
316 378 : }
317 :
318 378 : void StopMapPort()
319 : {
320 378 : if (g_mapport_thread.joinable()) {
321 0 : g_mapport_thread.join();
322 0 : g_mapport_interrupt.reset();
323 : }
324 378 : }
325 :
326 : #else // #if defined(USE_NATPMP) || defined(USE_UPNP)
327 : void StartMapPort(bool use_upnp, bool use_natpmp)
328 : {
329 : // Intentionally left blank.
330 : }
331 : void InterruptMapPort()
332 : {
333 : // Intentionally left blank.
334 : }
335 : void StopMapPort()
336 : {
337 : // Intentionally left blank.
338 : }
339 : #endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
|