diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index ecba1bb5db19..a0956fe26f43 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -79,7 +79,7 @@ jobs:
- name: macOS
os: macos-10.15
- packages: python3 autoconf automake berkeley-db4 libtool boost miniupnpc pkg-config qt5 zmq libevent qrencode gmp libsodium rust
+ packages: python3 autoconf automake berkeley-db4 libtool boost miniupnpc libnatpmp pkg-config qt5 zmq libevent qrencode gmp libsodium rust
cc: $(brew --prefix llvm)/bin/clang
cxx: $(brew --prefix llvm)/bin/clang++
@@ -165,7 +165,7 @@ jobs:
os: ubuntu-18.04
host: x86_64-unknown-linux-gnu
apt_get: python3-zmq qtbase5-dev qttools5-dev-tools libqt5svg5-dev libqt5charts5-dev libqrencode-dev libdbus-1-dev libharfbuzz-dev
- dep_opts: NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1
+ dep_opts: NO_QT=1 NO_UPNP=1 NO_NATPMP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1
- name: macOS 10.12
os: ubuntu-18.04
@@ -303,7 +303,7 @@ jobs:
- name: x86_64 Linux [GOAL:install] [bionic] [no depends only system libs]
os: ubuntu-18.04
host: x86_64-unknown-linux-gnu
- apt_get: python3-zmq qtbase5-dev qttools5-dev-tools libqt5svg5-dev libqt5charts5-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev libgmp-dev libsodium-dev cargo
+ apt_get: python3-zmq qtbase5-dev qttools5-dev-tools libqt5svg5-dev libqt5charts5-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libgmp-dev libsodium-dev cargo
unit_tests: true
no_depends: 1
goal: install
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0f661c7d3477..cff73d1a9638 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,6 +79,7 @@ find_package(GMP REQUIRED)
find_package(ZMQ)
find_package(Miniupnp)
+find_package(NAT-PMP)
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
find_package(Sodium REQUIRED)
@@ -195,6 +196,7 @@ set(SERVER_SOURCES
./src/interfaces/wallet.cpp
./src/dbwrapper.cpp
./src/legacy/validation_zerocoin_legacy.cpp
+ ./src/mapport.cpp
./src/merkleblock.cpp
./src/miner.cpp
./src/blockassembler.cpp
@@ -559,6 +561,10 @@ if(MINIUPNP_FOUND)
target_link_libraries(pivxd ${MINIUPNP_LIBRARY})
target_include_directories(pivxd PUBLIC ${MINIUPNP_INCLUDE_DIR})
endif()
+if(NAT-PMP_FOUND)
+ target_link_libraries(pivxd ${NAT-PMP_LIBRARY})
+ target_include_directories(pivxd PUBLIC ${NAT-PMP_INCLUDE_DIR})
+endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_link_libraries(pivxd "-framework Cocoa")
diff --git a/build-aux/snap/snapcraft.yaml b/build-aux/snap/snapcraft.yaml
index 187fbc1187a3..f05dc2d04fce 100644
--- a/build-aux/snap/snapcraft.yaml
+++ b/build-aux/snap/snapcraft.yaml
@@ -317,6 +317,7 @@ parts:
- libsodium-dev
- cargo
- libminiupnpc-dev
+ - libnatpmp-dev
- libzmq3-dev
- libqt5gui5
- libqt5core5a
@@ -352,6 +353,7 @@ parts:
- libevent-2.1-6
- libevent-pthreads-2.1-6
- libminiupnpc10
+ - libnatpmp1
- libnorm1
- libpgm-5.2-0
- libprotobuf10
diff --git a/configure.ac b/configure.ac
index 31675dd059dc..2b0fefc6e65a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -146,6 +146,18 @@ AC_ARG_ENABLE([upnp-default],
[use_upnp_default=$enableval],
[use_upnp_default=no])
+AC_ARG_WITH([natpmp],
+ [AS_HELP_STRING([--with-natpmp],
+ [enable NAT-PMP (default is yes if libnatpmp is found)])],
+ [use_natpmp=$withval],
+ [use_natpmp=auto])
+
+AC_ARG_ENABLE([natpmp-default],
+ [AS_HELP_STRING([--enable-natpmp-default],
+ [if NAT-PMP is enabled, turn it on at startup (default is no)])],
+ [use_natpmp_default=$enableval],
+ [use_natpmp_default=no])
+
AC_ARG_ENABLE(tests,
AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]),
[use_tests=$enableval],
@@ -1011,6 +1023,7 @@ if test "x$enable_fuzz" = "xyes"; then
enable_wallet=yes # needs to be built for now.
use_bench=no
use_upnp=no
+ use_natpmp=no
use_zmq=no
else
BITCOIN_QT_INIT
@@ -1053,6 +1066,13 @@ if test x$have_miniupnpc != xno; then
fi
fi
+dnl Check for libnatpmp (optional).
+if test "x$use_natpmp" != xno; then
+ AC_CHECK_HEADERS([natpmp.h],
+ [AC_CHECK_LIB([natpmp], [initnatpmp], [NATPMP_LIBS=-lnatpmp], [have_natpmp=no])],
+ [have_natpmp=no])
+fi
+
if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then
use_boost=no
else
@@ -1358,6 +1378,34 @@ else
fi
fi
+dnl Enable NAT-PMP support.
+AC_MSG_CHECKING([whether to build with support for NAT-PMP])
+if test "x$have_natpmp" = xno; then
+ if test "x$use_natpmp" = xyes; then
+ AC_MSG_ERROR([NAT-PMP requested but cannot be built. Use --without-natpmp])
+ fi
+ AC_MSG_RESULT([no])
+ use_natpmp=no
+else
+ if test "x$use_natpmp" != xno; then
+ AC_MSG_RESULT([yes])
+ AC_MSG_CHECKING([whether to build with NAT-PMP enabled by default])
+ use_natpmp=yes
+ natpmp_setting=0
+ if test "x$use_natpmp_default" != xno; then
+ use_natpmp_default=yes
+ natpmp_setting=1
+ fi
+ AC_MSG_RESULT($use_natpmp_default)
+ AC_DEFINE_UNQUOTED([USE_NATPMP], [$natpmp_setting], [NAT-PMP support not compiled if undefined, otherwise value (0 or 1) determines default state])
+ if test x$TARGET_OS = xwindows; then
+ NATPMP_CPPFLAGS="-DSTATICLIB -DNATPMP_STATICLIB"
+ fi
+ else
+ AC_MSG_RESULT([no])
+ fi
+fi
+
dnl these are only used when qt is enabled
BUILD_TEST_QT=""
if test x$bitcoin_enable_qt != xno; then
@@ -1492,6 +1540,8 @@ AC_SUBST(BOOST_LIBS)
AC_SUBST(TESTDEFS)
AC_SUBST(MINIUPNPC_CPPFLAGS)
AC_SUBST(MINIUPNPC_LIBS)
+AC_SUBST(NATPMP_CPPFLAGS)
+AC_SUBST(NATPMP_LIBS)
AC_SUBST(EVENT_LIBS)
AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(SODIUM_LIBS)
@@ -1580,6 +1630,7 @@ if test x$use_tests != xno; then
fi
echo " with bench = $use_bench"
echo " with upnp = $use_upnp"
+echo " with natpmp = $use_natpmp"
echo " with params = $params_path"
echo " use asm = $use_asm"
echo " sanitizers = $use_sanitizers"
diff --git a/contrib/cmake/FindNAT-PMP.cmake b/contrib/cmake/FindNAT-PMP.cmake
new file mode 100644
index 000000000000..98ada739a03c
--- /dev/null
+++ b/contrib/cmake/FindNAT-PMP.cmake
@@ -0,0 +1,40 @@
+# - Find NAT-PMP
+# This module defines
+# NAT-PMP_INCLUDE_DIR, where to find NAT-PMP headers
+# NAT-PMP_LIBRARY, NAT-PMP libraries
+# NAT-PMP_FOUND, If false, do not try to use NAT-PMP
+
+set(NAT-PMP_PREFIX "" CACHE PATH "path ")
+
+find_path(NAT-PMP_INCLUDE_DIR natpmp.h
+ PATHS ${NAT-PMP_PREFIX}/include /usr/include /usr/local/include)
+
+find_library(NAT-PMP_LIBRARY NAMES natpmp libnatpmp
+ PATHS ${NAT-PMP_PREFIX}/lib /usr/lib /usr/local/lib)
+
+if(NAT-PMP_INCLUDE_DIR AND NAT-PMP_LIBRARY)
+ get_filename_component(NAT-PMP_LIBRARY_DIR ${NAT-PMP_LIBRARY} PATH)
+ set(NAT-PMP_FOUND TRUE)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(
+ NAT-PMP DEFAULT_MSG
+ NAT-PMP_INCLUDE_DIR
+ NAT-PMP_LIBRARY
+)
+
+if(NAT-PMP_FOUND)
+ if(NOT NAT-PMP_FIND_QUIETLY)
+ MESSAGE(STATUS "Found NAT-PMP: ${NAT-PMP_LIBRARY}")
+ endif()
+else()
+ if(NAT-PMP_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find NAT-PMP")
+ endif()
+endif()
+
+mark_as_advanced(
+ NAT-PMP_LIBRARY
+ NAT-PMP_INCLUDE_DIR
+)
\ No newline at end of file
diff --git a/depends/Makefile b/depends/Makefile
index ba86d937f344..5a39ec94ae31 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -36,6 +36,7 @@ NO_QT ?=
NO_WALLET ?=
NO_ZMQ ?=
NO_UPNP ?=
+NO_NATPMP ?=
NO_RUST ?=
FALLBACK_DOWNLOAD_PATH ?= https://depends.pivx.org
@@ -130,9 +131,11 @@ endif
qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages)
wallet_packages_$(NO_WALLET) = $(wallet_packages)
upnp_packages_$(NO_UPNP) = $(upnp_packages)
+natpmp_packages_$(NO_NATPMP) = $(natpmp_packages)
+
zmq_packages_$(NO_ZMQ) = $(zmq_packages)
-packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(rust_packages) $(wallet_packages_) $(upnp_packages_)
+packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(rust_packages) $(wallet_packages_) $(upnp_packages_) $(natpmp_packages_)
native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages)
ifneq ($(qt_packages_),)
@@ -189,6 +192,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_
-e 's|@no_zmq@|$(NO_ZMQ)|' \
-e 's|@no_wallet@|$(NO_WALLET)|' \
-e 's|@no_upnp@|$(NO_UPNP)|' \
+ -e 's|@no_natpmp@|$(NO_NATPMP)|' \
-e 's|@debug@|$(DEBUG)|' \
$< > $@
$(AT)touch $@
diff --git a/depends/README.md b/depends/README.md
index 5baf70979ada..6c9caa838016 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -80,6 +80,8 @@ The following can be set when running make: `make FOO=bar`
Don't download/build/cache libs needed to enable the wallet
NO_UPNP
Don't download/build/cache packages needed for enabling upnp
+NO_NATPMP
+Don't download/build/cache packages needed for enabling NAT-PMP
NO_RUST
Don't download/build/cache rust packages (including librustzcash)
ALLOW_HOST_PACKAGES
diff --git a/depends/config.site.in b/depends/config.site.in
index ade83626560d..cb680237b167 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -28,6 +28,10 @@ if test -z $with_miniupnpc && test -n "@no_upnp@"; then
with_miniupnpc=no
fi
+if test -z $with_natpmp && test -n "@no_natpmp@"; then
+ with_natpmp=no
+fi
+
if test -z $with_gui && test -n "@no_qt@"; then
with_gui=no
fi
diff --git a/depends/packages/libnatpmp.mk b/depends/packages/libnatpmp.mk
new file mode 100644
index 000000000000..cdcf8c0bf2c1
--- /dev/null
+++ b/depends/packages/libnatpmp.mk
@@ -0,0 +1,22 @@
+package=libnatpmp
+$(package)_version=4536032ae32268a45c073a4d5e91bbab4534773a
+$(package)_download_path=https://github.com/miniupnp/libnatpmp/archive
+$(package)_file_name=$($(package)_version).tar.gz
+$(package)_sha256_hash=543b460aab26acf91e11d15e17d8798f845304199eea2d76c2f444ec749c5383
+
+define $(package)_set_vars
+ $(package)_build_opts=CC="$($(package)_cc)"
+ $(package)_build_opts_mingw32=CPPFLAGS=-DNATPMP_STATICLIB
+ $(package)_build_opts_darwin=LIBTOOL="$($(package)_libtool)"
+ $(package)_build_env+=CFLAGS="$($(package)_cflags) $($(package)_cppflags)" AR="$($(package)_ar)"
+endef
+
+define $(package)_build_cmds
+ $(MAKE) libnatpmp.a $($(package)_build_opts)
+endef
+
+define $(package)_stage_cmds
+ mkdir -p $($(package)_staging_prefix_dir)/include $($(package)_staging_prefix_dir)/lib &&\
+ install *.h $($(package)_staging_prefix_dir)/include &&\
+ install libnatpmp.a $($(package)_staging_prefix_dir)/lib
+endef
diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk
index 41349501135c..8a3a63a1b790 100644
--- a/depends/packages/packages.mk
+++ b/depends/packages/packages.mk
@@ -86,6 +86,7 @@ wallet_packages=bdb
zmq_packages=zeromq
upnp_packages=miniupnpc
+natpmp_packages=libnatpmp
darwin_native_packages = native_ds_store native_mac_alias
diff --git a/doc/build-osx.md b/doc/build-osx.md
index 56777b83abde..a92961bfcbe8 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -16,7 +16,7 @@ Then install [Homebrew](https://brew.sh).
Dependencies
----------------------
- brew install autoconf automake berkeley-db4 libtool boost miniupnpc pkg-config python3 qt5 zmq libevent qrencode gmp libsodium rust
+ brew install autoconf automake berkeley-db4 libtool boost miniupnpc libnatpmp pkg-config python3 qt5 zmq libevent qrencode gmp libsodium rust
See [dependencies.md](dependencies.md) for a complete overview.
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 6d07dd7c2edc..f7ae3bfacfcb 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -41,6 +41,7 @@ Optional dependencies:
Library | Purpose | Description
------------|------------------|----------------------
miniupnpc | UPnP Support | Firewall-jumping support
+ libnatpmp | NAT-PMP Support | Firewall-jumping support
libdb4.8 | Berkeley DB | Wallet storage (only needed when wallet enabled)
qt | GUI | GUI toolkit (only needed when GUI enabled)
univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure)
@@ -93,9 +94,9 @@ Otherwise, you can build from self-compiled `depends` (see above).
To build PIVX Core without wallet, see [*Disable-wallet mode*](/doc/build-unix.md#disable-wallet-mode)
-Optional (see --with-miniupnpc and --enable-upnp-default):
+Optional port mapping libraries (see: `--with-miniupnpc`, and `--enable-upnp-default`, `--with-natpmp`, `--enable-natpmp-default`):
- sudo apt-get install libminiupnpc-dev
+ sudo apt install libminiupnpc-dev libnatpmp-dev
ZMQ dependencies (provides ZMQ API):
@@ -127,7 +128,7 @@ Build requirements:
Optional:
- sudo dnf install miniupnpc-devel zeromq-devel
+ sudo dnf install miniupnpc-devel libnatpmp-devel zeromq-devel
To build with Qt 5 you need the following:
@@ -144,12 +145,23 @@ miniupnpc
[miniupnpc](http://miniupnp.free.fr/) may be used for UPnP port mapping. It can be downloaded from [here](
http://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and
-turned off by default. See the configure options for upnp behavior desired:
+turned off by default. See the configure options for UPnp behavior desired:
- --without-miniupnpc No UPnP support miniupnp not required
+ --without-miniupnpc No UPnP support, miniupnp not required
--disable-upnp-default (the default) UPnP support turned off by default at runtime
--enable-upnp-default UPnP support turned on by default at runtime
+libnatpmp
+---------
+
+[libnatpmp](https://miniupnp.tuxfamily.org/libnatpmp.html) may be used for NAT-PMP port mapping. It can be downloaded
+from [here](https://miniupnp.tuxfamily.org/files/). NAT-PMP support is compiled in and
+turned off by default. See the configure options for NAT-PMP behavior desired:
+
+ --without-natpmp No NAT-PMP support, libnatpmp not required
+ --disable-natpmp-default (the default) NAT-PMP support turned off by default at runtime
+ --enable-natpmp-default NAT-PMP support turned on by default at runtime
+
To build:
tar -xzvf miniupnpc-1.6.tar.gz
diff --git a/doc/dependencies.md b/doc/dependencies.md
index 9ff685e6885a..0c997b60d274 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -15,6 +15,7 @@ These are the dependencies currently used by PIVX Core. You can find instruction
| GCC | | [4.8+](https://gcc.gnu.org/) (C++11 support) | | | |
| HarfBuzz-NG | | | | | |
| libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | |
+| libnatpmp | [20150609](https://miniupnp.tuxfamily.org/files) | | No | | |
| libjpeg | | | | | [Yes](https://github.com/pivx-project/pivx/blob/master/depends/packages/qt.mk#L65) |
| libpng | | | | | [Yes](https://github.com/pivx-project/pivx/blob/master/depends/packages/qt.mk#L64) |
| librsvg | | | | | |
@@ -36,7 +37,8 @@ Controlling dependencies
Some dependencies are not needed in all configurations. The following are some factors that affect the dependency list.
#### Options passed to `./configure`
-* MiniUPnPc is not needed with `--with-miniupnpc=no`.
+* MiniUPnPc is not needed with `--without-miniupnpc`.
+* libnatpmp is not needed with `--without-natpmp`.
* Berkeley DB is not needed with `--disable-wallet`.
* Qt is not needed with `--without-gui`.
* If the qrencode dependency is absent, QR support won't be added. To force an error when that happens, pass `--with-qrencode`.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 8f797359a950..41e4d625a9a7 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -83,6 +83,13 @@ Note that while multi-wallet is now fully supported, the RPC multi-wallet interf
GUI changes
-----------
+### Settings
+
+A new checkbox has been added to the wallet settings UI to enable or disable automatic port mapping with NAT-PMP.
+If both UPnP and NAT-PMP are enabled, a successful allocation from UPnP prevails over one from NAT-PMP.
+
+Note: Successful automatic port mapping requires a router that supports either UPnP or NAT-PMP.
+
### RPC-Console
The GUI RPC-Console now accepts "parenthesized syntax", nested commands, and simple queries (see [PR #2282](https://github.com/PIVX-Project/PIVX/pull/2282).
@@ -221,6 +228,12 @@ The minimum supported miniUPnPc API version is set to 10. This keeps compatibili
OpenSSL is no longer used by PIVX Core
+P2P and network changes
+-----------------------
+
+- Added NAT-PMP port mapping support via [`libnatpmp`](https://miniupnp.tuxfamily.org/libnatpmp.html)
+
+
Configuration changes
---------------------
@@ -248,6 +261,11 @@ A new init option flag '-blocksdir' will allow one to keep the blockfiles extern
A new configure flag has been introduced to allow more granular control over weather or not the PoW mining RPC commands are compiled into the wallet. By default they are not. This behavior can be overridden by passing `--enable-mining-rpc` to the `configure` script.
+### Enable NAT-PMP port mapping at startup
+
+The `-natpmp` option has been added to use NAT-PMP to map the listening port. If both UPnP
+and NAT-PMP are enabled, a successful allocation from UPnP prevails over one from NAT-PMP.
+
### Removed startup options
- `printstakemodifier`
diff --git a/doc/tor.md b/doc/tor.md
index fad40436dc94..773082f3d684 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -87,7 +87,7 @@ as well, use `discover` instead:
./pivxd ... -discover
-and open port 51472 on your firewall (or use -upnp).
+and open port 51472 on your firewall (or use port mapping, i.e., `-upnp` or `-natpmp`).
If you only want to use Tor to reach .onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use:
diff --git a/src/Makefile.am b/src/Makefile.am
index 87564a4d964a..b4a257dbb03a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -216,6 +216,7 @@ BITCOIN_CORE_H = \
budget/budgetvote.h \
budget/finalizedbudget.h \
budget/finalizedbudgetvote.h \
+ mapport.h \
memusage.h \
masternode.h \
masternode-payments.h \
@@ -317,7 +318,7 @@ obj/build.h: FORCE
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# server: shared between pivxd and pivx-qt
-libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
@@ -343,6 +344,7 @@ libbitcoin_server_a_SOURCES = \
sapling/sapling_validation.cpp \
merkleblock.cpp \
blockassembler.cpp \
+ mapport.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
@@ -625,7 +627,7 @@ pivxd_LDADD = \
$(LIBRUSTZCASH) \
$(LIBZCASH_LIBS)
-pivxd_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+pivxd_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
# pivx-cli binary #
pivx_cli_SOURCES = pivx-cli.cpp
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 80c81c30276b..9b7d1855ceec 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -47,7 +47,7 @@ if ENABLE_ZMQ
bench_bench_pivx_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
-bench_bench_pivx_LDADD += $(LIBBITCOIN_CONSENSUS) $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
+bench_bench_pivx_LDADD += $(LIBBITCOIN_CONSENSUS) $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
bench_bench_pivx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
# !TODO: .raw.h generated test files are not removed with make clean
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 0c0e91983ae3..d8f6e1a899ec 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -646,7 +646,7 @@ if ENABLE_ZMQ
qt_pivx_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_pivx_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBBITCOIN_ZEROCOIN) $(LIBSAPLING) $(LIBRUSTZCASH) $(LIBZCASH_LIBS) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
- $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(SVG_LIBS) $(CHARTS_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(SVG_LIBS) $(CHARTS_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
qt_pivx_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_pivx_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index b80ec1fa5e6a..fe2be2add0c8 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -28,7 +28,7 @@ qt_test_test_pivx_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_test_test_pivx_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBBITCOIN_ZEROCOIN) $(LIBLEVELDB) $(LIBSAPLING) $(LIBRUSTZCASH) $(LIBZCASH_LIBS) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
- $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
qt_test_test_pivx_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_test_test_pivx_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 6123d0918fd6..4f6b78c02eaa 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -185,7 +185,7 @@ test_test_pivx_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMM
test_test_pivx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_pivx_LDADD += $(LIBRUSTZCASH) $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH_LIBS)
+test_test_pivx_LDADD += $(LIBRUSTZCASH) $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBZCASH_LIBS)
test_test_pivx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
diff --git a/src/init.cpp b/src/init.cpp
index 47dd50e99bf2..cb28d190d310 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -29,6 +29,7 @@
#include "httprpc.h"
#include "invalid.h"
#include "key.h"
+#include "mapport.h"
#include "masternode-payments.h"
#include "masternodeconfig.h"
#include "masternodeman.h"
@@ -502,6 +503,9 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)"));
strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), DEFAULT_UPNP));
+#ifdef USE_NATPMP
+ strUsage += HelpMessageOpt("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"));
+#endif // USE_NATPMP
strUsage += HelpMessageOpt("-whitebind=", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt("-whitelist=", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
@@ -914,10 +918,13 @@ void InitParameterInteraction()
// to protect privacy, do not listen by default if a default proxy server is specified
if (gArgs.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 use 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 (gArgs.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
+ if (gArgs.SoftSetBoolArg("-natpmp", false)) {
+ LogPrintf("%s: parameter interaction: -proxy set -> setting -natpmp=0\n", __func__);
+ }
// to protect privacy, do not discover addresses by default
if (gArgs.SoftSetBoolArg("-discover", false))
LogPrintf("%s : parameter interaction: -proxy set -> setting -discover=0\n", __func__);
@@ -927,6 +934,9 @@ void InitParameterInteraction()
// do not map ports or try to retrieve public IP when not listening (pointless)
if (gArgs.SoftSetBoolArg("-upnp", false))
LogPrintf("%s : parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
+ if (gArgs.SoftSetBoolArg("-natpmp", false)) {
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -natpmp=0\n", __func__);
+ }
if (gArgs.SoftSetBoolArg("-discover", false))
LogPrintf("%s : parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (gArgs.SoftSetBoolArg("-listenonion", false))
@@ -1938,10 +1948,8 @@ bool AppInitMain()
Discover();
- // Map ports with UPnP
- if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) {
- StartMapPort();
- }
+ // Map ports with UPnP or NAT-PMP
+ StartMapPort(gArgs.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP));
std::string strNodeError;
CConnman::Options connOptions;
diff --git a/src/mapport.cpp b/src/mapport.cpp
new file mode 100644
index 000000000000..888918093402
--- /dev/null
+++ b/src/mapport.cpp
@@ -0,0 +1,336 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Copyright (c) 2021 The PIVX developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include "config/pivx-config.h"
+#endif
+
+#include "mapport.h"
+
+#include "clientversion.h"
+#include "logging.h"
+#include "net.h"
+#include "netaddress.h"
+#include "netbase.h"
+#include "threadinterrupt.h"
+#include "util/system.h"
+
+#ifdef USE_NATPMP
+#include
+#include
+#endif // USE_NATPMP
+
+#ifdef USE_UPNP
+#include
+#include
+#include
+// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
+// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
+static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
+#endif // USE_UPNP
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(USE_NATPMP) || defined(USE_UPNP)
+static CThreadInterrupt g_mapport_interrupt;
+static std::thread g_mapport_thread;
+static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
+static std::atomic g_mapport_current_proto{MapPortProtoFlag::NONE};
+
+static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD = std::chrono::minutes(20);
+static constexpr auto PORT_MAPPING_RETRY_PERIOD = std::chrono::minutes(5);
+
+#ifdef USE_NATPMP
+static uint16_t g_mapport_external_port = 0;
+static bool NatpmpInit(natpmp_t* natpmp)
+{
+ const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0);
+ if (r_init == 0) return true;
+ LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init);
+ return false;
+}
+
+static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr)
+{
+ const int r_send = sendpublicaddressrequest(natpmp);
+ if (r_send == 2 /* OK */) {
+ int r_read;
+ natpmpresp_t response;
+ do {
+ r_read = readnatpmpresponseorretry(natpmp, &response);
+ } while (r_read == NATPMP_TRYAGAIN);
+
+ if (r_read == 0) {
+ external_ipv4_addr = response.pnu.publicaddress.addr;
+ return true;
+ } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
+ LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
+ } else {
+ LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
+ }
+ } else {
+ LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
+ }
+
+ return false;
+}
+
+static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered)
+{
+ const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
+ const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/);
+ if (r_send == 12 /* OK */) {
+ int r_read;
+ natpmpresp_t response;
+ do {
+ r_read = readnatpmpresponseorretry(natpmp, &response);
+ } while (r_read == NATPMP_TRYAGAIN);
+
+ if (r_read == 0) {
+ auto pm = response.pnu.newportmapping;
+ if (private_port == pm.privateport && pm.lifetime > 0) {
+ g_mapport_external_port = pm.mappedpublicport;
+ const CService external{external_ipv4_addr, pm.mappedpublicport};
+ if (!external_ip_discovered && fDiscover) {
+ AddLocal(external, LOCAL_MAPPED);
+ external_ip_discovered = true;
+ }
+ LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString());
+ return true;
+ } else {
+ LogPrintf("natpmp: Port mapping failed.\n");
+ }
+ } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
+ LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
+ } else {
+ LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
+ }
+ } else {
+ LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
+ }
+
+ return false;
+}
+
+static bool ProcessNatpmp()
+{
+ bool ret = false;
+ natpmp_t natpmp;
+ struct in_addr external_ipv4_addr{};
+ if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
+ bool external_ip_discovered = false;
+ const uint16_t private_port = GetListenPort();
+ do {
+ ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
+ } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
+ g_mapport_interrupt.reset();
+
+ const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
+ g_mapport_external_port = 0;
+ if (r_send == 12 /* OK */) {
+ LogPrintf("natpmp: Port mapping removed successfully.\n");
+ } else {
+ LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
+ }
+ }
+
+ closenatpmp(&natpmp);
+ return ret;
+}
+#endif // USE_NATPMP
+
+#ifdef USE_UPNP
+static bool ProcessUpnp()
+{
+ bool ret = false;
+ std::string port = strprintf("%u", GetListenPort());
+ const char* multicastif = nullptr;
+ const char* minissdpdpath = nullptr;
+ struct UPNPDev* devlist = nullptr;
+ char lanaddr[64];
+
+ int error = 0;
+#if MINIUPNPC_API_VERSION < 14
+ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
+#else
+ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
+#endif
+
+ struct UPNPUrls urls{};
+ struct IGDdatas data{};
+ int r;
+
+ r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
+ if (r == 1) {
+ if (fDiscover) {
+ char externalIPAddress[40];
+ r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+ if (r != UPNPCOMMAND_SUCCESS) {
+ LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
+ } else {
+ if (externalIPAddress[0]) {
+ CNetAddr resolved;
+ if (LookupHost(externalIPAddress, resolved, false)) {
+ LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str());
+ AddLocal(resolved, LOCAL_MAPPED);
+ }
+ } else {
+ LogPrintf("UPnP: GetExternalIPAddress failed.\n");
+ }
+ }
+ }
+
+ std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
+
+ do {
+ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", nullptr, "0");
+
+ if (r != UPNPCOMMAND_SUCCESS) {
+ ret = false;
+ LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
+ break;
+ } else {
+ ret = true;
+ LogPrintf("UPnP Port Mapping successful.\n");
+ }
+ } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
+ g_mapport_interrupt.reset();
+
+ r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", nullptr);
+ LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
+ freeUPNPDevlist(devlist); devlist = nullptr;
+ FreeUPNPUrls(&urls);
+ } else {
+ LogPrintf("No valid UPnP IGDs found\n");
+ freeUPNPDevlist(devlist);
+ devlist = nullptr;
+ if (r != 0)
+ FreeUPNPUrls(&urls);
+ }
+
+ return ret;
+}
+#endif // USE_UPNP
+
+static void ThreadMapPort()
+{
+ bool ok;
+ do {
+ ok = false;
+
+#ifdef USE_UPNP
+ // High priority protocol.
+ if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
+ g_mapport_current_proto = MapPortProtoFlag::UPNP;
+ ok = ProcessUpnp();
+ if (ok) continue;
+ }
+#endif // USE_UPNP
+
+#ifdef USE_NATPMP
+ // Low priority protocol.
+ if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
+ g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
+ ok = ProcessNatpmp();
+ if (ok) continue;
+ }
+#endif // USE_NATPMP
+
+ g_mapport_current_proto = MapPortProtoFlag::NONE;
+ if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
+ return;
+ }
+
+ } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
+}
+
+void StartThreadMapPort()
+{
+ if (!g_mapport_thread.joinable()) {
+ assert(!g_mapport_interrupt);
+ g_mapport_thread = std::thread(std::bind(&TraceThread, "mapport", &ThreadMapPort));
+ }
+}
+
+static void DispatchMapPort()
+{
+ if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
+ return;
+ }
+
+ if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
+ StartThreadMapPort();
+ return;
+ }
+
+ if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
+ InterruptMapPort();
+ StopMapPort();
+ return;
+ }
+
+ if (g_mapport_enabled_protos & g_mapport_current_proto) {
+ // Enabling another protocol does not cause switching from the currently used one.
+ return;
+ }
+
+ assert(g_mapport_thread.joinable());
+ assert(!g_mapport_interrupt);
+ // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
+ // to force trying the next protocol in the ThreadMapPort() loop.
+ g_mapport_interrupt();
+}
+
+static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
+{
+ if (enabled) {
+ g_mapport_enabled_protos |= proto;
+ } else {
+ g_mapport_enabled_protos &= ~proto;
+ }
+}
+
+void StartMapPort(bool use_upnp, bool use_natpmp)
+{
+ MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
+ MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
+ DispatchMapPort();
+}
+
+void InterruptMapPort()
+{
+ g_mapport_enabled_protos = MapPortProtoFlag::NONE;
+ if (g_mapport_thread.joinable()) {
+ g_mapport_interrupt();
+ }
+}
+
+void StopMapPort()
+{
+ if (g_mapport_thread.joinable()) {
+ g_mapport_thread.join();
+ g_mapport_interrupt.reset();
+ }
+}
+
+#else // #if defined(USE_NATPMP) || defined(USE_UPNP)
+void StartMapPort(bool use_upnp, bool use_natpmp)
+{
+ // Intentionally left blank.
+}
+void InterruptMapPort()
+{
+ // Intentionally left blank.
+}
+void StopMapPort()
+{
+ // Intentionally left blank.
+}
+#endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
diff --git a/src/mapport.h b/src/mapport.h
new file mode 100644
index 000000000000..34ca80b14575
--- /dev/null
+++ b/src/mapport.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Copyright (c) 2021 The PIVX developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef PIVX_MAPPORT_H
+#define PIVX_MAPPORT_H
+
+#ifdef USE_UPNP
+static constexpr bool DEFAULT_UPNP = USE_UPNP;
+#else
+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,
+ UPNP = 0x01,
+ NAT_PMP = 0x02,
+};
+
+void StartMapPort(bool use_upnp, bool use_natpmp);
+void InterruptMapPort();
+void StopMapPort();
+
+#endif //PIVX_MAPPORT_H
diff --git a/src/net.cpp b/src/net.cpp
index d203c07bf382..27c2eff4bb9e 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -35,16 +35,6 @@
#include
#endif
-#ifdef USE_UPNP
-#include
-#include
-#include
-// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
-// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
-static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
-#endif
-
-
#include
// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s)
@@ -1357,111 +1347,6 @@ void CConnman::WakeMessageHandler()
}
-#ifdef USE_UPNP
-static CThreadInterrupt g_upnp_interrupt;
-static std::thread g_upnp_thread;
-void ThreadMapPort()
-{
- std::string port = strprintf("%u", GetListenPort());
- const char* multicastif = 0;
- const char* minissdpdpath = 0;
- struct UPNPDev* devlist = 0;
- char lanaddr[64];
-
- int error = 0;
-#if MINIUPNPC_API_VERSION < 14
- devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
-#else
- devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
-#endif
-
- struct UPNPUrls urls;
- struct IGDdatas data;
- int r;
-
- r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
- if (r == 1) {
- if (fDiscover) {
- char externalIPAddress[40];
- r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
- if (r != UPNPCOMMAND_SUCCESS) {
- LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
- } else {
- if (externalIPAddress[0]) {
- CNetAddr resolved;
- if (LookupHost(externalIPAddress, resolved, false)) {
- LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str());
- AddLocal(resolved, LOCAL_UPNP);
- }
- } else {
- LogPrintf("UPnP: GetExternalIPAddress failed.\n");
- }
- }
- }
-
- std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
-
- do {
- r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
-
- if (r != UPNPCOMMAND_SUCCESS) {
- LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
- } else {
- LogPrintf("UPnP Port Mapping successful.\n");
- }
- } while(g_upnp_interrupt.sleep_for(std::chrono::minutes(20)));
-
- r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
- LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
- freeUPNPDevlist(devlist); devlist = nullptr;
- FreeUPNPUrls(&urls);
- } else {
- LogPrintf("No valid UPnP IGDs found\n");
- freeUPNPDevlist(devlist);
- devlist = 0;
- if (r != 0)
- FreeUPNPUrls(&urls);
- }
-}
-
-void StartMapPort()
-{
- if (!g_upnp_thread.joinable()) {
- assert(!g_upnp_interrupt);
- g_upnp_thread = std::thread(std::bind(&TraceThread, "upnp", &ThreadMapPort));
- }
-}
-
-void InterruptMapPort()
-{
- if (g_upnp_thread.joinable()) {
- g_upnp_interrupt();
- }
-}
-
-void StopMapPort()
-{
- if (g_upnp_thread.joinable()) {
- g_upnp_thread.join();
- g_upnp_interrupt.reset();
- }
-}
-
-#else
-void StartMapPort()
-{
- // Intentionally left blank.
-}
-void InterruptMapPort()
-{
- // Intentionally left blank.
-}
-void StopMapPort()
-{
- // Intentionally left blank.
-}
-#endif
-
static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredServiceBits)
{
//use default host for non-filter-capable seeds or if we use the default service bits (NODE_NETWORK)
diff --git a/src/net.h b/src/net.h
index fa5f538fed70..f0b6db6ec2c5 100644
--- a/src/net.h
+++ b/src/net.h
@@ -61,12 +61,6 @@ static const unsigned int MAX_SUBVERSION_LENGTH = 256;
static const int MAX_OUTBOUND_CONNECTIONS = 16;
/** -listen default */
static const bool DEFAULT_LISTEN = true;
-/** -upnp default */
-#ifdef USE_UPNP
-static const bool DEFAULT_UPNP = USE_UPNP;
-#else
-static const bool DEFAULT_UPNP = false;
-#endif
/** The maximum number of entries in mapAskFor */
static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
/** The maximum number of entries in setAskFor (larger due to getdata latency)*/
@@ -389,9 +383,6 @@ class CConnman
};
extern std::unique_ptr g_connman;
void Discover();
-void StartMapPort();
-void InterruptMapPort();
-void StopMapPort();
unsigned short GetListenPort();
bool BindListenPort(const CService& bindAddr, std::string& strError, bool fWhitelisted = false);
void CheckOffsetDisconnectedPeers(const CNetAddr& ip);
@@ -427,7 +418,7 @@ enum {
LOCAL_NONE, // unknown
LOCAL_IF, // address a local interface listens on
LOCAL_BIND, // address explicit bound to
- LOCAL_UPNP, // address reported by UPnP
+ LOCAL_MAPPED, // address reported by UPnP or NAT-PMP
LOCAL_MANUAL, // address explicitly specified (-externalip=)
LOCAL_MAX
diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt
index fd4131b419e9..8cc88cbc28e8 100644
--- a/src/qt/CMakeLists.txt
+++ b/src/qt/CMakeLists.txt
@@ -223,6 +223,10 @@ if(MINIUPNP_FOUND)
target_link_libraries(pivx-qt ${MINIUPNP_LIBRARY})
target_include_directories(pivx-qt PUBLIC ${MINIUPNP_INCLUDE_DIR})
endif()
+if(NAT-PMP_FOUND)
+ target_link_libraries(pivx-qt ${NAT-PMP_LIBRARY})
+ target_include_directories(pivx-qt PUBLIC ${NAT-PMP_INCLUDE_DIR})
+endif()
target_link_libraries(pivx-qt Qt5::Gui Qt5::Core Qt5::Widgets Qt5::Network Qt5::Svg ${QT_LIBRARIES})
if (Qt5DBus_FOUND)
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 51096dcabaa4..e12e44921d2e 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -15,6 +15,7 @@
#include "checkpoints.h"
#include "clientversion.h"
#include "interfaces/handler.h"
+#include "mapport.h"
#include "masternodeman.h"
#include "net.h"
#include "netbase.h"
@@ -347,6 +348,10 @@ void ClientModel::unsubscribeFromCoreSignals()
m_handler_notify_block_tip->disconnect();
}
+void ClientModel::mapPort(bool use_upnp, bool use_natpmp) {
+ StartMapPort(use_upnp, use_natpmp);
+}
+
bool ClientModel::getTorInfo(std::string& ip_port) const
{
proxyType onion;
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index f783aaecd931..93aa427b1ae1 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -9,6 +9,7 @@
#include "uint256.h"
#include "chain.h"
+
#include
#include
@@ -94,6 +95,9 @@ class ClientModel : public QObject
bool getTorInfo(std::string& ip_port) const;
+ //! Set the automatic port mapping options
+ static void mapPort(bool use_upnp, bool use_natpmp);
+
// Start/Stop the masternode polling timer
void startMasternodesTimer();
void stopMasternodesTimer();
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 7f4d39da2702..3939828ecb0e 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -15,6 +15,7 @@
#include "amount.h"
#include "init.h"
+#include "mapport.h"
#include "net.h"
#include "netbase.h"
#include "txdb.h" // for -dbcache defaults
@@ -137,6 +138,11 @@ void OptionsModel::setNetworkDefaultOptions(QSettings& settings, bool reset)
if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp");
+ if (!settings.contains("fUseNatpmp") || reset )
+ settings.setValue("fUseNatpmp", DEFAULT_NATPMP);
+ if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool()))
+ addOverriddenOption("-natpmp");
+
if (!settings.contains("fListen") || reset)
settings.setValue("fListen", DEFAULT_LISTEN);
if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
@@ -235,7 +241,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;
@@ -308,12 +320,9 @@ bool OptionsModel::setData(const QModelIndex& index, const QVariant& value, int
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- if (value.toBool()) {
- StartMapPort();
- } else {
- InterruptMapPort();
- StopMapPort();
- }
+ break;
+ case MapPortNatpmp: // core option - can be changed on-the-fly
+ settings.setValue("fUseNatpmp", value.toBool());
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 8e7b81e5181c..6c72a28191a8 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -28,6 +28,7 @@ class OptionsModel : public QAbstractListModel
StartAtStartup, // bool
MinimizeToTray, // bool
MapPortUPnP, // bool
+ MapPortNatpmp, // bool
MinimizeOnClose, // bool
ProxyUse, // bool
ProxyIP, // QString
diff --git a/src/qt/pivx/settings/forms/settingswalletoptionswidget.ui b/src/qt/pivx/settings/forms/settingswalletoptionswidget.ui
index 8c0af9566890..8e9afc5f884b 100644
--- a/src/qt/pivx/settings/forms/settingswalletoptionswidget.ui
+++ b/src/qt/pivx/settings/forms/settingswalletoptionswidget.ui
@@ -290,12 +290,22 @@
0
-
-
+
Map port using UPnP
+ -
+
+
+ Automatically open the PIVX client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.
+
+
+ Map port using NAT-PMP
+
+
+
-
diff --git a/src/qt/pivx/settings/settingswalletoptionswidget.cpp b/src/qt/pivx/settings/settingswalletoptionswidget.cpp
index 26a61951c170..f03214b5a822 100644
--- a/src/qt/pivx/settings/settingswalletoptionswidget.cpp
+++ b/src/qt/pivx/settings/settingswalletoptionswidget.cpp
@@ -32,7 +32,12 @@ SettingsWalletOptionsWidget::SettingsWalletOptionsWidget(PIVXGUI* _window, QWidg
ui->spinBoxStakeSplitThreshold->setAttribute(Qt::WA_MacShowFocusRect, 0);
setShadow(ui->spinBoxStakeSplitThreshold);
- // Radio buttons
+#ifndef USE_UPNP
+ ui->mapPortUpnp->setVisible(false);
+#endif
+#ifndef USE_NATPMP
+ ui->mapPortNatpmp->setVisible(false);
+#endif
// Title
ui->labelTitleNetwork->setText(tr("Network"));
@@ -62,6 +67,7 @@ void SettingsWalletOptionsWidget::onResetClicked()
QSettings settings;
walletModel->resetWalletOptions(settings);
clientModel->getOptionsModel()->setNetworkDefaultOptions(settings, true);
+ saveMapPortOptions();
inform(tr("Options reset succeed"));
}
@@ -70,7 +76,8 @@ void SettingsWalletOptionsWidget::setMapper(QDataWidgetMapper *mapper)
mapper->addMapping(ui->radioButtonSpend, OptionsModel::SpendZeroConfChange);
// Network
- mapper->addMapping(ui->checkBoxMap, OptionsModel::MapPortUPnP);
+ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
+ mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp);
mapper->addMapping(ui->checkBoxAllow, OptionsModel::Listen);
mapper->addMapping(ui->checkBoxConnect, OptionsModel::ProxyUse);
mapper->addMapping(ui->lineEditProxy, OptionsModel::ProxyIP);
@@ -120,6 +127,11 @@ void SettingsWalletOptionsWidget::discardWalletOnlyOptions()
reloadWalletOptions();
}
+void SettingsWalletOptionsWidget::saveMapPortOptions()
+{
+ clientModel->mapPort(ui->mapPortUpnp->isChecked(), ui->mapPortNatpmp->isChecked());
+}
+
SettingsWalletOptionsWidget::~SettingsWalletOptionsWidget(){
delete ui;
}
diff --git a/src/qt/pivx/settings/settingswalletoptionswidget.h b/src/qt/pivx/settings/settingswalletoptionswidget.h
index 55a3f7f84905..665f4ecd57b1 100644
--- a/src/qt/pivx/settings/settingswalletoptionswidget.h
+++ b/src/qt/pivx/settings/settingswalletoptionswidget.h
@@ -25,6 +25,8 @@ class SettingsWalletOptionsWidget : public PWidget
void discardWalletOnlyOptions();
bool saveWalletOnlyOptions();
+ void saveMapPortOptions();
+
Q_SIGNALS:
void saveSettings();
void discardSettings();
diff --git a/src/qt/pivx/settings/settingswidget.cpp b/src/qt/pivx/settings/settingswidget.cpp
index 99510cd387ef..5db04f16da5b 100644
--- a/src/qt/pivx/settings/settingswidget.cpp
+++ b/src/qt/pivx/settings/settingswidget.cpp
@@ -224,6 +224,9 @@ void SettingsWidget::onSaveOptionsClicked()
return;
}
+ // Save port mapping settings
+ settingsWalletOptionsWidget->saveMapPortOptions();
+
if (mapper->submit()) {
OptionsModel* optionsModel = this->clientModel->getOptionsModel();
if (optionsModel->isRestartRequired()) {
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index db5df51ce43f..c18a36e32003 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -179,6 +179,10 @@ if(MINIUPNP_FOUND)
target_link_libraries(test_pivx PRIVATE ${MINIUPNP_LIBRARY})
target_include_directories(test_pivx PRIVATE ${MINIUPNP_INCLUDE_DIR})
endif()
+if(NAT-PMP_FOUND)
+ target_link_libraries(test_pivx PRIVATE ${NAT-PMP_LIBRARY})
+ target_include_directories(test_pivx PRIVATE ${NAT-PMP_INCLUDE_DIR})
+endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_link_libraries(test_pivx PRIVATE "-framework Cocoa")
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index de17d6fb3ad9..7faefbf7453c 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -316,6 +316,7 @@ def initialize_datadir(dirname, n):
f.write("listenonion=0\n")
f.write("spendzeroconfchange=1\n")
f.write("printtoconsole=0\n")
+ f.write("natpmp=0\n")
return datadir
def get_datadir_path(dirname, n):