LCOV - code coverage report
Current view: top level - src - mapport.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 0.0 % 96 0
Test Date: 2024-08-28 04:44:32 Functions: 0.0 % 8 0
Branches: 0.0 % 114 0

             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                 :           0 : static bool ProcessUpnp()
     150                 :             : {
     151                 :           0 :     bool ret = false;
     152                 :           0 :     std::string port = strprintf("%u", GetListenPort());
     153                 :           0 :     const char * multicastif = nullptr;
     154                 :           0 :     const char * minissdpdpath = nullptr;
     155                 :           0 :     struct UPNPDev * devlist = nullptr;
     156                 :           0 :     char lanaddr[64];
     157                 :             : 
     158                 :           0 :     int error = 0;
     159         [ #  # ]:           0 :     devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
     160                 :             : 
     161                 :           0 :     struct UPNPUrls urls;
     162                 :           0 :     struct IGDdatas data;
     163                 :           0 :     int r;
     164                 :             : #if MINIUPNPC_API_VERSION <= 17
     165                 :             :     r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
     166                 :             : #else
     167         [ #  # ]:           0 :     r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), nullptr, 0);
     168                 :             : #endif
     169         [ #  # ]:           0 :     if (r == 1)
     170                 :             :     {
     171         [ #  # ]:           0 :         if (fDiscover) {
     172                 :           0 :             char externalIPAddress[40];
     173         [ #  # ]:           0 :             r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
     174         [ #  # ]:           0 :             if (r != UPNPCOMMAND_SUCCESS) {
     175         [ #  # ]:           0 :                 LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
     176                 :             :             } else {
     177         [ #  # ]:           0 :                 if (externalIPAddress[0]) {
     178   [ #  #  #  #  :           0 :                     std::optional<CNetAddr> resolved{LookupHost(externalIPAddress, false)};
                   #  # ]
     179         [ #  # ]:           0 :                     if (resolved.has_value()) {
     180   [ #  #  #  # ]:           0 :                         LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved->ToStringAddr());
     181   [ #  #  #  # ]:           0 :                         AddLocal(resolved.value(), LOCAL_MAPPED);
     182                 :             :                     }
     183                 :           0 :                 } else {
     184         [ #  # ]:           0 :                     LogPrintf("UPnP: GetExternalIPAddress failed.\n");
     185                 :             :                 }
     186                 :             :             }
     187                 :             :         }
     188                 :             : 
     189   [ #  #  #  # ]:           0 :         std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
     190                 :             : 
     191                 :           0 :         do {
     192         [ #  # ]:           0 :             r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0");
     193                 :             : 
     194         [ #  # ]:           0 :             if (r != UPNPCOMMAND_SUCCESS) {
     195                 :           0 :                 ret = false;
     196   [ #  #  #  # ]:           0 :                 LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
     197                 :             :                 break;
     198                 :             :             } else {
     199                 :           0 :                 ret = true;
     200         [ #  # ]:           0 :                 LogPrintf("UPnP Port Mapping successful.\n");
     201                 :             :             }
     202   [ #  #  #  # ]:           0 :         } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
     203         [ #  # ]:           0 :         g_mapport_interrupt.reset();
     204                 :             : 
     205         [ #  # ]:           0 :         r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr);
     206         [ #  # ]:           0 :         LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
     207         [ #  # ]:           0 :         freeUPNPDevlist(devlist); devlist = nullptr;
     208         [ #  # ]:           0 :         FreeUPNPUrls(&urls);
     209                 :           0 :     } else {
     210         [ #  # ]:           0 :         LogPrintf("No valid UPnP IGDs found\n");
     211         [ #  # ]:           0 :         freeUPNPDevlist(devlist); devlist = nullptr;
     212         [ #  # ]:           0 :         if (r != 0)
     213         [ #  # ]:           0 :             FreeUPNPUrls(&urls);
     214                 :             :     }
     215                 :             : 
     216                 :           0 :     return ret;
     217                 :           0 : }
     218                 :             : #endif // USE_UPNP
     219                 :             : 
     220                 :           0 : static void ThreadMapPort()
     221                 :             : {
     222                 :           0 :     bool ok;
     223                 :           0 :     do {
     224                 :           0 :         ok = false;
     225                 :             : 
     226                 :             : #ifdef USE_UPNP
     227                 :             :         // High priority protocol.
     228         [ #  # ]:           0 :         if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
     229                 :           0 :             g_mapport_current_proto = MapPortProtoFlag::UPNP;
     230                 :           0 :             ok = ProcessUpnp();
     231         [ #  # ]:           0 :             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         [ #  # ]:           0 :         g_mapport_current_proto = MapPortProtoFlag::NONE;
     245         [ #  # ]:           0 :         if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
     246                 :             :             return;
     247                 :             :         }
     248                 :             : 
     249   [ #  #  #  #  :           0 :     } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
                   #  # ]
     250                 :             : }
     251                 :             : 
     252                 :           0 : void StartThreadMapPort()
     253                 :             : {
     254         [ #  # ]:           0 :     if (!g_mapport_thread.joinable()) {
     255         [ #  # ]:           0 :         assert(!g_mapport_interrupt);
     256                 :           0 :         g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
     257                 :             :     }
     258                 :           0 : }
     259                 :             : 
     260                 :           0 : static void DispatchMapPort()
     261                 :             : {
     262   [ #  #  #  # ]:           0 :     if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
     263                 :             :         return;
     264                 :             :     }
     265                 :             : 
     266   [ #  #  #  # ]:           0 :     if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
     267                 :           0 :         StartThreadMapPort();
     268                 :           0 :         return;
     269                 :             :     }
     270                 :             : 
     271   [ #  #  #  # ]:           0 :     if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
     272                 :           0 :         InterruptMapPort();
     273                 :           0 :         StopMapPort();
     274                 :           0 :         return;
     275                 :             :     }
     276                 :             : 
     277         [ #  # ]:           0 :     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         [ #  # ]:           0 :     assert(g_mapport_thread.joinable());
     283         [ #  # ]:           0 :     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                 :           0 :     g_mapport_interrupt();
     287                 :             : }
     288                 :             : 
     289                 :           0 : static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
     290                 :             : {
     291         [ #  # ]:           0 :     if (enabled) {
     292                 :           0 :         g_mapport_enabled_protos |= proto;
     293                 :             :     } else {
     294                 :           0 :         g_mapport_enabled_protos &= ~proto;
     295                 :             :     }
     296                 :           0 : }
     297                 :             : 
     298                 :           0 : void StartMapPort(bool use_upnp, bool use_natpmp)
     299                 :             : {
     300                 :           0 :     MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
     301                 :           0 :     MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
     302                 :           0 :     DispatchMapPort();
     303                 :           0 : }
     304                 :             : 
     305                 :           0 : void InterruptMapPort()
     306                 :             : {
     307         [ #  # ]:           0 :     g_mapport_enabled_protos = MapPortProtoFlag::NONE;
     308         [ #  # ]:           0 :     if (g_mapport_thread.joinable()) {
     309                 :           0 :         g_mapport_interrupt();
     310                 :             :     }
     311                 :           0 : }
     312                 :             : 
     313                 :           0 : void StopMapPort()
     314                 :             : {
     315         [ #  # ]:           0 :     if (g_mapport_thread.joinable()) {
     316                 :           0 :         g_mapport_thread.join();
     317                 :           0 :         g_mapport_interrupt.reset();
     318                 :             :     }
     319                 :           0 : }
     320                 :             : 
     321                 :             : #else  // #if defined(USE_NATPMP) || defined(USE_UPNP)
     322                 :             : void StartMapPort(bool use_upnp, bool use_natpmp)
     323                 :             : {
     324                 :             :     // Intentionally left blank.
     325                 :             : }
     326                 :             : void InterruptMapPort()
     327                 :             : {
     328                 :             :     // Intentionally left blank.
     329                 :             : }
     330                 :             : void StopMapPort()
     331                 :             : {
     332                 :             :     // Intentionally left blank.
     333                 :             : }
     334                 :             : #endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
        

Generated by: LCOV version 2.0-1