Branch data Line data Source code
# 1 : : // Copyright (c) 2011-2020 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #if defined(HAVE_CONFIG_H)
# 6 : : #include <config/bitcoin-config.h>
# 7 : : #endif
# 8 : :
# 9 : : #include <mapport.h>
# 10 : :
# 11 : : #include <clientversion.h>
# 12 : : #include <logging.h>
# 13 : : #include <net.h>
# 14 : : #include <netaddress.h>
# 15 : : #include <netbase.h>
# 16 : : #include <threadinterrupt.h>
# 17 : : #include <util/system.h>
# 18 : : #include <util/thread.h>
# 19 : :
# 20 : : #ifdef USE_NATPMP
# 21 : : #include <compat.h>
# 22 : : #include <natpmp.h>
# 23 : : #endif // USE_NATPMP
# 24 : :
# 25 : : #ifdef USE_UPNP
# 26 : : #include <miniupnpc/miniupnpc.h>
# 27 : : #include <miniupnpc/upnpcommands.h>
# 28 : : #include <miniupnpc/upnperrors.h>
# 29 : : // The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
# 30 : : // with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
# 31 : : static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
# 32 : : #endif // USE_UPNP
# 33 : :
# 34 : : #include <atomic>
# 35 : : #include <cassert>
# 36 : : #include <chrono>
# 37 : : #include <functional>
# 38 : : #include <string>
# 39 : : #include <thread>
# 40 : :
# 41 : : #if defined(USE_NATPMP) || defined(USE_UPNP)
# 42 : : static CThreadInterrupt g_mapport_interrupt;
# 43 : : static std::thread g_mapport_thread;
# 44 : : static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
# 45 : : static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
# 46 : :
# 47 : : using namespace std::chrono_literals;
# 48 : : static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
# 49 : : static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
# 50 : :
# 51 : : #ifdef USE_NATPMP
# 52 : : static uint16_t g_mapport_external_port = 0;
# 53 : : static bool NatpmpInit(natpmp_t* natpmp)
# 54 : 0 : {
# 55 : 0 : const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0);
# 56 [ # # ]: 0 : if (r_init == 0) return true;
# 57 : 0 : LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init);
# 58 : 0 : return false;
# 59 : 0 : }
# 60 : :
# 61 : : static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr)
# 62 : 0 : {
# 63 : 0 : const int r_send = sendpublicaddressrequest(natpmp);
# 64 [ # # ]: 0 : if (r_send == 2 /* OK */) {
# 65 : 0 : int r_read;
# 66 : 0 : natpmpresp_t response;
# 67 : 0 : do {
# 68 : 0 : r_read = readnatpmpresponseorretry(natpmp, &response);
# 69 [ # # ]: 0 : } while (r_read == NATPMP_TRYAGAIN);
# 70 : :
# 71 [ # # ]: 0 : if (r_read == 0) {
# 72 : 0 : external_ipv4_addr = response.pnu.publicaddress.addr;
# 73 : 0 : return true;
# 74 [ # # ]: 0 : } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
# 75 : 0 : LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
# 76 : 0 : } else {
# 77 : 0 : LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
# 78 : 0 : }
# 79 : 0 : } else {
# 80 : 0 : LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
# 81 : 0 : }
# 82 : :
# 83 : 0 : return false;
# 84 : 0 : }
# 85 : :
# 86 : : static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered)
# 87 : 0 : {
# 88 [ # # ]: 0 : const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
# 89 : 0 : const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/);
# 90 [ # # ]: 0 : if (r_send == 12 /* OK */) {
# 91 : 0 : int r_read;
# 92 : 0 : natpmpresp_t response;
# 93 : 0 : do {
# 94 : 0 : r_read = readnatpmpresponseorretry(natpmp, &response);
# 95 [ # # ]: 0 : } while (r_read == NATPMP_TRYAGAIN);
# 96 : :
# 97 [ # # ]: 0 : if (r_read == 0) {
# 98 : 0 : auto pm = response.pnu.newportmapping;
# 99 [ # # ][ # # ]: 0 : if (private_port == pm.privateport && pm.lifetime > 0) {
# 100 : 0 : g_mapport_external_port = pm.mappedpublicport;
# 101 : 0 : const CService external{external_ipv4_addr, pm.mappedpublicport};
# 102 [ # # ][ # # ]: 0 : if (!external_ip_discovered && fDiscover) {
# 103 : 0 : AddLocal(external, LOCAL_MAPPED);
# 104 : 0 : external_ip_discovered = true;
# 105 : 0 : }
# 106 : 0 : LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString());
# 107 : 0 : return true;
# 108 : 0 : } else {
# 109 : 0 : LogPrintf("natpmp: Port mapping failed.\n");
# 110 : 0 : }
# 111 [ # # ]: 0 : } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
# 112 : 0 : LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
# 113 : 0 : } else {
# 114 : 0 : LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
# 115 : 0 : }
# 116 : 0 : } else {
# 117 : 0 : LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
# 118 : 0 : }
# 119 : :
# 120 : 0 : return false;
# 121 : 0 : }
# 122 : :
# 123 : : static bool ProcessNatpmp()
# 124 : 0 : {
# 125 : 0 : bool ret = false;
# 126 : 0 : natpmp_t natpmp;
# 127 : 0 : struct in_addr external_ipv4_addr;
# 128 [ # # ][ # # ]: 0 : if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
# 129 : 0 : bool external_ip_discovered = false;
# 130 : 0 : const uint16_t private_port = GetListenPort();
# 131 : 0 : do {
# 132 : 0 : ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
# 133 [ # # ][ # # ]: 0 : } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
# 134 : 0 : g_mapport_interrupt.reset();
# 135 : :
# 136 : 0 : const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
# 137 : 0 : g_mapport_external_port = 0;
# 138 [ # # ]: 0 : if (r_send == 12 /* OK */) {
# 139 : 0 : LogPrintf("natpmp: Port mapping removed successfully.\n");
# 140 : 0 : } else {
# 141 : 0 : LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
# 142 : 0 : }
# 143 : 0 : }
# 144 : :
# 145 : 0 : closenatpmp(&natpmp);
# 146 : 0 : return ret;
# 147 : 0 : }
# 148 : : #endif // USE_NATPMP
# 149 : :
# 150 : : #ifdef USE_UPNP
# 151 : : static bool ProcessUpnp()
# 152 : 0 : {
# 153 : 0 : bool ret = false;
# 154 : 0 : std::string port = strprintf("%u", GetListenPort());
# 155 : 0 : const char * multicastif = nullptr;
# 156 : 0 : const char * minissdpdpath = nullptr;
# 157 : 0 : struct UPNPDev * devlist = nullptr;
# 158 : 0 : char lanaddr[64];
# 159 : :
# 160 : 0 : int error = 0;
# 161 : : #if MINIUPNPC_API_VERSION < 14
# 162 : : devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
# 163 : : #else
# 164 : 0 : devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
# 165 : 0 : #endif
# 166 : :
# 167 : 0 : struct UPNPUrls urls;
# 168 : 0 : struct IGDdatas data;
# 169 : 0 : int r;
# 170 : :
# 171 : 0 : r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
# 172 [ # # ]: 0 : if (r == 1)
# 173 : 0 : {
# 174 [ # # ]: 0 : if (fDiscover) {
# 175 : 0 : char externalIPAddress[40];
# 176 : 0 : r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
# 177 [ # # ]: 0 : if (r != UPNPCOMMAND_SUCCESS) {
# 178 : 0 : LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
# 179 : 0 : } else {
# 180 [ # # ]: 0 : if (externalIPAddress[0]) {
# 181 : 0 : CNetAddr resolved;
# 182 [ # # ]: 0 : if (LookupHost(externalIPAddress, resolved, false)) {
# 183 : 0 : LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString());
# 184 : 0 : AddLocal(resolved, LOCAL_MAPPED);
# 185 : 0 : }
# 186 : 0 : } else {
# 187 : 0 : LogPrintf("UPnP: GetExternalIPAddress failed.\n");
# 188 : 0 : }
# 189 : 0 : }
# 190 : 0 : }
# 191 : :
# 192 : 0 : std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
# 193 : :
# 194 : 0 : do {
# 195 : 0 : r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
# 196 : :
# 197 [ # # ]: 0 : if (r != UPNPCOMMAND_SUCCESS) {
# 198 : 0 : ret = false;
# 199 : 0 : LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
# 200 : 0 : break;
# 201 : 0 : } else {
# 202 : 0 : ret = true;
# 203 : 0 : LogPrintf("UPnP Port Mapping successful.\n");
# 204 : 0 : }
# 205 [ # # ]: 0 : } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
# 206 : 0 : g_mapport_interrupt.reset();
# 207 : :
# 208 : 0 : r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
# 209 : 0 : LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
# 210 : 0 : freeUPNPDevlist(devlist); devlist = nullptr;
# 211 : 0 : FreeUPNPUrls(&urls);
# 212 : 0 : } else {
# 213 : 0 : LogPrintf("No valid UPnP IGDs found\n");
# 214 : 0 : freeUPNPDevlist(devlist); devlist = nullptr;
# 215 [ # # ]: 0 : if (r != 0)
# 216 : 0 : FreeUPNPUrls(&urls);
# 217 : 0 : }
# 218 : :
# 219 : 0 : return ret;
# 220 : 0 : }
# 221 : : #endif // USE_UPNP
# 222 : :
# 223 : : static void ThreadMapPort()
# 224 : 0 : {
# 225 : 0 : bool ok;
# 226 : 0 : do {
# 227 : 0 : ok = false;
# 228 : :
# 229 : 0 : #ifdef USE_UPNP
# 230 : : // High priority protocol.
# 231 [ # # ]: 0 : if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
# 232 : 0 : g_mapport_current_proto = MapPortProtoFlag::UPNP;
# 233 : 0 : ok = ProcessUpnp();
# 234 [ # # ]: 0 : if (ok) continue;
# 235 : 0 : }
# 236 : 0 : #endif // USE_UPNP
# 237 : :
# 238 : 0 : #ifdef USE_NATPMP
# 239 : : // Low priority protocol.
# 240 [ # # ]: 0 : if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
# 241 : 0 : g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
# 242 : 0 : ok = ProcessNatpmp();
# 243 [ # # ]: 0 : if (ok) continue;
# 244 : 0 : }
# 245 : 0 : #endif // USE_NATPMP
# 246 : :
# 247 : 0 : g_mapport_current_proto = MapPortProtoFlag::NONE;
# 248 [ # # ]: 0 : if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
# 249 : 0 : return;
# 250 : 0 : }
# 251 : :
# 252 [ # # ][ # # ]: 0 : } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
# 253 : 0 : }
# 254 : :
# 255 : : void StartThreadMapPort()
# 256 : 0 : {
# 257 [ # # ]: 0 : if (!g_mapport_thread.joinable()) {
# 258 : 0 : assert(!g_mapport_interrupt);
# 259 : 0 : g_mapport_thread = std::thread(&util::TraceThread, "mapport", &ThreadMapPort);
# 260 : 0 : }
# 261 : 0 : }
# 262 : :
# 263 : : static void DispatchMapPort()
# 264 : 621 : {
# 265 [ + - ][ + - ]: 621 : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
# 266 : 621 : return;
# 267 : 621 : }
# 268 : :
# 269 [ # # ][ # # ]: 0 : if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
# 270 : 0 : StartThreadMapPort();
# 271 : 0 : return;
# 272 : 0 : }
# 273 : :
# 274 [ # # ][ # # ]: 0 : if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
# 275 : 0 : InterruptMapPort();
# 276 : 0 : StopMapPort();
# 277 : 0 : return;
# 278 : 0 : }
# 279 : :
# 280 [ # # ]: 0 : if (g_mapport_enabled_protos & g_mapport_current_proto) {
# 281 : : // Enabling another protocol does not cause switching from the currently used one.
# 282 : 0 : return;
# 283 : 0 : }
# 284 : :
# 285 : 0 : assert(g_mapport_thread.joinable());
# 286 : 0 : assert(!g_mapport_interrupt);
# 287 : : // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
# 288 : : // to force trying the next protocol in the ThreadMapPort() loop.
# 289 : 0 : g_mapport_interrupt();
# 290 : 0 : }
# 291 : :
# 292 : : static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
# 293 : 1242 : {
# 294 [ - + ]: 1242 : if (enabled) {
# 295 : 0 : g_mapport_enabled_protos |= proto;
# 296 : 1242 : } else {
# 297 : 1242 : g_mapport_enabled_protos &= ~proto;
# 298 : 1242 : }
# 299 : 1242 : }
# 300 : :
# 301 : : void StartMapPort(bool use_upnp, bool use_natpmp)
# 302 : 621 : {
# 303 : 621 : MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
# 304 : 621 : MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
# 305 : 621 : DispatchMapPort();
# 306 : 621 : }
# 307 : :
# 308 : : void InterruptMapPort()
# 309 : 663 : {
# 310 : 663 : g_mapport_enabled_protos = MapPortProtoFlag::NONE;
# 311 [ - + ]: 663 : if (g_mapport_thread.joinable()) {
# 312 : 0 : g_mapport_interrupt();
# 313 : 0 : }
# 314 : 663 : }
# 315 : :
# 316 : : void StopMapPort()
# 317 : 663 : {
# 318 [ - + ]: 663 : if (g_mapport_thread.joinable()) {
# 319 : 0 : g_mapport_thread.join();
# 320 : 0 : g_mapport_interrupt.reset();
# 321 : 0 : }
# 322 : 663 : }
# 323 : :
# 324 : : #else // #if defined(USE_NATPMP) || defined(USE_UPNP)
# 325 : : void StartMapPort(bool use_upnp, bool use_natpmp)
# 326 : : {
# 327 : : // Intentionally left blank.
# 328 : : }
# 329 : : void InterruptMapPort()
# 330 : : {
# 331 : : // Intentionally left blank.
# 332 : : }
# 333 : : void StopMapPort()
# 334 : : {
# 335 : : // Intentionally left blank.
# 336 : : }
# 337 : : #endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
|