24 #include <miniupnpc/miniupnpc.h>
25 #include <miniupnpc/upnpcommands.h>
26 #include <miniupnpc/upnperrors.h>
29 static_assert(MINIUPNPC_API_VERSION >= 17,
"miniUPnPc API version >= 17 assumed");
39 #if defined(USE_NATPMP) || defined(USE_UPNP)
41 static std::thread g_mapport_thread;
45 using namespace std::chrono_literals;
46 static constexpr
auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
47 static constexpr
auto PORT_MAPPING_RETRY_PERIOD{5min};
50 static uint16_t g_mapport_external_port = 0;
51 static bool NatpmpInit(natpmp_t* natpmp)
53 const int r_init = initnatpmp(natpmp, 0, 0);
54 if (r_init == 0)
return true;
55 LogPrintf(
"natpmp: initnatpmp() failed with %d error.\n", r_init);
59 static bool NatpmpDiscover(natpmp_t* natpmp,
struct in_addr& external_ipv4_addr)
61 const int r_send = sendpublicaddressrequest(natpmp);
64 natpmpresp_t response;
66 r_read = readnatpmpresponseorretry(natpmp, &response);
67 }
while (r_read == NATPMP_TRYAGAIN);
70 external_ipv4_addr = response.pnu.publicaddress.addr;
72 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
73 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
75 LogPrintf(
"natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
78 LogPrintf(
"natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
84 static bool NatpmpMapping(natpmp_t* natpmp,
const struct in_addr& external_ipv4_addr, uint16_t private_port,
bool& external_ip_discovered)
86 const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
87 const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 );
90 natpmpresp_t response;
92 r_read = readnatpmpresponseorretry(natpmp, &response);
93 }
while (r_read == NATPMP_TRYAGAIN);
96 auto pm = response.pnu.newportmapping;
97 if (private_port == pm.privateport && pm.lifetime > 0) {
98 g_mapport_external_port = pm.mappedpublicport;
99 const CService external{external_ipv4_addr, pm.mappedpublicport};
100 if (!external_ip_discovered &&
fDiscover) {
102 external_ip_discovered =
true;
104 LogPrintf(
"natpmp: Port mapping successful. External address = %s\n", external.ToStringAddrPort());
107 LogPrintf(
"natpmp: Port mapping failed.\n");
109 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
110 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
112 LogPrintf(
"natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
115 LogPrintf(
"natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
121 static bool ProcessNatpmp()
125 struct in_addr external_ipv4_addr;
126 if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
127 bool external_ip_discovered =
false;
130 ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
131 }
while (
ret && g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
132 g_mapport_interrupt.
reset();
134 const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, 0);
135 g_mapport_external_port = 0;
137 LogPrintf(
"natpmp: Port mapping removed successfully.\n");
139 LogPrintf(
"natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
143 closenatpmp(&natpmp);
149 static bool ProcessUpnp()
153 const char * multicastif =
nullptr;
154 const char * minissdpdpath =
nullptr;
155 struct UPNPDev * devlist =
nullptr;
159 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
161 struct UPNPUrls urls;
162 struct IGDdatas data;
164 #if MINIUPNPC_API_VERSION <= 17
165 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr));
167 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr),
nullptr, 0);
172 char externalIPAddress[40];
173 r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
174 if (r != UPNPCOMMAND_SUCCESS) {
175 LogPrintf(
"UPnP: GetExternalIPAddress() returned %d\n", r);
177 if (externalIPAddress[0]) {
178 std::optional<CNetAddr> resolved{
LookupHost(externalIPAddress,
false)};
179 if (resolved.has_value()) {
180 LogPrintf(
"UPnP: ExternalIPAddress = %s\n", resolved->ToStringAddr());
184 LogPrintf(
"UPnP: GetExternalIPAddress failed.\n");
192 r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(),
"TCP",
nullptr,
"0");
194 if (r != UPNPCOMMAND_SUCCESS) {
196 LogPrintf(
"AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
200 LogPrintf(
"UPnP Port Mapping successful.\n");
202 }
while (g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
203 g_mapport_interrupt.
reset();
205 r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(),
"TCP",
nullptr);
206 LogPrintf(
"UPNP_DeletePortMapping() returned: %d\n", r);
207 freeUPNPDevlist(devlist); devlist =
nullptr;
211 freeUPNPDevlist(devlist); devlist =
nullptr;
220 static void ThreadMapPort()
239 ok = ProcessNatpmp();
249 }
while (ok || g_mapport_interrupt.
sleep_for(PORT_MAPPING_RETRY_PERIOD));
252 void StartThreadMapPort()
254 if (!g_mapport_thread.joinable()) {
255 assert(!g_mapport_interrupt);
260 static void DispatchMapPort()
267 StartThreadMapPort();
277 if (g_mapport_enabled_protos & g_mapport_current_proto) {
282 assert(g_mapport_thread.joinable());
283 assert(!g_mapport_interrupt);
286 g_mapport_interrupt();
292 g_mapport_enabled_protos |= proto;
294 g_mapport_enabled_protos &= ~proto;
308 if (g_mapport_thread.joinable()) {
309 g_mapport_interrupt();
315 if (g_mapport_thread.joinable()) {
316 g_mapport_thread.join();
317 g_mapport_interrupt.
reset();
A combination of a network address (CNetAddr) and a (TCP) port.
A helper class for interruptible sleeps.
bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut)
std::string FormatFullVersion()
void StartMapPort(bool use_upnp, bool use_natpmp)
void TraceThread(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
bool AddLocal(const CService &addr_, int nScore)
std::vector< CNetAddr > LookupHost(const std::string &name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
Resolve a host string to its corresponding network addresses.