diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index 0429c52ab..b995723c3 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -26,6 +26,16 @@ using namespace dev::p2p; const unsigned dev::p2p::c_protocolVersion = 3; +bool p2p::isPublicAddress(std::string const& _addressToCheck) +{ + return isPublicAddress(bi::address::from_string(_addressToCheck)); +} + +bool p2p::isPublicAddress(bi::address const& _addressToCheck) +{ + return !(isPrivateAddress(_addressToCheck) || isLocalHostAddress(_addressToCheck)); +} + // Helper function to determine if an address falls within one of the reserved ranges // For V4: // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" @@ -55,6 +65,11 @@ bool p2p::isPrivateAddress(bi::address const& _addressToCheck) return false; } +bool p2p::isPrivateAddress(std::string const& _addressToCheck) +{ + return isPrivateAddress(bi::address::from_string(_addressToCheck)); +} + // Helper function to determine if an address is localhost bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) { @@ -69,6 +84,11 @@ bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) return find(c_rejectAddresses.begin(), c_rejectAddresses.end(), _addressToCheck) != c_rejectAddresses.end(); } +bool p2p::isLocalHostAddress(std::string const& _addressToCheck) +{ + return isLocalHostAddress(bi::address::from_string(_addressToCheck)); +} + std::string p2p::reasonOf(DisconnectReason _r) { switch (_r) @@ -89,3 +109,9 @@ std::string p2p::reasonOf(DisconnectReason _r) default: return "Unknown reason."; } } + +void Node::cullEndpoint() +{ + if (!isPublicAddress(endpoint.tcp.address()) && isPublicAddress(endpoint.udp.address())) + endpoint.tcp.address(endpoint.udp.address()); +} diff --git a/libp2p/Common.h b/libp2p/Common.h index 66b3f08e8..3303c9c07 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -54,7 +54,11 @@ extern const unsigned c_protocolVersion; using NodeId = h512; bool isPrivateAddress(bi::address const& _addressToCheck); +bool isPrivateAddress(std::string const& _addressToCheck); bool isLocalHostAddress(bi::address const& _addressToCheck); +bool isLocalHostAddress(std::string const& _addressToCheck); +bool isPublicAddress(bi::address const& _addressToCheck); +bool isPublicAddress(std::string const& _addressToCheck); class UPnP; class Capability; @@ -62,6 +66,8 @@ class Host; class Session; struct NetworkStartRequired: virtual dev::Exception {}; +struct InvalidPublicIPAddress: virtual dev::Exception {}; +struct InvalidHostIPAddress: virtual dev::Exception {}; struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; }; struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; }; @@ -169,6 +175,9 @@ struct Node virtual NodeId const& address() const { return id; } virtual Public const& publicKey() const { return id; } + /// Adopt UDP address for TCP if TCP isn't public and UDP is. (to be removed when protocol is updated for nat) + void cullEndpoint(); + NodeId id; /// Endpoints by which we expect to reach node. diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b946d9b3e..41c2fbb77 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -592,12 +592,12 @@ void Host::startedWorking() m_run = true; } - // try to open acceptor (todo: ipv6) - m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); - - // start capability threads + // start capability threads (ready for incoming connections) for (auto const& h: m_capabilities) h.second->onStarting(); + + // try to open acceptor (todo: ipv6) + m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); // determine public IP, but only if we're able to listen for connections // todo: GUI when listen is unavailable in UI diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 7fe531762..c230c5441 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -70,25 +70,22 @@ shared_ptr NodeTable::addNode(Public const& _pubk, bi::udp::endpoint shared_ptr NodeTable::addNode(Node const& _node) { - if (_node.endpoint.udp.address().to_string() == "0.0.0.0" || _node.endpoint.tcp.address().to_string() == "0.0.0.0") + // re-enable tcp checks when NAT hosts are handled by discover + // we handle when tcp endpoint is 0 below + if (_node.endpoint.udp.address().to_string() == "0.0.0.0") { - string ptype; - if (_node.endpoint.udp.address().to_string() != "0.0.0.0") - ptype = "TCP"; - else if (_node.endpoint.tcp.address().to_string() != "0.0.0.0") - ptype = "UDP"; - else - ptype = "TCP,UDP"; - - clog(NodeTableWarn) << "addNode Failed. Invalid" << ptype << "address 0.0.0.0 for" << _node.id.abridged(); + clog(NodeTableWarn) << "addNode Failed. Invalid UDP address 0.0.0.0 for" << _node.id.abridged(); return move(shared_ptr()); } - // ping address if nodeid is empty + // ping address to recover nodeid if nodeid is empty if (!_node.id) { clog(NodeTableConnect) << "Sending public key discovery Ping to" << _node.endpoint.udp << "(Advertising:" << m_node.endpoint.udp << ")"; - m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); + { + Guard l(x_pubkDiscoverPings); + m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); + } PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); @@ -103,6 +100,7 @@ shared_ptr NodeTable::addNode(Node const& _node) shared_ptr ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp))); m_nodes[_node.id] = ret; + ret->cullEndpoint(); PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); p.sign(m_secret); m_socketPointer->send(p); @@ -314,10 +312,9 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en if (!!node && !node->pending) { clog(NodeTableConnect) << "Noting active node:" << _pubk.abridged() << _endpoint.address().to_string() << ":" << _endpoint.port(); - - // update udp endpoint node->endpoint.udp.address(_endpoint.address()); node->endpoint.udp.port(_endpoint.port()); + node->cullEndpoint(); shared_ptr contested; { @@ -399,7 +396,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes bytesConstRef signedBytes(hashedBytes.cropped(Signature::size, hashedBytes.size() - Signature::size)); - // todo: verify sig via known-nodeid and MDC, or, do ping/pong auth if node/endpoint is unknown/untrusted + // todo: verify sig via known-nodeid and MDC bytesConstRef sigBytes(_packet.cropped(h256::size, Signature::size)); Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(signedBytes))); @@ -430,8 +427,7 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes dropNode(n); if (auto n = nodeEntry(it->first.first)) - if (m_nodeEventHandler && n->pending) - n->pending = false; + n->pending = false; it = m_evictions.erase(it); } @@ -441,14 +437,18 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes { if (auto n = nodeEntry(nodeid)) n->pending = false; + else if (m_pubkDiscoverPings.count(_from.address())) + { + { + Guard l(x_pubkDiscoverPings); + m_pubkDiscoverPings.erase(_from.address()); + } + if (!haveNode(nodeid)) + addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port())); + } + else + return; // unsolicited pong; don't note node as active } - else if (m_pubkDiscoverPings.count(_from.address())) - { - m_pubkDiscoverPings.erase(_from.address()); - addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port())); - } - else - return; // unsolicited pong; don't note node as active break; } diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 16bfe0fb1..e311c615a 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -131,7 +131,14 @@ void RLPXHandshake::readAck() void RLPXHandshake::error() { - clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + m_idleTimer.cancel(); + + auto connected = m_socket->isConnected(); + if (connected && !m_socket->remoteEndpoint().address().is_unspecified()) + clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + else + clog(NetConnect) << "Handshake Failed (Connection reset by peer)"; + m_socket->close(); if (m_io != nullptr) delete m_io; @@ -140,7 +147,10 @@ void RLPXHandshake::error() void RLPXHandshake::transition(boost::system::error_code _ech) { if (_ech || m_nextState == Error || m_cancel) + { + clog(NetConnect) << "Handshake Failed (I/O Error:" << _ech.message() << ")"; return error(); + } auto self(shared_from_this()); if (m_nextState == New) @@ -265,7 +275,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) { if (!_ec) { - clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)"; + if (!m_socket->remoteEndpoint().address().is_unspecified()) + clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)"; cancel(); } }); diff --git a/test/net.cpp b/test/net.cpp index 7ba2d8a12..ec1efb360 100644 --- a/test/net.cpp +++ b/test/net.cpp @@ -145,7 +145,41 @@ public: bool success = false; }; -BOOST_AUTO_TEST_CASE(badPingNodePacket) +BOOST_AUTO_TEST_CASE(isIPAddressType) +{ + string wildcard = "0.0.0.0"; + BOOST_REQUIRE(bi::address::from_string(wildcard).is_unspecified()); + + string empty = ""; + BOOST_REQUIRE_THROW(bi::address::from_string(empty).is_unspecified(), std::exception); + + string publicAddress192 = "192.169.0.0"; + BOOST_REQUIRE(isPublicAddress(publicAddress192)); + BOOST_REQUIRE(!isPrivateAddress(publicAddress192)); + BOOST_REQUIRE(!isLocalHostAddress(publicAddress192)); + + string publicAddress172 = "172.32.0.0"; + BOOST_REQUIRE(isPublicAddress(publicAddress172)); + BOOST_REQUIRE(!isPrivateAddress(publicAddress172)); + BOOST_REQUIRE(!isLocalHostAddress(publicAddress172)); + + string privateAddress192 = "192.168.1.0"; + BOOST_REQUIRE(isPrivateAddress(privateAddress192)); + BOOST_REQUIRE(!isPublicAddress(privateAddress192)); + BOOST_REQUIRE(!isLocalHostAddress(privateAddress192)); + + string privateAddress172 = "172.16.0.0"; + BOOST_REQUIRE(isPrivateAddress(privateAddress172)); + BOOST_REQUIRE(!isPublicAddress(privateAddress172)); + BOOST_REQUIRE(!isLocalHostAddress(privateAddress172)); + + string privateAddress10 = "10.0.0.0"; + BOOST_REQUIRE(isPrivateAddress(privateAddress10)); + BOOST_REQUIRE(!isPublicAddress(privateAddress10)); + BOOST_REQUIRE(!isLocalHostAddress(privateAddress10)); +} + +BOOST_AUTO_TEST_CASE(v2PingNodePacket) { // test old versino of pingNode packet w/new RLPStream s; @@ -153,7 +187,7 @@ BOOST_AUTO_TEST_CASE(badPingNodePacket) PingNode p((bi::udp::endpoint())); BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out()))); - BOOST_REQUIRE(p.version == 0); + BOOST_REQUIRE(p.version == 2); } BOOST_AUTO_TEST_CASE(test_neighbours_packet)