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 : : #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)
|