5 #if defined(HAVE_CONFIG_H)
26 #include <miniupnpc/miniupnpc.h>
27 #include <miniupnpc/upnpcommands.h>
28 #include <miniupnpc/upnperrors.h>
31 static_assert(MINIUPNPC_API_VERSION >= 17,
"miniUPnPc API version >= 17 assumed");
41 #if defined(USE_NATPMP) || defined(USE_UPNP)
43 static std::thread g_mapport_thread;
47 using namespace std::chrono_literals;
48 static constexpr
auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
49 static constexpr
auto PORT_MAPPING_RETRY_PERIOD{5min};
52 static uint16_t g_mapport_external_port = 0;
53 static bool NatpmpInit(natpmp_t* natpmp)
55 const int r_init = initnatpmp(natpmp, 0, 0);
56 if (r_init == 0)
return true;
57 LogPrintf(
"natpmp: initnatpmp() failed with %d error.\n", r_init);
61 static bool NatpmpDiscover(natpmp_t* natpmp,
struct in_addr& external_ipv4_addr)
63 const int r_send = sendpublicaddressrequest(natpmp);
66 natpmpresp_t response;
68 r_read = readnatpmpresponseorretry(natpmp, &response);
69 }
while (r_read == NATPMP_TRYAGAIN);
72 external_ipv4_addr = response.pnu.publicaddress.addr;
74 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
75 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
77 LogPrintf(
"natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
80 LogPrintf(
"natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
86 static bool NatpmpMapping(natpmp_t* natpmp,
const struct in_addr& external_ipv4_addr, uint16_t private_port,
bool& external_ip_discovered)
88 const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
89 const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 );
92 natpmpresp_t response;
94 r_read = readnatpmpresponseorretry(natpmp, &response);
95 }
while (r_read == NATPMP_TRYAGAIN);
98 auto pm = response.pnu.newportmapping;
99 if (private_port == pm.privateport && pm.lifetime > 0) {
100 g_mapport_external_port = pm.mappedpublicport;
101 const CService external{external_ipv4_addr, pm.mappedpublicport};
102 if (!external_ip_discovered &&
fDiscover) {
104 external_ip_discovered =
true;
106 LogPrintf(
"natpmp: Port mapping successful. External address = %s\n", external.ToStringAddrPort());
109 LogPrintf(
"natpmp: Port mapping failed.\n");
111 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
112 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
114 LogPrintf(
"natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
117 LogPrintf(
"natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
123 static bool ProcessNatpmp()
127 struct in_addr external_ipv4_addr;
128 if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
129 bool external_ip_discovered =
false;
132 ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
133 }
while (
ret && g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
134 g_mapport_interrupt.
reset();
136 const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, 0);
137 g_mapport_external_port = 0;
139 LogPrintf(
"natpmp: Port mapping removed successfully.\n");
141 LogPrintf(
"natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
145 closenatpmp(&natpmp);
151 static bool ProcessUpnp()
155 const char * multicastif =
nullptr;
156 const char * minissdpdpath =
nullptr;
157 struct UPNPDev * devlist =
nullptr;
161 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
163 struct UPNPUrls urls;
164 struct IGDdatas data;
167 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr));
171 char externalIPAddress[40];
172 r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
173 if (r != UPNPCOMMAND_SUCCESS) {
174 LogPrintf(
"UPnP: GetExternalIPAddress() returned %d\n", r);
176 if (externalIPAddress[0]) {
177 std::optional<CNetAddr> resolved{
LookupHost(externalIPAddress,
false)};
178 if (resolved.has_value()) {
179 LogPrintf(
"UPnP: ExternalIPAddress = %s\n", resolved->ToStringAddr());
183 LogPrintf(
"UPnP: GetExternalIPAddress failed.\n");
191 r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(),
"TCP",
nullptr,
"0");
193 if (r != UPNPCOMMAND_SUCCESS) {
195 LogPrintf(
"AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
199 LogPrintf(
"UPnP Port Mapping successful.\n");
201 }
while (g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
202 g_mapport_interrupt.
reset();
204 r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(),
"TCP",
nullptr);
205 LogPrintf(
"UPNP_DeletePortMapping() returned: %d\n", r);
206 freeUPNPDevlist(devlist); devlist =
nullptr;
210 freeUPNPDevlist(devlist); devlist =
nullptr;
219 static void ThreadMapPort()
238 ok = ProcessNatpmp();
248 }
while (ok || g_mapport_interrupt.
sleep_for(PORT_MAPPING_RETRY_PERIOD));
251 void StartThreadMapPort()
253 if (!g_mapport_thread.joinable()) {
254 assert(!g_mapport_interrupt);
259 static void DispatchMapPort()
266 StartThreadMapPort();
276 if (g_mapport_enabled_protos & g_mapport_current_proto) {
281 assert(g_mapport_thread.joinable());
282 assert(!g_mapport_interrupt);
285 g_mapport_interrupt();
291 g_mapport_enabled_protos |= proto;
293 g_mapport_enabled_protos &= ~proto;
307 if (g_mapport_thread.joinable()) {
308 g_mapport_interrupt();
314 if (g_mapport_thread.joinable()) {
315 g_mapport_thread.join();
316 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.