Browse Source

Merge pull request #1429 from ethereum/p2p

p2p: forge Ping ipAddress from UDP packet headers when not public
cl-refactor
Gav Wood 10 years ago
parent
commit
67f5ec66e3
  1. 26
      libp2p/Common.cpp
  2. 9
      libp2p/Common.h
  3. 8
      libp2p/Host.cpp
  4. 32
      libp2p/NodeTable.cpp
  5. 11
      libp2p/RLPxHandshake.cpp
  6. 38
      test/net.cpp

26
libp2p/Common.cpp

@ -26,6 +26,16 @@ using namespace dev::p2p;
const unsigned dev::p2p::c_protocolVersion = 3; 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 // Helper function to determine if an address falls within one of the reserved ranges
// For V4: // For V4:
// Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" // 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; 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 // Helper function to determine if an address is localhost
bool p2p::isLocalHostAddress(bi::address const& _addressToCheck) 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(); 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) std::string p2p::reasonOf(DisconnectReason _r)
{ {
switch (_r) switch (_r)
@ -89,3 +109,9 @@ std::string p2p::reasonOf(DisconnectReason _r)
default: return "Unknown reason."; default: return "Unknown reason.";
} }
} }
void Node::cullEndpoint()
{
if (!isPublicAddress(endpoint.tcp.address()) && isPublicAddress(endpoint.udp.address()))
endpoint.tcp.address(endpoint.udp.address());
}

9
libp2p/Common.h

@ -54,7 +54,11 @@ extern const unsigned c_protocolVersion;
using NodeId = h512; using NodeId = h512;
bool isPrivateAddress(bi::address const& _addressToCheck); bool isPrivateAddress(bi::address const& _addressToCheck);
bool isPrivateAddress(std::string const& _addressToCheck);
bool isLocalHostAddress(bi::address 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 UPnP;
class Capability; class Capability;
@ -62,6 +66,8 @@ class Host;
class Session; class Session;
struct NetworkStartRequired: virtual dev::Exception {}; 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 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; }; 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 NodeId const& address() const { return id; }
virtual Public const& publicKey() 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; NodeId id;
/// Endpoints by which we expect to reach node. /// Endpoints by which we expect to reach node.

8
libp2p/Host.cpp

@ -592,13 +592,13 @@ void Host::startedWorking()
m_run = true; m_run = true;
} }
// try to open acceptor (todo: ipv6) // start capability threads (ready for incoming connections)
m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort);
// start capability threads
for (auto const& h: m_capabilities) for (auto const& h: m_capabilities)
h.second->onStarting(); 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 // determine public IP, but only if we're able to listen for connections
// todo: GUI when listen is unavailable in UI // todo: GUI when listen is unavailable in UI
if (m_listenPort) if (m_listenPort)

32
libp2p/NodeTable.cpp

@ -70,25 +70,22 @@ shared_ptr<NodeEntry> NodeTable::addNode(Public const& _pubk, bi::udp::endpoint
shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node) shared_ptr<NodeEntry> 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; clog(NodeTableWarn) << "addNode Failed. Invalid UDP address 0.0.0.0 for" << _node.id.abridged();
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();
return move(shared_ptr<NodeEntry>()); return move(shared_ptr<NodeEntry>());
} }
// ping address if nodeid is empty // ping address to recover nodeid if nodeid is empty
if (!_node.id) if (!_node.id)
{ {
clog(NodeTableConnect) << "Sending public key discovery Ping to" << _node.endpoint.udp << "(Advertising:" << m_node.endpoint.udp << ")"; clog(NodeTableConnect) << "Sending public key discovery Ping to" << _node.endpoint.udp << "(Advertising:" << m_node.endpoint.udp << ")";
{
Guard l(x_pubkDiscoverPings);
m_pubkDiscoverPings[_node.endpoint.udp.address()] = std::chrono::steady_clock::now(); 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()); PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port());
p.sign(m_secret); p.sign(m_secret);
m_socketPointer->send(p); m_socketPointer->send(p);
@ -103,6 +100,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp))); shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, NodeIPEndpoint(_node.endpoint.udp, _node.endpoint.tcp)));
m_nodes[_node.id] = ret; m_nodes[_node.id] = ret;
ret->cullEndpoint();
PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port()); PingNode p(_node.endpoint.udp, m_node.endpoint.udp.address().to_string(), m_node.endpoint.udp.port());
p.sign(m_secret); p.sign(m_secret);
m_socketPointer->send(p); m_socketPointer->send(p);
@ -314,10 +312,9 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
if (!!node && !node->pending) if (!!node && !node->pending)
{ {
clog(NodeTableConnect) << "Noting active node:" << _pubk.abridged() << _endpoint.address().to_string() << ":" << _endpoint.port(); 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.address(_endpoint.address());
node->endpoint.udp.port(_endpoint.port()); node->endpoint.udp.port(_endpoint.port());
node->cullEndpoint();
shared_ptr<NodeEntry> contested; shared_ptr<NodeEntry> 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)); 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)); bytesConstRef sigBytes(_packet.cropped(h256::size, Signature::size));
Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(signedBytes))); Public nodeid(dev::recover(*(Signature const*)sigBytes.data(), sha3(signedBytes)));
@ -430,7 +427,6 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
dropNode(n); dropNode(n);
if (auto n = nodeEntry(it->first.first)) if (auto n = nodeEntry(it->first.first))
if (m_nodeEventHandler && n->pending)
n->pending = false; n->pending = false;
it = m_evictions.erase(it); it = m_evictions.erase(it);
@ -441,14 +437,18 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
{ {
if (auto n = nodeEntry(nodeid)) if (auto n = nodeEntry(nodeid))
n->pending = false; n->pending = false;
}
else if (m_pubkDiscoverPings.count(_from.address())) else if (m_pubkDiscoverPings.count(_from.address()))
{ {
{
Guard l(x_pubkDiscoverPings);
m_pubkDiscoverPings.erase(_from.address()); m_pubkDiscoverPings.erase(_from.address());
}
if (!haveNode(nodeid))
addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port())); addNode(nodeid, _from, bi::tcp::endpoint(_from.address(), _from.port()));
} }
else else
return; // unsolicited pong; don't note node as active return; // unsolicited pong; don't note node as active
}
break; break;
} }

11
libp2p/RLPxHandshake.cpp

@ -131,7 +131,14 @@ void RLPXHandshake::readAck()
void RLPXHandshake::error() void RLPXHandshake::error()
{ {
m_idleTimer.cancel();
auto connected = m_socket->isConnected();
if (connected && !m_socket->remoteEndpoint().address().is_unspecified())
clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)";
else
clog(NetConnect) << "Handshake Failed (Connection reset by peer)";
m_socket->close(); m_socket->close();
if (m_io != nullptr) if (m_io != nullptr)
delete m_io; delete m_io;
@ -140,7 +147,10 @@ void RLPXHandshake::error()
void RLPXHandshake::transition(boost::system::error_code _ech) void RLPXHandshake::transition(boost::system::error_code _ech)
{ {
if (_ech || m_nextState == Error || m_cancel) if (_ech || m_nextState == Error || m_cancel)
{
clog(NetConnect) << "Handshake Failed (I/O Error:" << _ech.message() << ")";
return error(); return error();
}
auto self(shared_from_this()); auto self(shared_from_this());
if (m_nextState == New) if (m_nextState == New)
@ -265,6 +275,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
{ {
if (!_ec) if (!_ec)
{ {
if (!m_socket->remoteEndpoint().address().is_unspecified())
clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)"; clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)";
cancel(); cancel();
} }

38
test/net.cpp

@ -145,7 +145,41 @@ public:
bool success = false; 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 // test old versino of pingNode packet w/new
RLPStream s; RLPStream s;
@ -153,7 +187,7 @@ BOOST_AUTO_TEST_CASE(badPingNodePacket)
PingNode p((bi::udp::endpoint())); PingNode p((bi::udp::endpoint()));
BOOST_REQUIRE_NO_THROW(p = PingNode::fromBytesConstRef(bi::udp::endpoint(), bytesConstRef(&s.out()))); 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) BOOST_AUTO_TEST_CASE(test_neighbours_packet)

Loading…
Cancel
Save