5#if defined(HAVE_CONFIG_H)
6#include <config/bitcoin-config.h>
26#include <miniupnpc/miniupnpc.h>
27#include <miniupnpc/upnpcommands.h>
28#include <miniupnpc/upnperrors.h>
31static_assert(MINIUPNPC_API_VERSION >= 10,
32 "miniUPnPc API version >= 10 assumed");
42#if defined(USE_NATPMP) || defined(USE_UPNP)
44static std::thread g_mapport_thread;
46static std::atomic<MapPortProtoFlag> g_mapport_current_proto{
49using namespace std::chrono_literals;
50static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
51static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
54static uint16_t g_mapport_external_port = 0;
55static bool NatpmpInit(natpmp_t *natpmp) {
56 const int r_init = initnatpmp(natpmp, 0,
61 LogPrintf(
"natpmp: initnatpmp() failed with %d error.\n", r_init);
65static bool NatpmpDiscover(natpmp_t *natpmp,
66 struct in_addr &external_ipv4_addr) {
67 const int r_send = sendpublicaddressrequest(natpmp);
72 r_read = readnatpmpresponseorretry(natpmp, &
response);
73 }
while (r_read == NATPMP_TRYAGAIN);
76 external_ipv4_addr =
response.pnu.publicaddress.addr;
78 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
79 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
81 LogPrintf(
"natpmp: readnatpmpresponseorretry() for public address "
82 "failed with %d error.\n",
86 LogPrintf(
"natpmp: sendpublicaddressrequest() failed with %d error.\n",
93static bool NatpmpMapping(natpmp_t *natpmp,
94 const struct in_addr &external_ipv4_addr,
95 uint16_t private_port,
bool &external_ip_discovered) {
96 const uint16_t suggested_external_port =
97 g_mapport_external_port ? g_mapport_external_port : private_port;
99 sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port,
100 suggested_external_port, 3600 );
105 r_read = readnatpmpresponseorretry(natpmp, &
response);
106 }
while (r_read == NATPMP_TRYAGAIN);
109 auto pm =
response.pnu.newportmapping;
110 if (private_port == pm.privateport && pm.lifetime > 0) {
111 g_mapport_external_port = pm.mappedpublicport;
112 const CService external{external_ipv4_addr,
113 pm.mappedpublicport};
114 if (!external_ip_discovered &&
fDiscover) {
116 external_ip_discovered =
true;
119 "natpmp: Port mapping successful. External address = %s\n",
120 external.ToString());
123 LogPrintf(
"natpmp: Port mapping failed.\n");
125 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
126 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
128 LogPrintf(
"natpmp: readnatpmpresponseorretry() for port mapping "
129 "failed with %d error.\n",
133 LogPrintf(
"natpmp: sendnewportmappingrequest() failed with %d error.\n",
140[[maybe_unused]]
static bool ProcessNatpmp() {
143 struct in_addr external_ipv4_addr;
144 if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
145 bool external_ip_discovered =
false;
148 ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port,
149 external_ip_discovered);
151 g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
152 g_mapport_interrupt.
reset();
154 const int r_send = sendnewportmappingrequest(
155 &natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port,
157 g_mapport_external_port = 0;
159 LogPrintf(
"natpmp: Port mapping removed successfully.\n");
162 "natpmp: sendnewportmappingrequest(0) failed with %d error.\n",
167 closenatpmp(&natpmp);
173static bool ProcessUpnp() {
176 const char *multicastif =
nullptr;
177 const char *minissdpdpath =
nullptr;
178 struct UPNPDev *devlist =
nullptr;
182#if MINIUPNPC_API_VERSION < 14
183 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &
error);
185 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &
error);
188 struct UPNPUrls urls;
189 struct IGDdatas data;
191#if MINIUPNPC_API_VERSION <= 17
192 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr));
194 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr),
199 char externalIPAddress[40];
200 r = UPNP_GetExternalIPAddress(
201 urls.controlURL, data.first.servicetype, externalIPAddress);
202 if (r != UPNPCOMMAND_SUCCESS) {
203 LogPrintf(
"UPnP: GetExternalIPAddress() returned %d\n", r);
205 if (externalIPAddress[0]) {
207 if (
LookupHost(externalIPAddress, resolved,
false)) {
208 LogPrintf(
"UPnP: ExternalIPAddress = %s\n",
213 LogPrintf(
"UPnP: GetExternalIPAddress failed.\n");
221 r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
222 port.c_str(), port.c_str(), lanaddr,
223 strDesc.c_str(),
"TCP", 0,
"0");
225 if (r != UPNPCOMMAND_SUCCESS) {
228 "AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
229 port, port, lanaddr, r, strupnperror(r));
233 LogPrintf(
"UPnP Port Mapping successful.\n");
235 }
while (g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
236 g_mapport_interrupt.
reset();
238 r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype,
239 port.c_str(),
"TCP", 0);
240 LogPrintf(
"UPNP_DeletePortMapping() returned: %d\n", r);
241 freeUPNPDevlist(devlist);
246 freeUPNPDevlist(devlist);
257static void ThreadMapPort() {
277 ok = ProcessNatpmp();
289 }
while (ok || g_mapport_interrupt.
sleep_for(PORT_MAPPING_RETRY_PERIOD));
292void StartThreadMapPort() {
293 if (!g_mapport_thread.joinable()) {
294 assert(!g_mapport_interrupt);
300static void DispatchMapPort() {
308 StartThreadMapPort();
319 if (g_mapport_enabled_protos & g_mapport_current_proto) {
325 assert(g_mapport_thread.joinable());
326 assert(!g_mapport_interrupt);
330 g_mapport_interrupt();
335 g_mapport_enabled_protos |= proto;
337 g_mapport_enabled_protos &= ~proto;
349 if (g_mapport_thread.joinable()) {
350 g_mapport_interrupt();
355 if (g_mapport_thread.joinable()) {
356 g_mapport_thread.join();
357 g_mapport_interrupt.
reset();
std::string ToString() const
A combination of a network address (CNetAddr) and a (TCP) port.
A helper class for interruptible sleeps.
bool sleep_for(std::chrono::milliseconds rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut)
std::string FormatFullVersion()
bool error(const char *fmt, const Args &...args)
void StartMapPort(bool use_upnp, bool use_natpmp)
void TraceThread(const char *thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
bool AddLocal(const CService &addr, int nScore)
bool LookupHost(const std::string &name, std::vector< CNetAddr > &vIP, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
Resolve a host string to its corresponding network addresses.