/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see .
*/
/** @file Common.h
* @author Gav Wood
* @author Alex Leverington
* @date 2014
*
* Miscellanea required for the Host/Session/NodeTable classes.
*/
#pragma once
#include
#include
#include
// Make sure boost/asio.hpp is included before windows.h.
#include
#include
#include
#include
#include
#include
#include
#include
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
class RLP;
class RLPStream;
namespace p2p
{
/// Peer network protocol version.
extern const unsigned c_protocolVersion;
extern const unsigned c_defaultIPPort;
class NodeIPEndpoint;
class Node;
extern const NodeIPEndpoint UnspecifiedNodeIPEndpoint;
extern const Node UnspecifiedNode;
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;
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(); static const int verbosity = 0; };
struct NetNote: public LogChannel { static const char* name(); static const int verbosity = 2; };
struct NetImpolite: public LogChannel { static const char* name(); static const int verbosity = 3; };
struct NetMessageSummary: public LogChannel { static const char* name(); static const int verbosity = 4; };
struct NetConnect: public LogChannel { static const char* name(); static const int verbosity = 10; };
struct NetMessageDetail: public LogChannel { static const char* name(); static const int verbosity = 5; };
struct NetTriviaSummary: public LogChannel { static const char* name(); static const int verbosity = 10; };
struct NetTriviaDetail: public LogChannel { static const char* name(); static const int verbosity = 11; };
struct NetAllDetail: public LogChannel { static const char* name(); static const int verbosity = 13; };
struct NetRight: public LogChannel { static const char* name(); static const int verbosity = 14; };
struct NetLeft: public LogChannel { static const char* name(); static const int verbosity = 15; };
struct NetP2PWarn: public LogChannel { static const char* name(); static const int verbosity = 2; };
struct NetP2PNote: public LogChannel { static const char* name(); static const int verbosity = 6; };
struct NetP2PConnect: public LogChannel { static const char* name(); static const int verbosity = 10; };
enum PacketType
{
HelloPacket = 0,
DisconnectPacket,
PingPacket,
PongPacket,
GetPeersPacket,
PeersPacket,
UserPacket = 0x10
};
enum DisconnectReason
{
DisconnectRequested = 0,
TCPError,
BadProtocol,
UselessPeer,
TooManyPeers,
DuplicatePeer,
IncompatibleProtocol,
NullIdentity,
ClientQuit,
UnexpectedIdentity,
LocalIdentity,
PingTimeout,
UserReason = 0x10,
NoDisconnect = 0xffff
};
inline bool isPermanentProblem(DisconnectReason _r)
{
switch (_r)
{
case DuplicatePeer:
case IncompatibleProtocol:
case NullIdentity:
case UnexpectedIdentity:
case LocalIdentity:
return true;
default:
return false;
}
}
/// @returns the string form of the given disconnection reason.
std::string reasonOf(DisconnectReason _r);
using CapDesc = std::pair;
using CapDescSet = std::set;
using CapDescs = std::vector;
/*
* Used by Host to pass negotiated information about a connection to a
* new Peer Session; PeerSessionInfo is then maintained by Session and can
* be queried for point-in-time status information via Host.
*/
struct PeerSessionInfo
{
NodeID const id;
std::string const clientVersion;
std::string const host;
unsigned short const port;
std::chrono::steady_clock::duration lastPing;
std::set const caps;
unsigned socketId;
std::map notes;
unsigned const protocolVersion;
};
using PeerSessionInfos = std::vector;
enum class PeerType
{
Optional,
Required
};
/**
* @brief IPv4,UDP/TCP endpoints.
*/
class NodeIPEndpoint
{
public:
enum RLPAppend
{
StreamList,
StreamInline
};
/// Setting true causes isAllowed to return true for all addresses. (Used by test fixtures)
static bool test_allowLocal;
NodeIPEndpoint() = default;
NodeIPEndpoint(bi::address _addr, uint16_t _udp, uint16_t _tcp): address(_addr), udpPort(_udp), tcpPort(_tcp) {}
NodeIPEndpoint(RLP const& _r) { interpretRLP(_r); }
operator bi::udp::endpoint() const { return bi::udp::endpoint(address, udpPort); }
operator bi::tcp::endpoint() const { return bi::tcp::endpoint(address, tcpPort); }
operator bool() const { return !address.is_unspecified() && udpPort > 0 && tcpPort > 0; }
bool isAllowed() const { return NodeIPEndpoint::test_allowLocal ? !address.is_unspecified() : isPublicAddress(address); }
void streamRLP(RLPStream& _s, RLPAppend _append = StreamList) const;
void interpretRLP(RLP const& _r);
// TODO: make private, give accessors and rename m_...
bi::address address;
uint16_t udpPort = 0;
uint16_t tcpPort = 0;
};
struct NodeSpec
{
NodeSpec() {}
/// Accepts user-readable strings of the form (enode://pubkey@)host({:port,:tcpport.udpport})
NodeSpec(std::string const& _user);
NodeSpec(std::string const& _addr, uint16_t _port, int _udpPort = -1):
m_address(_addr),
m_tcpPort(_port),
m_udpPort(_udpPort == -1 ? _port : (uint16_t)_udpPort)
{}
NodeID id() const { return m_id; }
NodeIPEndpoint nodeIPEndpoint() const;
std::string enode() const;
private:
std::string m_address;
uint16_t m_tcpPort = 0;
uint16_t m_udpPort = 0;
NodeID m_id;
};
class Node
{
public:
Node() = default;
Node(Node const&) = default;
Node(Public _publicKey, NodeIPEndpoint const& _ip, PeerType _peerType = PeerType::Optional): id(_publicKey), endpoint(_ip), peerType(_peerType) {}
Node(NodeSpec const& _s, PeerType _peerType = PeerType::Optional);
virtual NodeID const& address() const { return id; }
virtual Public const& publicKey() const { return id; }
virtual operator bool() const { return (bool)id; }
// TODO: make private, give accessors and rename m_...
NodeID id;
/// Endpoints by which we expect to reach node.
// TODO: make private, give accessors and rename m_...
NodeIPEndpoint endpoint;
// TODO: p2p implement
PeerType peerType = PeerType::Optional;
};
class DeadlineOps
{
class DeadlineOp
{
public:
DeadlineOp(ba::io_service& _io, unsigned _msInFuture, std::function const& _f): m_timer(new ba::deadline_timer(_io)) { m_timer->expires_from_now(boost::posix_time::milliseconds(_msInFuture)); m_timer->async_wait(_f); }
~DeadlineOp() { if (m_timer) m_timer->cancel(); }
DeadlineOp(DeadlineOp&& _s): m_timer(_s.m_timer.release()) {}
DeadlineOp& operator=(DeadlineOp&& _s)
{
assert(&_s != this);
m_timer.reset(_s.m_timer.release());
return *this;
}
bool expired() { Guard l(x_timer); return m_timer->expires_from_now().total_nanoseconds() <= 0; }
void wait() { Guard l(x_timer); m_timer->wait(); }
private:
std::unique_ptr m_timer;
Mutex x_timer;
};
public:
DeadlineOps(ba::io_service& _io, unsigned _reapIntervalMs = 100): m_io(_io), m_reapIntervalMs(_reapIntervalMs), m_stopped(false) { reap(); }
~DeadlineOps() { stop(); }
void schedule(unsigned _msInFuture, std::function const& _f) { if (m_stopped) return; DEV_GUARDED(x_timers) m_timers.emplace_back(m_io, _msInFuture, _f); }
void stop() { m_stopped = true; DEV_GUARDED(x_timers) m_timers.clear(); }
bool isStopped() const { return m_stopped; }
protected:
void reap();
private:
ba::io_service& m_io;
unsigned m_reapIntervalMs;
std::vector m_timers;
Mutex x_timers;
std::atomic m_stopped;
};
}
/// Simple stream output for a NodeIPEndpoint.
std::ostream& operator<<(std::ostream& _out, dev::p2p::NodeIPEndpoint const& _ep);
}
/// std::hash for asio::adress
namespace std
{
template <> struct hash
{
size_t operator()(bi::address const& _a) const
{
if (_a.is_v4())
return std::hash()(_a.to_v4().to_ulong());
if (_a.is_v6())
{
auto const& range = _a.to_v6().to_bytes();
return boost::hash_range(range.begin(), range.end());
}
if (_a.is_unspecified())
return static_cast(0x3487194039229152ull); // Chosen by fair dice roll, guaranteed to be random
return std::hash()(_a.to_string());
}
};
}