LCOV - code coverage report
Current view: top level - src - mapport.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 4 10 40.0 %
Date: 2023-09-26 12:08:55 Functions: 4 7 57.1 %

          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)

Generated by: LCOV version 1.14