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