diff --git a/alephzero/alephzero.pro b/alephzero/alephzero.pro index 74d63351c..c6e8d583b 100644 --- a/alephzero/alephzero.pro +++ b/alephzero/alephzero.pro @@ -14,7 +14,7 @@ CONFIG(debug, debug|release): DEFINES += ETH_DEBUG QMAKE_CXXFLAGS += -std=c++11 QMAKE_LIBDIR += ../../cpp-ethereum-build/libethereum ../../secp256k1 ../../cryptopp562 -LIBS += -Wl,-rpath,../../cpp-ethereum-build/libethereum -Wl,-rpath,../../secp256k1 -Wl,-rpath,../../cryptopp562 -lethereum -lsecp256k1 -lleveldb -lcryptopp -lgmp -lboost_filesystem -lboost_system +LIBS += -Wl,-rpath,../../cpp-ethereum-build/libethereum -Wl,-rpath,../../secp256k1 -Wl,-rpath,../../cryptopp562 -lethereum -lminiupnpc -lsecp256k1 -lleveldb -lcryptopp -lgmp -lboost_filesystem -lboost_system INCLUDEPATH = ../../secp256k1/include ../../cryptopp562 ../../cpp-ethereum SOURCES += main.cpp \ diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt index 98cd4c745..0e7cef06e 100644 --- a/eth/CMakeLists.txt +++ b/eth/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(eth ${SRC_LIST}) find_package(Threads REQUIRED) target_link_libraries(eth ethereum) +target_link_libraries(eth miniupnpc) target_link_libraries(eth leveldb) target_link_libraries(eth secp256k1) target_link_libraries(eth cryptopp) diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index a7153fa5e..cd79edd66 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(ethereum ${SRC_LIST}) find_package(Threads REQUIRED) target_link_libraries(ethereum secp256k1) +target_link_libraries(ethereum miniupnpc) target_link_libraries(ethereum leveldb) target_link_libraries(ethereum cryptopp) target_link_libraries(ethereum gmp) diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index f1fb87f0b..7317083d6 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "Common.h" #include "BlockChain.h" #include "BlockInfo.h" @@ -450,7 +451,7 @@ void PeerSession::start() { RLPStream s; prep(s); - s.appendList(5) << (uint)Hello << (uint)0 << (uint)0 << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_acceptor.local_endpoint().port(); + s.appendList(5) << (uint)Hello << (uint)0 << (uint)0 << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_public.port(); sealAndSend(s); ping(); @@ -508,16 +509,169 @@ void PeerSession::doRead() }); } +#include +#include + +#include +#include +#include + +namespace eth { +struct UPnP +{ + UPnP() + { + ok = false; + + struct UPNPDev * devlist; + struct UPNPDev * dev; + char * descXML; + int descXMLsize = 0; + int upnperror = 0; + printf("TB : init_upnp()\n"); + memset(&urls, 0, sizeof(struct UPNPUrls)); + memset(&data, 0, sizeof(struct IGDdatas)); + devlist = upnpDiscover(2000, NULL/*multicast interface*/, NULL/*minissdpd socket path*/, 0/*sameport*/, 0/*ipv6*/, &upnperror); + if (devlist) + { + dev = devlist; + while (dev) + { + if (strstr (dev->st, "InternetGatewayDevice")) + break; + dev = dev->pNext; + } + if (!dev) + dev = devlist; /* defaulting to first device */ + + printf("UPnP device :\n" + " desc: %s\n st: %s\n", + dev->descURL, dev->st); + + descXML = (char*)miniwget(dev->descURL, &descXMLsize); + if (descXML) + { + parserootdesc (descXML, descXMLsize, &data); + free (descXML); descXML = 0; + GetUPNPUrls (&urls, &data, dev->descURL); + ok = true; + } + freeUPNPDevlist(devlist); + } + else + { + /* error ! */ + } + } + ~UPnP() + { + auto r = m_reg; + for (auto i: r) + removeRedirect(i); + } + + string externalIP() + { + char addr[16]; + UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, addr); + return addr; + } + + int addRedirect(char const* addr, int port) + { + char port_str[16]; + int r; + printf("TB : upnp_add_redir (%d)\n", port); + if (urls.controlURL[0] == '\0') + { + printf("TB : the init was not done !\n"); + return -1; + } + sprintf(port_str, "%d", port); + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port_str, port_str, addr, "ethereum", "TCP", NULL, NULL); + if (r) + { + printf("AddPortMapping(%s, %s, %s) failed with %d. Trying non-specific external port...\n", port_str, port_str, addr, r); + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port_str, NULL, addr, "ethereum", "TCP", NULL, NULL); + } + if (r) + { + printf("AddPortMapping(%s, NULL, %s) failed with %d. Trying non-specific internal port...\n", port_str, addr, r); + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, NULL, port_str, addr, "ethereum", "TCP", NULL, NULL); + } + if (r) + { + printf("AddPortMapping(NULL, %s, %s) failed with %d. Trying non-specific both ports...\n", port_str, addr, r); + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, NULL, NULL, addr, "ethereum", "TCP", NULL, NULL); + } + if (r) + printf("AddPortMapping(NULL, NULL, %s) failed with %d\n", addr, r); + else + { + unsigned num = 0; + UPNP_GetPortMappingNumberOfEntries(urls.controlURL, data.first.servicetype, &num); + for (unsigned i = 0; i < num; ++i) + { + char extPort[16]; + char intClient[16]; + char intPort[6]; + char protocol[4]; + char desc[80]; + char enabled[4]; + char rHost[64]; + char duration[16]; + UPNP_GetGenericPortMappingEntry(urls.controlURL, data.first.servicetype, toString(i).c_str(), extPort, intClient, intPort, protocol, desc, enabled, rHost, duration); + if (string("ethereum") == desc) + { + m_reg.insert(atoi(extPort)); + return atoi(extPort); + } + } + cerr << "ERROR: Mapped port not found." << endl; + } + return -1; + } + + void removeRedirect(int port) + { + char port_str[16]; +// int t; + printf("TB : upnp_rem_redir (%d)\n", port); + if (urls.controlURL[0] == '\0') + { + printf("TB : the init was not done !\n"); + return; + } + sprintf(port_str, "%d", port); + UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", NULL); + m_reg.erase(port); + } + + bool isValid() const + { + return ok; + } + + set m_reg; + + bool ok; + struct UPNPUrls urls; + struct IGDdatas data; +}; +} + class NoNetworking: public std::exception {}; PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port): m_clientVersion(_clientVersion), + m_listenPort(_port), m_chain(&_ch), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), m_socket(m_ioService), m_requiredNetworkId(_networkId) { populateAddresses(); + determinePublic(); ensureAccepting(); if (m_verbosity) cout << "Genesis: " << m_chain->genesisHash() << endl; @@ -525,6 +679,7 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId): m_clientVersion(_clientVersion), + m_listenPort(-1), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), m_socket(m_ioService), m_requiredNetworkId(_networkId) @@ -540,6 +695,37 @@ PeerServer::~PeerServer() for (auto const& i: m_peers) if (auto p = i.lock()) p->disconnect(); + delete m_upnp; +} + +void PeerServer::determinePublic() +{ + m_upnp = new UPnP; + if (m_upnp->isValid() && m_peerAddresses.size()) + { + bi::tcp::resolver r(m_ioService); + cout << "external addr: " << m_upnp->externalIP() << endl; + int p = m_upnp->addRedirect(m_peerAddresses[0].to_string().c_str(), m_listenPort); + if (p == -1) + { + // couldn't map + cerr << "*** WARNING: Couldn't punch through NAT (or no NAT in place). Using " << m_listenPort << endl; + p = m_listenPort; + } + + auto it = r.resolve({m_upnp->externalIP(), toString(p)}); + m_public = it->endpoint(); + m_addresses.push_back(m_public.address().to_v4()); + } +/* int er; + UPNPDev* dlist = upnpDiscover(250, 0, 0, 0, 0, &er); + for (UPNPDev* d = dlist; d; d = dlist->pNext) + { + IGDdatas data; + parserootdesc(d->descURL, 0, &data); + data.presentationurl() + } + freeUPNPDevlist(dlist);*/ } void PeerServer::populateAddresses() @@ -588,7 +774,7 @@ void PeerServer::ensureAccepting() if (m_accepting == false) { if (m_verbosity >= 1) - cout << "Listening on port " << m_acceptor.local_endpoint().port() << endl; + cout << "Listening on local port " << m_listenPort << " (public: " << m_public << ")" << endl; m_accepting = true; m_acceptor.async_accept(m_socket, [&](boost::system::error_code ec) { diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index d8984139e..780a766a9 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -117,6 +117,8 @@ enum class NodeMode PeerServer }; +class UPnP; + class PeerServer { friend class PeerSession; @@ -159,6 +161,7 @@ public: private: void seal(bytes& _b); void populateAddresses(); + void determinePublic(); void ensureAccepting(); std::vector potentialPeers(); @@ -176,11 +179,16 @@ private: */ unsigned m_verbosity = 4; + short m_listenPort; + BlockChain const* m_chain = nullptr; ba::io_service m_ioService; bi::tcp::acceptor m_acceptor; bi::tcp::socket m_socket; + UPnP* m_upnp = nullptr; + bi::tcp::endpoint m_public; + uint m_requiredNetworkId; std::vector> m_peers; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index af8967ce9..0923fbbee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(testeth ${SRC_LIST}) find_package(Threads REQUIRED) target_link_libraries(testeth ethereum) +target_link_libraries(testeth miniupnpc) target_link_libraries(testeth cryptopp) target_link_libraries(testeth secp256k1) target_link_libraries(testeth gmp)