LCOV - code coverage report
Current view: top level - src - mapport.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 18 160 11.2 %
Date: 2025-02-23 09:33:43 Functions: 5 12 41.7 %

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

Generated by: LCOV version 1.14