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 >= 10,
"miniUPnPc API version >= 10 assumed");
41 #if defined(USE_NATPMP) || defined(USE_UPNP) 43 static std::thread g_mapport_thread;
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.ToString());
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 #if MINIUPNPC_API_VERSION < 14 162 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &
error);
164 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &
error);
167 struct UPNPUrls urls;
168 struct IGDdatas data;
171 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr));
175 char externalIPAddress[40];
176 r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
177 if (r != UPNPCOMMAND_SUCCESS) {
178 LogPrintf(
"UPnP: GetExternalIPAddress() returned %d\n", r);
180 if (externalIPAddress[0]) {
182 if (
LookupHost(externalIPAddress, resolved,
false)) {
187 LogPrintf(
"UPnP: GetExternalIPAddress failed.\n");
195 r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(),
"TCP", 0,
"0");
197 if (r != UPNPCOMMAND_SUCCESS) {
199 LogPrintf(
"AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
203 LogPrintf(
"UPnP Port Mapping successful.\n");
205 }
while (g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
206 g_mapport_interrupt.
reset();
208 r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(),
"TCP", 0);
209 LogPrintf(
"UPNP_DeletePortMapping() returned: %d\n", r);
210 freeUPNPDevlist(devlist); devlist =
nullptr;
214 freeUPNPDevlist(devlist); devlist =
nullptr;
223 static void ThreadMapPort()
242 ok = ProcessNatpmp();
252 }
while (ok || g_mapport_interrupt.
sleep_for(PORT_MAPPING_RETRY_PERIOD));
255 void StartThreadMapPort()
257 if (!g_mapport_thread.joinable()) {
258 assert(!g_mapport_interrupt);
263 static void DispatchMapPort()
270 StartThreadMapPort();
280 if (g_mapport_enabled_protos & g_mapport_current_proto) {
285 assert(g_mapport_thread.joinable());
286 assert(!g_mapport_interrupt);
289 g_mapport_interrupt();
295 g_mapport_enabled_protos |= proto;
297 g_mapport_enabled_protos &= ~proto;
311 if (g_mapport_thread.joinable()) {
312 g_mapport_interrupt();
318 if (g_mapport_thread.joinable()) {
319 g_mapport_thread.join();
320 g_mapport_interrupt.
reset();
324 #else // #if defined(USE_NATPMP) || defined(USE_UPNP) 337 #endif // #if defined(USE_NATPMP) || defined(USE_UPNP) bool sleep_for(std::chrono::milliseconds rel_time)
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.
void StartMapPort(bool use_upnp, bool use_natpmp)
std::string ToString() const
void TraceThread(const char *thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
A combination of a network address (CNetAddr) and a (TCP) port.
std::string FormatFullVersion()
bool error(const char *fmt, const Args &... args)