Line data Source code
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 : #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 2 : #include <util/thread.h>
18 2 : #include <util/threadinterrupt.h>
19 :
20 : #ifdef USE_NATPMP
21 : #include <compat/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 17. This excludes
30 : // versions with known vulnerabilities.
31 : static_assert(MINIUPNPC_API_VERSION >= 17, "miniUPnPc API version >= 17 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;
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 :
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 2 : } 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 2 : return false;
84 : }
85 :
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.ToStringAddrPort());
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 :
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
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 : devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
162 :
163 : struct UPNPUrls urls;
164 : struct IGDdatas data;
165 : int r;
166 :
167 : r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
168 : if (r == 1)
169 : {
170 : if (fDiscover) {
171 : char externalIPAddress[40];
172 : r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
173 : if (r != UPNPCOMMAND_SUCCESS) {
174 : LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
175 : } else {
176 : if (externalIPAddress[0]) {
177 : std::optional<CNetAddr> resolved{LookupHost(externalIPAddress, false)};
178 : if (resolved.has_value()) {
179 : LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved->ToStringAddr());
180 : AddLocal(resolved.value(), LOCAL_MAPPED);
181 : }
182 : } else {
183 : LogPrintf("UPnP: GetExternalIPAddress failed.\n");
184 : }
185 : }
186 : }
187 :
188 : std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
189 :
190 : do {
191 : r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0");
192 :
193 : if (r != UPNPCOMMAND_SUCCESS) {
194 : ret = false;
195 : LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
196 : break;
197 : } else {
198 : ret = true;
199 : LogPrintf("UPnP Port Mapping successful.\n");
200 : }
201 : } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
202 : g_mapport_interrupt.reset();
203 :
204 : r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr);
205 : LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
206 : freeUPNPDevlist(devlist); devlist = nullptr;
207 : FreeUPNPUrls(&urls);
208 : } else {
209 : LogPrintf("No valid UPnP IGDs found\n");
210 : freeUPNPDevlist(devlist); devlist = nullptr;
211 : if (r != 0)
212 : FreeUPNPUrls(&urls);
213 : }
214 :
215 : return ret;
216 : }
217 : #endif // USE_UPNP
218 :
219 : static void ThreadMapPort()
220 : {
221 : bool ok;
222 : do {
223 : ok = false;
224 :
225 : #ifdef USE_UPNP
226 : // High priority protocol.
227 : if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
228 : g_mapport_current_proto = MapPortProtoFlag::UPNP;
229 : ok = ProcessUpnp();
230 : if (ok) continue;
231 : }
232 : #endif // USE_UPNP
233 :
234 : #ifdef USE_NATPMP
235 : // Low priority protocol.
236 : if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
237 : g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
238 : ok = ProcessNatpmp();
239 : if (ok) continue;
240 : }
241 : #endif // USE_NATPMP
242 :
243 : g_mapport_current_proto = MapPortProtoFlag::NONE;
244 : if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
245 : return;
246 : }
247 :
248 : } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
249 : }
250 :
251 : void StartThreadMapPort()
252 : {
253 : if (!g_mapport_thread.joinable()) {
254 : assert(!g_mapport_interrupt);
255 : g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
256 : }
257 : }
258 :
259 : static void DispatchMapPort()
260 : {
261 : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
262 : return;
263 : }
264 :
265 : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
266 : StartThreadMapPort();
267 : return;
268 : }
269 :
270 : if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
271 : InterruptMapPort();
272 : StopMapPort();
273 : return;
274 : }
275 :
276 : if (g_mapport_enabled_protos & g_mapport_current_proto) {
277 : // Enabling another protocol does not cause switching from the currently used one.
278 : return;
279 : }
280 :
281 : assert(g_mapport_thread.joinable());
282 : assert(!g_mapport_interrupt);
283 : // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
284 : // to force trying the next protocol in the ThreadMapPort() loop.
285 : g_mapport_interrupt();
286 : }
287 :
288 : static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
289 : {
290 : if (enabled) {
291 : g_mapport_enabled_protos |= proto;
292 : } else {
293 : g_mapport_enabled_protos &= ~proto;
294 : }
295 : }
296 :
297 : void StartMapPort(bool use_upnp, bool use_natpmp)
298 : {
299 : MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
300 : MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
301 : DispatchMapPort();
302 : }
303 :
304 : void InterruptMapPort()
305 : {
306 : g_mapport_enabled_protos = MapPortProtoFlag::NONE;
307 : if (g_mapport_thread.joinable()) {
308 : g_mapport_interrupt();
309 : }
310 : }
311 :
312 : void StopMapPort()
313 : {
314 : if (g_mapport_thread.joinable()) {
315 : g_mapport_thread.join();
316 : g_mapport_interrupt.reset();
317 : }
318 : }
319 :
320 : #else // #if defined(USE_NATPMP) || defined(USE_UPNP)
321 0 : void StartMapPort(bool use_upnp, bool use_natpmp)
322 : {
323 : // Intentionally left blank.
324 0 : }
325 0 : void InterruptMapPort()
326 : {
327 : // Intentionally left blank.
328 0 : }
329 0 : void StopMapPort()
330 : {
331 : // Intentionally left blank.
332 0 : }
333 : #endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
|