Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
mapport.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2022 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#include <config/bitcoin-config.h> // IWYU pragma: keep
6
7#include <mapport.h>
8
9#include <clientversion.h>
10#include <common/system.h>
11#include <logging.h>
12#include <net.h>
13#include <netaddress.h>
14#include <netbase.h>
15#include <util/thread.h>
17
18#ifdef USE_NATPMP
19#include <compat/compat.h>
20#include <natpmp.h>
21#endif // USE_NATPMP
22
23#ifdef USE_UPNP
24#include <miniupnpc/miniupnpc.h>
25#include <miniupnpc/upnpcommands.h>
26#include <miniupnpc/upnperrors.h>
27// The minimum supported miniUPnPc API version is set to 17. This excludes
28// versions with known vulnerabilities.
29static_assert(MINIUPNPC_API_VERSION >= 17, "miniUPnPc API version >= 17 assumed");
30#endif // USE_UPNP
31
32#include <atomic>
33#include <cassert>
34#include <chrono>
35#include <functional>
36#include <string>
37#include <thread>
38
39#if defined(USE_NATPMP) || defined(USE_UPNP)
41static std::thread g_mapport_thread;
43static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
44
45using namespace std::chrono_literals;
46static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
47static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
48
49#ifdef USE_NATPMP
50static uint16_t g_mapport_external_port = 0;
51static bool NatpmpInit(natpmp_t* natpmp)
52{
53 const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0);
54 if (r_init == 0) return true;
55 LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init);
56 return false;
57}
58
59static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr)
60{
61 const int r_send = sendpublicaddressrequest(natpmp);
62 if (r_send == 2 /* OK */) {
63 int r_read;
64 natpmpresp_t response;
65 do {
66 r_read = readnatpmpresponseorretry(natpmp, &response);
67 } while (r_read == NATPMP_TRYAGAIN);
68
69 if (r_read == 0) {
70 external_ipv4_addr = response.pnu.publicaddress.addr;
71 return true;
72 } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
73 LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
74 } else {
75 LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
76 }
77 } else {
78 LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
79 }
80
81 return false;
82}
83
84static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered)
85{
86 const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
87 const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/);
88 if (r_send == 12 /* OK */) {
89 int r_read;
90 natpmpresp_t response;
91 do {
92 r_read = readnatpmpresponseorretry(natpmp, &response);
93 } while (r_read == NATPMP_TRYAGAIN);
94
95 if (r_read == 0) {
96 auto pm = response.pnu.newportmapping;
97 if (private_port == pm.privateport && pm.lifetime > 0) {
98 g_mapport_external_port = pm.mappedpublicport;
99 const CService external{external_ipv4_addr, pm.mappedpublicport};
100 if (!external_ip_discovered && fDiscover) {
101 AddLocal(external, LOCAL_MAPPED);
102 external_ip_discovered = true;
103 }
104 LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToStringAddrPort());
105 return true;
106 } else {
107 LogPrintf("natpmp: Port mapping failed.\n");
108 }
109 } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
110 LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
111 } else {
112 LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
113 }
114 } else {
115 LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
116 }
117
118 return false;
119}
120
121static bool ProcessNatpmp()
122{
123 bool ret = false;
124 natpmp_t natpmp;
125 struct in_addr external_ipv4_addr;
126 if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
127 bool external_ip_discovered = false;
128 const uint16_t private_port = GetListenPort();
129 do {
130 ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
133
134 const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
136 if (r_send == 12 /* OK */) {
137 LogPrintf("natpmp: Port mapping removed successfully.\n");
138 } else {
139 LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
140 }
141 }
142
143 closenatpmp(&natpmp);
144 return ret;
145}
146#endif // USE_NATPMP
147
148#ifdef USE_UPNP
149static bool ProcessUpnp()
150{
151 bool ret = false;
152 std::string port = strprintf("%u", GetListenPort());
153 const char * multicastif = nullptr;
154 const char * minissdpdpath = nullptr;
155 struct UPNPDev * devlist = nullptr;
156 char lanaddr[64];
157
158 int error = 0;
159 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
160
161 struct UPNPUrls urls;
162 struct IGDdatas data;
163 int r;
164#if MINIUPNPC_API_VERSION <= 17
165 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
166#else
167 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), nullptr, 0);
168#endif
169 if (r == 1)
170 {
171 if (fDiscover) {
172 char externalIPAddress[40];
173 r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
174 if (r != UPNPCOMMAND_SUCCESS) {
175 LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
176 } else {
177 if (externalIPAddress[0]) {
178 std::optional<CNetAddr> resolved{LookupHost(externalIPAddress, false)};
179 if (resolved.has_value()) {
180 LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved->ToStringAddr());
181 AddLocal(resolved.value(), LOCAL_MAPPED);
182 }
183 } else {
184 LogPrintf("UPnP: GetExternalIPAddress failed.\n");
185 }
186 }
187 }
188
189 std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
190
191 do {
192 r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0");
193
194 if (r != UPNPCOMMAND_SUCCESS) {
195 ret = false;
196 LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
197 break;
198 } else {
199 ret = true;
200 LogPrintf("UPnP Port Mapping successful.\n");
201 }
204
205 r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr);
206 LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
207 freeUPNPDevlist(devlist); devlist = nullptr;
208 FreeUPNPUrls(&urls);
209 } else {
210 LogPrintf("No valid UPnP IGDs found\n");
211 freeUPNPDevlist(devlist); devlist = nullptr;
212 if (r != 0)
213 FreeUPNPUrls(&urls);
214 }
215
216 return ret;
217}
218#endif // USE_UPNP
219
220static void ThreadMapPort()
221{
222 bool ok;
223 do {
224 ok = false;
225
226#ifdef USE_UPNP
227 // High priority protocol.
230 ok = ProcessUpnp();
231 if (ok) continue;
232 }
233#endif // USE_UPNP
234
235#ifdef USE_NATPMP
236 // Low priority protocol.
239 ok = ProcessNatpmp();
240 if (ok) continue;
241 }
242#endif // USE_NATPMP
243
246 return;
247 }
248
250}
251
253{
254 if (!g_mapport_thread.joinable()) {
256 g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
257 }
258}
259
260static void DispatchMapPort()
261{
263 return;
264 }
265
268 return;
269 }
270
273 StopMapPort();
274 return;
275 }
276
278 // Enabling another protocol does not cause switching from the currently used one.
279 return;
280 }
281
282 assert(g_mapport_thread.joinable());
284 // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
285 // to force trying the next protocol in the ThreadMapPort() loop.
287}
288
289static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
290{
291 if (enabled) {
293 } else {
294 g_mapport_enabled_protos &= ~proto;
295 }
296}
297
298void StartMapPort(bool use_upnp, bool use_natpmp)
299{
303}
304
312
314{
315 if (g_mapport_thread.joinable()) {
316 g_mapport_thread.join();
318 }
319}
320
321#else // #if defined(USE_NATPMP) || defined(USE_UPNP)
322void StartMapPort(bool use_upnp, bool use_natpmp)
323{
324 // Intentionally left blank.
325}
326void InterruptMapPort()
327{
328 // Intentionally left blank.
329}
330void StopMapPort()
331{
332 // Intentionally left blank.
333}
334#endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
int ret
#define PACKAGE_NAME
A combination of a network address (CNetAddr) and a (TCP) port.
Definition netaddress.h:531
A helper class for interruptible sleeps.
bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut)
std::string FormatFullVersion()
#define LogPrintf(...)
Definition logging.h:274
void StartMapPort(bool use_upnp, bool use_natpmp)
Definition mapport.cpp:298
static void ThreadMapPort()
Definition mapport.cpp:220
static std::thread g_mapport_thread
Definition mapport.cpp:41
static bool NatpmpDiscover(natpmp_t *natpmp, struct in_addr &external_ipv4_addr)
Definition mapport.cpp:59
static bool NatpmpMapping(natpmp_t *natpmp, const struct in_addr &external_ipv4_addr, uint16_t private_port, bool &external_ip_discovered)
Definition mapport.cpp:84
static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD
Definition mapport.cpp:46
static CThreadInterrupt g_mapport_interrupt
Definition mapport.cpp:40
void StopMapPort()
Definition mapport.cpp:313
static bool ProcessUpnp()
Definition mapport.cpp:149
static std::atomic< MapPortProtoFlag > g_mapport_current_proto
Definition mapport.cpp:43
static uint16_t g_mapport_external_port
Definition mapport.cpp:50
static void DispatchMapPort()
Definition mapport.cpp:260
static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
Definition mapport.cpp:289
static bool ProcessNatpmp()
Definition mapport.cpp:121
static constexpr auto PORT_MAPPING_RETRY_PERIOD
Definition mapport.cpp:47
static std::atomic_uint g_mapport_enabled_protos
Definition mapport.cpp:42
static bool NatpmpInit(natpmp_t *natpmp)
Definition mapport.cpp:51
void InterruptMapPort()
Definition mapport.cpp:305
void StartThreadMapPort()
Definition mapport.cpp:252
MapPortProtoFlag
Definition mapport.h:12
@ UPNP
Definition mapport.h:14
@ NAT_PMP
Definition mapport.h:15
@ NONE
Definition mapport.h:13
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition thread.cpp:16
uint16_t GetListenPort()
Definition net.cpp:133
bool fDiscover
Definition net.cpp:113
bool AddLocal(const CService &addr_, int nScore)
Definition net.cpp:266
@ LOCAL_MAPPED
Definition net.h:151
std::vector< CNetAddr > LookupHost(const std::string &name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
Resolve a host string to its corresponding network addresses.
Definition netbase.cpp:177
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
assert(!tx.IsCoinBase())