LCOV - code coverage report
Current view: top level - src - mapport.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 3 10 30.0 %
Date: 2023-11-10 23:46:46 Functions: 0 6 0.0 %
Branches: 3 6 50.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                 :            : #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                 :            :         } 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