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