5 #if defined(HAVE_CONFIG_H) 25 #include <miniupnpc/miniupnpc.h> 26 #include <miniupnpc/upnpcommands.h> 27 #include <miniupnpc/upnperrors.h> 30 static_assert(MINIUPNPC_API_VERSION >= 10,
"miniUPnPc API version >= 10 assumed");
40 #if defined(USE_NATPMP) || defined(USE_UPNP) 42 static std::thread g_mapport_thread;
47 static constexpr
auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
48 static constexpr
auto PORT_MAPPING_RETRY_PERIOD{5min};
51 static uint16_t g_mapport_external_port = 0;
52 static bool NatpmpInit(natpmp_t* natpmp)
54 const int r_init = initnatpmp(natpmp, 0, 0);
55 if (r_init == 0)
return true;
56 LogPrintf(
"natpmp: initnatpmp() failed with %d error.\n", r_init);
60 static bool NatpmpDiscover(natpmp_t* natpmp,
struct in_addr& external_ipv4_addr)
62 const int r_send = sendpublicaddressrequest(natpmp);
65 natpmpresp_t response;
67 r_read = readnatpmpresponseorretry(natpmp, &response);
68 }
while (r_read == NATPMP_TRYAGAIN);
71 external_ipv4_addr = response.pnu.publicaddress.addr;
73 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
74 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
76 LogPrintf(
"natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
79 LogPrintf(
"natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
85 static bool NatpmpMapping(natpmp_t* natpmp,
const struct in_addr& external_ipv4_addr, uint16_t private_port,
bool& external_ip_discovered)
87 const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
88 const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 );
91 natpmpresp_t response;
93 r_read = readnatpmpresponseorretry(natpmp, &response);
94 }
while (r_read == NATPMP_TRYAGAIN);
97 auto pm = response.pnu.newportmapping;
98 if (private_port == pm.privateport && pm.lifetime > 0) {
99 g_mapport_external_port = pm.mappedpublicport;
100 const CService external{external_ipv4_addr, pm.mappedpublicport};
101 if (!external_ip_discovered &&
fDiscover) {
103 external_ip_discovered =
true;
105 LogPrintf(
"natpmp: Port mapping successful. External address = %s\n", external.ToString());
108 LogPrintf(
"natpmp: Port mapping failed.\n");
110 }
else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
111 LogPrintf(
"natpmp: The gateway does not support NAT-PMP.\n");
113 LogPrintf(
"natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
116 LogPrintf(
"natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
122 static bool ProcessNatpmp()
126 struct in_addr external_ipv4_addr;
127 if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
128 bool external_ip_discovered =
false;
131 ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
132 }
while (ret && g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
133 g_mapport_interrupt.
reset();
135 const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, 0);
136 g_mapport_external_port = 0;
138 LogPrintf(
"natpmp: Port mapping removed successfully.\n");
140 LogPrintf(
"natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
144 closenatpmp(&natpmp);
150 static bool ProcessUpnp()
154 const char * multicastif =
nullptr;
155 const char * minissdpdpath =
nullptr;
156 struct UPNPDev * devlist =
nullptr;
160 #if MINIUPNPC_API_VERSION < 14 161 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
163 devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
166 struct UPNPUrls urls;
167 struct IGDdatas data;
170 r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr,
sizeof(lanaddr));
174 char externalIPAddress[40];
175 r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
176 if (r != UPNPCOMMAND_SUCCESS) {
177 LogPrintf(
"UPnP: GetExternalIPAddress() returned %d\n", r);
179 if (externalIPAddress[0]) {
181 if (
LookupHost(externalIPAddress, resolved,
false)) {
186 LogPrintf(
"UPnP: GetExternalIPAddress failed.\n");
194 r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(),
"TCP", 0,
"0");
196 if (r != UPNPCOMMAND_SUCCESS) {
198 LogPrintf(
"AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
202 LogPrintf(
"UPnP Port Mapping successful.\n");
204 }
while (g_mapport_interrupt.
sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
205 g_mapport_interrupt.
reset();
207 r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(),
"TCP", 0);
208 LogPrintf(
"UPNP_DeletePortMapping() returned: %d\n", r);
209 freeUPNPDevlist(devlist); devlist =
nullptr;
213 freeUPNPDevlist(devlist); devlist =
nullptr;
222 static void ThreadMapPort()
241 ok = ProcessNatpmp();
251 }
while (ok || g_mapport_interrupt.
sleep_for(PORT_MAPPING_RETRY_PERIOD));
254 void StartThreadMapPort()
256 if (!g_mapport_thread.joinable()) {
257 assert(!g_mapport_interrupt);
258 g_mapport_thread = std::thread(std::bind(&
TraceThread<
void (*)()>,
"mapport", &ThreadMapPort));
262 static void DispatchMapPort()
269 StartThreadMapPort();
279 if (g_mapport_enabled_protos & g_mapport_current_proto) {
284 assert(g_mapport_thread.joinable());
285 assert(!g_mapport_interrupt);
288 g_mapport_interrupt();
294 g_mapport_enabled_protos |= proto;
296 g_mapport_enabled_protos &= ~proto;
302 MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
303 MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
310 if (g_mapport_thread.joinable()) {
311 g_mapport_interrupt();
317 if (g_mapport_thread.joinable()) {
318 g_mapport_thread.join();
319 g_mapport_interrupt.
reset();
323 #else // #if defined(USE_NATPMP) || defined(USE_UPNP) 336 #endif // #if defined(USE_NATPMP) || defined(USE_UPNP) bool sleep_for(std::chrono::milliseconds rel_time)
bool AddLocal(const CService &addr, int nScore)
static void LogPrintf(const char *fmt, const Args &... args)
void StartMapPort(bool use_upnp, bool use_natpmp)
std::string ToString() const
A combination of a network address (CNetAddr) and a (TCP) port.
void TraceThread(const char *name, Callable func)
std::string FormatFullVersion()
bool LookupHost(const std::string &name, std::vector< CNetAddr > &vIP, unsigned int nMaxSolutions, bool fAllowLookup)
Resolve a host string to its corresponding network addresses.
bool error(const char *fmt, const Args &... args)