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