LCOV - code coverage report
Current view: top level - src - mapport.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 22 214 10.3 %
Date: 2021-06-29 14:35:33 Functions: 5 12 41.7 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 5 90 5.6 %

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

Generated by: LCOV version 1.14