Branch data 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 : : #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>
16 : : #include <util/threadinterrupt.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.
29 : : static_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)
40 : : static CThreadInterrupt g_mapport_interrupt;
41 : : static std::thread g_mapport_thread;
42 : : static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
43 : : static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
44 : :
45 : : using namespace std::chrono_literals;
46 : : static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
47 : : static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
48 : :
49 : : #ifdef USE_NATPMP
50 : : static uint16_t g_mapport_external_port = 0;
51 : : static 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 : :
59 : : static 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 : :
84 : : static 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 : :
121 : : static 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);
131 : : } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
132 : : g_mapport_interrupt.reset();
133 : :
134 : : const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
135 : : g_mapport_external_port = 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
149 : : static 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 : : }
202 : : } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
203 : : g_mapport_interrupt.reset();
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 : :
220 : : static void ThreadMapPort()
221 : : {
222 : : bool ok;
223 : : do {
224 : : ok = false;
225 : :
226 : : #ifdef USE_UPNP
227 : : // High priority protocol.
228 : : if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
229 : : g_mapport_current_proto = MapPortProtoFlag::UPNP;
230 : : ok = ProcessUpnp();
231 : : if (ok) continue;
232 : : }
233 : : #endif // USE_UPNP
234 : :
235 : : #ifdef USE_NATPMP
236 : : // Low priority protocol.
237 : : if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
238 : : g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
239 : : ok = ProcessNatpmp();
240 : : if (ok) continue;
241 : : }
242 : : #endif // USE_NATPMP
243 : :
244 : : g_mapport_current_proto = MapPortProtoFlag::NONE;
245 : : if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
246 : : return;
247 : : }
248 : :
249 : : } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
250 : : }
251 : :
252 : : void StartThreadMapPort()
253 : : {
254 : : if (!g_mapport_thread.joinable()) {
255 : : assert(!g_mapport_interrupt);
256 : : g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
257 : : }
258 : : }
259 : :
260 : : static void DispatchMapPort()
261 : : {
262 : : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
263 : : return;
264 : : }
265 : :
266 : : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
267 : : StartThreadMapPort();
268 : : return;
269 : : }
270 : :
271 : : if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
272 : : InterruptMapPort();
273 : : StopMapPort();
274 : : return;
275 : : }
276 : :
277 : : if (g_mapport_enabled_protos & g_mapport_current_proto) {
278 : : // Enabling another protocol does not cause switching from the currently used one.
279 : : return;
280 : : }
281 : :
282 : : assert(g_mapport_thread.joinable());
283 : : assert(!g_mapport_interrupt);
284 : : // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
285 : : // to force trying the next protocol in the ThreadMapPort() loop.
286 : : g_mapport_interrupt();
287 : : }
288 : :
289 : : static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
290 : : {
291 : : if (enabled) {
292 : : g_mapport_enabled_protos |= proto;
293 : : } else {
294 : : g_mapport_enabled_protos &= ~proto;
295 : : }
296 : : }
297 : :
298 : : void StartMapPort(bool use_upnp, bool use_natpmp)
299 : : {
300 : : MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
301 : : MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
302 : : DispatchMapPort();
303 : : }
304 : :
305 : : void InterruptMapPort()
306 : : {
307 : : g_mapport_enabled_protos = MapPortProtoFlag::NONE;
308 : : if (g_mapport_thread.joinable()) {
309 : : g_mapport_interrupt();
310 : : }
311 : : }
312 : :
313 : : void StopMapPort()
314 : : {
315 : : if (g_mapport_thread.joinable()) {
316 : : g_mapport_thread.join();
317 : : g_mapport_interrupt.reset();
318 : : }
319 : : }
320 : :
321 : : #else // #if defined(USE_NATPMP) || defined(USE_UPNP)
322 : 0 : void StartMapPort(bool use_upnp, bool use_natpmp)
323 : : {
324 : : // Intentionally left blank.
325 : 0 : }
326 : 0 : void InterruptMapPort()
327 : : {
328 : : // Intentionally left blank.
329 : 0 : }
330 : 0 : void StopMapPort()
331 : : {
332 : : // Intentionally left blank.
333 : 0 : }
334 : : #endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
|