diff --git a/src/init.cpp b/src/init.cpp index 8a260a3df2a..9a63fe0e030 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -469,6 +469,11 @@ void SetupServerArgs(NodeContext& node) #else hidden_args.emplace_back("-upnp"); #endif +#ifdef USE_NATPMP + argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION); +#else + hidden_args.emplace_back("-natpmp"); +#endif // USE_NATPMP argsman.AddArg("-whitebind=<[permissions@]addr>", "Bind to the given address and add permission flags to the peers connecting to it. " "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". " "Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -813,10 +818,13 @@ void InitParameterInteraction(ArgsManager& args) // to protect privacy, do not listen by default if a default proxy server is specified if (args.SoftSetBoolArg("-listen", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); - // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 + // to protect privacy, do not map ports when a proxy is set. The user may still specify -listen=1 // to listen locally, so don't rely on this happening through -listen below. if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -proxy set -> setting -natpmp=0\n", __func__); + } // to protect privacy, do not discover addresses by default if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__); @@ -826,6 +834,9 @@ void InitParameterInteraction(ArgsManager& args) // do not map ports or try to retrieve public IP when not listening (pointless) if (args.SoftSetBoolArg("-upnp", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); + if (args.SoftSetBoolArg("-natpmp", false)) { + LogPrintf("%s: parameter interaction: -listen=0 -> setting -natpmp=0\n", __func__); + } if (args.SoftSetBoolArg("-discover", false)) LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); if (args.SoftSetBoolArg("-listenonion", false)) @@ -1899,8 +1910,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA Discover(); - // Map ports with UPnP - StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP)); + // Map ports with UPnP or NAT-PMP. + StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP)); CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 97549aeb0e2..15f7ef62564 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -82,7 +82,7 @@ public: virtual bool shutdownRequested() = 0; //! Map port. - virtual void mapPort(bool use_upnp) = 0; + virtual void mapPort(bool use_upnp, bool use_natpmp) = 0; //! Get proxy. virtual bool getProxy(Network net, proxyType& proxy_info) = 0; diff --git a/src/mapport.cpp b/src/mapport.cpp index bb5cc477975..2df4ce45d2f 100644 --- a/src/mapport.cpp +++ b/src/mapport.cpp @@ -297,9 +297,10 @@ static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled) } } -void StartMapPort(bool use_upnp) +void StartMapPort(bool use_upnp, bool use_natpmp) { MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp); + MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp); DispatchMapPort(); } @@ -320,7 +321,7 @@ void StopMapPort() } #else // #if defined(USE_NATPMP) || defined(USE_UPNP) -void StartMapPort(bool use_upnp) +void StartMapPort(bool use_upnp, bool use_natpmp) { // Intentionally left blank. } diff --git a/src/mapport.h b/src/mapport.h index 2cdde775d3d..279d65167f5 100644 --- a/src/mapport.h +++ b/src/mapport.h @@ -5,12 +5,17 @@ #ifndef BITCOIN_MAPPORT_H #define BITCOIN_MAPPORT_H -/** -upnp default */ #ifdef USE_UPNP -static const bool DEFAULT_UPNP = USE_UPNP; +static constexpr bool DEFAULT_UPNP = USE_UPNP; #else -static const bool DEFAULT_UPNP = false; -#endif +static constexpr bool DEFAULT_UPNP = false; +#endif // USE_UPNP + +#ifdef USE_NATPMP +static constexpr bool DEFAULT_NATPMP = USE_NATPMP; +#else +static constexpr bool DEFAULT_NATPMP = false; +#endif // USE_NATPMP enum MapPortProtoFlag : unsigned int { NONE = 0x00, @@ -18,7 +23,7 @@ enum MapPortProtoFlag : unsigned int { NAT_PMP = 0x02, }; -void StartMapPort(bool use_upnp); +void StartMapPort(bool use_upnp, bool use_natpmp); void InterruptMapPort(); void StopMapPort(); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 6b84cb0f059..e07eaa33d81 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -94,7 +94,7 @@ public: } } bool shutdownRequested() override { return ShutdownRequested(); } - void mapPort(bool use_upnp) override { StartMapPort(use_upnp); } + void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d3677f0773b..4057712a41e 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -53,7 +53,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : #endif connect(this, &QDialog::accepted, [this](){ QSettings settings; - model->node().mapPort(settings.value("fUseUPnP").toBool()); + model->node().mapPort(settings.value("fUseUPnP").toBool(), settings.value("fUseNatpmp").toBool()); }); ui->proxyIp->setEnabled(false); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 81ebe3b8104..1e0391a35c5 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -124,6 +124,13 @@ void OptionsModel::Init(bool resetSettings) if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) addOverriddenOption("-upnp"); + if (!settings.contains("fUseNatpmp")) { + settings.setValue("fUseNatpmp", DEFAULT_NATPMP); + } + if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool())) { + addOverriddenOption("-natpmp"); + } + if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) @@ -283,7 +290,13 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fUseUPnP"); #else return false; -#endif +#endif // USE_UPNP + case MapPortNatpmp: +#ifdef USE_NATPMP + return settings.value("fUseNatpmp"); +#else + return false; +#endif // USE_NATPMP case MinimizeOnClose: return fMinimizeOnClose; @@ -356,6 +369,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); break; + case MapPortNatpmp: // core option - can be changed on-the-fly + settings.setValue("fUseNatpmp", value.toBool()); + break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); settings.setValue("fMinimizeOnClose", fMinimizeOnClose); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f1929a5e067..f7171951a10 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -48,6 +48,7 @@ public: ShowTrayIcon, // bool MinimizeToTray, // bool MapPortUPnP, // bool + MapPortNatpmp, // bool MinimizeOnClose, // bool ProxyUse, // bool ProxyIP, // QString diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 62ff5c6e332..b3eb2d61a70 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -362,6 +362,7 @@ def initialize_datadir(dirname, n, chain): f.write("listenonion=0\n") f.write("printtoconsole=0\n") f.write("upnp=0\n") + f.write("natpmp=0\n") f.write("shrinkdebugfile=0\n") os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True)