diff options
Diffstat (limited to 'src/net.h')
-rw-r--r-- | src/net.h | 1138 |
1 files changed, 636 insertions, 502 deletions
@@ -1,76 +1,475 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_NET_H #define BITCOIN_NET_H -#include "bloom.h" -#include "compat.h" -#include "hash.h" -#include "limitedmap.h" -#include "mruset.h" -#include "netbase.h" -#include "protocol.h" -#include "sync.h" -#include "uint256.h" -#include "util.h" - +#include <addrdb.h> +#include <addrman.h> +#include <amount.h> +#include <bloom.h> +#include <compat.h> +#include <hash.h> +#include <limitedmap.h> +#include <netaddress.h> +#include <policy/feerate.h> +#include <protocol.h> +#include <random.h> +#include <streams.h> +#include <sync.h> +#include <uint256.h> +#include <threadinterrupt.h> + +#include <atomic> #include <deque> #include <stdint.h> +#include <thread> +#include <memory> +#include <condition_variable> #ifndef WIN32 #include <arpa/inet.h> #endif -#include <boost/foreach.hpp> -#include <boost/signals2/signal.hpp> -#include <openssl/rand.h> -class CAddrMan; -class CBlockIndex; +class CScheduler; class CNode; -namespace boost { - class thread_group; -} - +/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */ +static const int PING_INTERVAL = 2 * 60; +/** Time after which to disconnect, after waiting for a ping response (or inactivity). */ +static const int TIMEOUT_INTERVAL = 20 * 60; +/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/ +static const int FEELER_INTERVAL = 120; /** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; +/** The maximum number of new addresses to accumulate before announcing. */ +static const unsigned int MAX_ADDR_TO_SEND = 1000; +/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */ +static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000; +/** Maximum length of strSubVer in `version` message */ +static const unsigned int MAX_SUBVERSION_LENGTH = 256; +/** Maximum number of automatic outgoing nodes */ +static const int MAX_OUTBOUND_CONNECTIONS = 8; +/** Maximum number of addnode outgoing nodes */ +static const int MAX_ADDNODE_CONNECTIONS = 8; +/** -listen default */ +static const bool DEFAULT_LISTEN = true; +/** -upnp default */ +#ifdef USE_UPNP +static const bool DEFAULT_UPNP = USE_UPNP; +#else +static const bool DEFAULT_UPNP = false; +#endif +/** The maximum number of entries in mapAskFor */ +static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; +/** The maximum number of entries in setAskFor (larger due to getdata latency)*/ +static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ; +/** The maximum number of peer connections to maintain. */ +static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; +/** The default for -maxuploadtarget. 0 = Unlimited */ +static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; +/** The default timeframe for -maxuploadtarget. 1 day. */ +static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24; +/** Default for blocks only*/ +static const bool DEFAULT_BLOCKSONLY = false; + +static const bool DEFAULT_FORCEDNSSEED = false; +static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; +static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; + +// NOTE: When adjusting this, update rpcnet:setban's help ("24h") +static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban + +typedef int64_t NodeId; + +struct AddedNodeInfo +{ + std::string strAddedNode; + CService resolvedAddress; + bool fConnected; + bool fInbound; +}; -inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } -inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } - -void AddOneShot(std::string strDest); -bool RecvLine(SOCKET hSocket, std::string& strLine); -bool GetMyExternalIP(CNetAddr& ipRet); -void AddressCurrentlyConnected(const CService& addr); -CNode* FindNode(const CNetAddr& ip); -CNode* FindNode(const CService& ip); -CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL); -void MapPort(bool fUseUPnP); -unsigned short GetListenPort(); -bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); -void StartNode(boost::thread_group& threadGroup); -bool StopNode(); -void SocketSendData(CNode *pnode); +class CNodeStats; +class CClientUIInterface; -typedef int NodeId; +struct CSerializedNetMsg +{ + CSerializedNetMsg() = default; + CSerializedNetMsg(CSerializedNetMsg&&) = default; + CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default; + // No copying, only moves. + CSerializedNetMsg(const CSerializedNetMsg& msg) = delete; + CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete; + + std::vector<unsigned char> data; + std::string command; +}; -// Signals for message handling -struct CNodeSignals +class NetEventsInterface; +class CConnman { - boost::signals2::signal<int ()> GetHeight; - boost::signals2::signal<bool (CNode*)> ProcessMessages; - boost::signals2::signal<bool (CNode*, bool)> SendMessages; - boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode; - boost::signals2::signal<void (NodeId)> FinalizeNode; +public: + + enum NumConnections { + CONNECTIONS_NONE = 0, + CONNECTIONS_IN = (1U << 0), + CONNECTIONS_OUT = (1U << 1), + CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), + }; + + struct Options + { + ServiceFlags nLocalServices = NODE_NONE; + int nMaxConnections = 0; + int nMaxOutbound = 0; + int nMaxAddnode = 0; + int nMaxFeeler = 0; + int nBestHeight = 0; + CClientUIInterface* uiInterface = nullptr; + NetEventsInterface* m_msgproc = nullptr; + unsigned int nSendBufferMaxSize = 0; + unsigned int nReceiveFloodSize = 0; + uint64_t nMaxOutboundTimeframe = 0; + uint64_t nMaxOutboundLimit = 0; + std::vector<std::string> vSeedNodes; + std::vector<CSubNet> vWhitelistedRange; + std::vector<CService> vBinds, vWhiteBinds; + bool m_use_addrman_outgoing = true; + std::vector<std::string> m_specified_outgoing; + std::vector<std::string> m_added_nodes; + }; + + void Init(const Options& connOptions) { + nLocalServices = connOptions.nLocalServices; + nMaxConnections = connOptions.nMaxConnections; + nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections); + nMaxAddnode = connOptions.nMaxAddnode; + nMaxFeeler = connOptions.nMaxFeeler; + nBestHeight = connOptions.nBestHeight; + clientInterface = connOptions.uiInterface; + m_msgproc = connOptions.m_msgproc; + nSendBufferMaxSize = connOptions.nSendBufferMaxSize; + nReceiveFloodSize = connOptions.nReceiveFloodSize; + { + LOCK(cs_totalBytesSent); + nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; + nMaxOutboundLimit = connOptions.nMaxOutboundLimit; + } + vWhitelistedRange = connOptions.vWhitelistedRange; + { + LOCK(cs_vAddedNodes); + vAddedNodes = connOptions.m_added_nodes; + } + } + + CConnman(uint64_t seed0, uint64_t seed1); + ~CConnman(); + bool Start(CScheduler& scheduler, const Options& options); + void Stop(); + void Interrupt(); + bool GetNetworkActive() const { return fNetworkActive; }; + void SetNetworkActive(bool active); + void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false); + bool CheckIncomingNonce(uint64_t nonce); + + bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func); + + void PushMessage(CNode* pnode, CSerializedNetMsg&& msg); + + template<typename Callable> + void ForEachNode(Callable&& func) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + func(node); + } + }; + + template<typename Callable> + void ForEachNode(Callable&& func) const + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + func(node); + } + }; + + template<typename Callable, typename CallableAfter> + void ForEachNodeThen(Callable&& pre, CallableAfter&& post) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + pre(node); + } + post(); + }; + + template<typename Callable, typename CallableAfter> + void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) { + if (NodeFullyConnected(node)) + pre(node); + } + post(); + }; + + // Addrman functions + size_t GetAddressCount() const; + void SetServices(const CService &addr, ServiceFlags nServices); + void MarkAddressGood(const CAddress& addr); + void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0); + std::vector<CAddress> GetAddresses(); + + // Denial-of-service detection/prevention + // The idea is to detect peers that are behaving + // badly and disconnect/ban them, but do it in a + // one-coding-mistake-won't-shatter-the-entire-network + // way. + // IMPORTANT: There should be nothing I can give a + // node that it will forward on that will make that + // node's peers drop it. If there is, an attacker + // can isolate a node and/or try to split the network. + // Dropping a node for sending stuff that is invalid + // now but might be valid in a later version is also + // dangerous, because it can cause a network split + // between nodes running old code and nodes running + // new code. + void Ban(const CNetAddr& netAddr, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + void Ban(const CSubNet& subNet, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + void ClearBanned(); // needed for unit testing + bool IsBanned(CNetAddr ip); + bool IsBanned(CSubNet subnet); + bool Unban(const CNetAddr &ip); + bool Unban(const CSubNet &ip); + void GetBanned(banmap_t &banmap); + void SetBanned(const banmap_t &banmap); + + // This allows temporarily exceeding nMaxOutbound, with the goal of finding + // a peer that is better than all our current peers. + void SetTryNewOutboundPeer(bool flag); + bool GetTryNewOutboundPeer(); + + // Return the number of outbound peers we have in excess of our target (eg, + // if we previously called SetTryNewOutboundPeer(true), and have since set + // to false, we may have extra peers that we wish to disconnect). This may + // return a value less than (num_outbound_connections - num_outbound_slots) + // in cases where some outbound connections are not yet fully connected, or + // not yet fully disconnected. + int GetExtraOutboundCount(); + + bool AddNode(const std::string& node); + bool RemoveAddedNode(const std::string& node); + std::vector<AddedNodeInfo> GetAddedNodeInfo(); + + size_t GetNodeCount(NumConnections num); + void GetNodeStats(std::vector<CNodeStats>& vstats); + bool DisconnectNode(const std::string& node); + bool DisconnectNode(NodeId id); + + ServiceFlags GetLocalServices() const; + + //!set the max outbound target in bytes + void SetMaxOutboundTarget(uint64_t limit); + uint64_t GetMaxOutboundTarget(); + + //!set the timeframe for the max outbound target + void SetMaxOutboundTimeframe(uint64_t timeframe); + uint64_t GetMaxOutboundTimeframe(); + + //!check if the outbound target is reached + // if param historicalBlockServingLimit is set true, the function will + // response true if the limit for serving historical blocks has been reached + bool OutboundTargetReached(bool historicalBlockServingLimit); + + //!response the bytes left in the current max outbound cycle + // in case of no limit, it will always response 0 + uint64_t GetOutboundTargetBytesLeft(); + + //!response the time in second left in the current max outbound cycle + // in case of no limit, it will always response 0 + uint64_t GetMaxOutboundTimeLeftInCycle(); + + uint64_t GetTotalBytesRecv(); + uint64_t GetTotalBytesSent(); + + void SetBestHeight(int height); + int GetBestHeight() const; + + /** Get a unique deterministic randomizer. */ + CSipHasher GetDeterministicRandomizer(uint64_t id) const; + + unsigned int GetReceiveFloodSize() const; + + void WakeMessageHandler(); +private: + struct ListenSocket { + SOCKET socket; + bool whitelisted; + + ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {} + }; + + bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); + bool Bind(const CService &addr, unsigned int flags); + bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds); + void ThreadOpenAddedConnections(); + void AddOneShot(const std::string& strDest); + void ProcessOneShot(); + void ThreadOpenConnections(std::vector<std::string> connect); + void ThreadMessageHandler(); + void AcceptConnection(const ListenSocket& hListenSocket); + void ThreadSocketHandler(); + void ThreadDNSAddressSeed(); + + uint64_t CalculateKeyedNetGroup(const CAddress& ad) const; + + CNode* FindNode(const CNetAddr& ip); + CNode* FindNode(const CSubNet& subNet); + CNode* FindNode(const std::string& addrName); + CNode* FindNode(const CService& addr); + + bool AttemptToEvictConnection(); + CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure); + bool IsWhitelistedRange(const CNetAddr &addr); + + void DeleteNode(CNode* pnode); + + NodeId GetNewNodeId(); + + size_t SocketSendData(CNode *pnode) const; + //!check is the banlist has unwritten changes + bool BannedSetIsDirty(); + //!set the "dirty" flag for the banlist + void SetBannedSetDirty(bool dirty=true); + //!clean unused entries (if bantime has expired) + void SweepBanned(); + void DumpAddresses(); + void DumpData(); + void DumpBanlist(); + + // Network stats + void RecordBytesRecv(uint64_t bytes); + void RecordBytesSent(uint64_t bytes); + + // Whether the node should be passed out in ForEach* callbacks + static bool NodeFullyConnected(const CNode* pnode); + + // Network usage totals + CCriticalSection cs_totalBytesRecv; + CCriticalSection cs_totalBytesSent; + uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv); + uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent); + + // outbound limit & stats + uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent); + + // Whitelisted ranges. Any node connecting from these is automatically + // whitelisted (as well as those connecting to whitelisted binds). + std::vector<CSubNet> vWhitelistedRange; + + unsigned int nSendBufferMaxSize; + unsigned int nReceiveFloodSize; + + std::vector<ListenSocket> vhListenSocket; + std::atomic<bool> fNetworkActive; + banmap_t setBanned; + CCriticalSection cs_setBanned; + bool setBannedIsDirty; + bool fAddressesInitialized; + CAddrMan addrman; + std::deque<std::string> vOneShots; + CCriticalSection cs_vOneShots; + std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes); + CCriticalSection cs_vAddedNodes; + std::vector<CNode*> vNodes; + std::list<CNode*> vNodesDisconnected; + mutable CCriticalSection cs_vNodes; + std::atomic<NodeId> nLastNodeId; + + /** Services this instance offers */ + ServiceFlags nLocalServices; + + std::unique_ptr<CSemaphore> semOutbound; + std::unique_ptr<CSemaphore> semAddnode; + int nMaxConnections; + int nMaxOutbound; + int nMaxAddnode; + int nMaxFeeler; + std::atomic<int> nBestHeight; + CClientUIInterface* clientInterface; + NetEventsInterface* m_msgproc; + + /** SipHasher seeds for deterministic randomness */ + const uint64_t nSeed0, nSeed1; + + /** flag for waking the message processor. */ + bool fMsgProcWake; + + std::condition_variable condMsgProc; + std::mutex mutexMsgProc; + std::atomic<bool> flagInterruptMsgProc; + + CThreadInterrupt interruptNet; + + std::thread threadDNSAddressSeed; + std::thread threadSocketHandler; + std::thread threadOpenAddedConnections; + std::thread threadOpenConnections; + std::thread threadMessageHandler; + + /** flag for deciding to connect to an extra outbound peer, + * in excess of nMaxOutbound + * This takes the place of a feeler connection */ + std::atomic_bool m_try_another_outbound_peer; + + friend struct CConnmanTest; }; +extern std::unique_ptr<CConnman> g_connman; +void Discover(); +void StartMapPort(); +void InterruptMapPort(); +void StopMapPort(); +unsigned short GetListenPort(); +bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); +struct CombinerAll +{ + typedef bool result_type; -CNodeSignals& GetNodeSignals(); + template<typename I> + bool operator()(I first, I last) const + { + while (first != last) { + if (!(*first)) return false; + ++first; + } + return true; + } +}; +/** + * Interface for message handling + */ +class NetEventsInterface +{ +public: + virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + virtual void InitializeNode(CNode* pnode) = 0; + virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0; +}; enum { @@ -78,43 +477,35 @@ enum LOCAL_IF, // address a local interface listens on LOCAL_BIND, // address explicit bound to LOCAL_UPNP, // address reported by UPnP - LOCAL_HTTP, // address reported by whatismyip.com and similar LOCAL_MANUAL, // address explicitly specified (-externalip=) LOCAL_MAX }; +bool IsPeerAddrLocalGood(CNode *pnode); +void AdvertiseLocal(CNode *pnode); void SetLimited(enum Network net, bool fLimited = true); bool IsLimited(enum Network net); bool IsLimited(const CNetAddr& addr); bool AddLocal(const CService& addr, int nScore = LOCAL_NONE); bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE); +bool RemoveLocal(const CService& addr); bool SeenLocal(const CService& addr); bool IsLocal(const CService& addr); -bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); +bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); +bool IsReachable(enum Network net); bool IsReachable(const CNetAddr &addr); -void SetReachable(enum Network net, bool fFlag = true); -CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); +CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices); extern bool fDiscover; -extern uint64_t nLocalServices; -extern uint64_t nLocalHostNonce; -extern CAddrMan addrman; -extern int nMaxConnections; - -extern std::vector<CNode*> vNodes; -extern CCriticalSection cs_vNodes; -extern std::map<CInv, CDataStream> mapRelay; -extern std::deque<std::pair<int64_t, CInv> > vRelayExpiration; -extern CCriticalSection cs_mapRelay; -extern limitedmap<CInv, int64_t> mapAlreadyAskedFor; +extern bool fListen; +extern bool fRelayTxes; -extern std::vector<std::string> vAddedNodes; -extern CCriticalSection cs_vAddedNodes; +extern limitedmap<uint256, int64_t> mapAlreadyAskedFor; -extern NodeId nLastNodeId; -extern CCriticalSection cs_nLastNodeId; +/** Subversion as sent to the P2P network in `version` messages */ +extern std::string strSubVersion; struct LocalServiceInfo { int nScore; @@ -122,33 +513,48 @@ struct LocalServiceInfo { }; extern CCriticalSection cs_mapLocalHost; -extern map<CNetAddr, LocalServiceInfo> mapLocalHost; +extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost; +typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes class CNodeStats { public: NodeId nodeid; - uint64_t nServices; + ServiceFlags nServices; + bool fRelayTxes; int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; + int64_t nTimeOffset; std::string addrName; int nVersion; std::string cleanSubVer; bool fInbound; + bool m_manual_connection; int nStartingHeight; uint64_t nSendBytes; + mapMsgCmdSize mapSendBytesPerMsgCmd; uint64_t nRecvBytes; - bool fSyncNode; + mapMsgCmdSize mapRecvBytesPerMsgCmd; + bool fWhitelisted; double dPingTime; double dPingWait; + double dMinPing; + // Our address, as reported by the peer std::string addrLocal; + // Address of this peer + CAddress addr; + // Bind address of our side of the connection + CAddress addrBind; }; class CNetMessage { +private: + mutable CHash256 hasher; + mutable uint256 data_hash; public: bool in_data; // parsing header (false) or data (true) @@ -159,11 +565,14 @@ public: CDataStream vRecv; // received message data unsigned int nDataPos; - CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) { + int64_t nTime; // time (in microseconds) of message receipt. + + CNetMessage(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) { hdrbuf.resize(24); in_data = false; nHdrPos = 0; nDataPos = 0; + nTime = 0; } bool complete() const @@ -173,6 +582,8 @@ public: return (hdr.nMessageSize == nDataPos); } + const uint256& GetMessageHash() const; + void SetVersion(int nVersionIn) { hdrbuf.SetVersion(nVersionIn); @@ -184,196 +595,185 @@ public: }; - - - /** Information about a peer */ class CNode { + friend class CConnman; public: // socket - uint64_t nServices; + std::atomic<ServiceFlags> nServices; SOCKET hSocket; - CDataStream ssSend; size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent uint64_t nSendBytes; - std::deque<CSerializeData> vSendMsg; + std::deque<std::vector<unsigned char>> vSendMsg; CCriticalSection cs_vSend; + CCriticalSection cs_hSocket; + CCriticalSection cs_vRecv; + + CCriticalSection cs_vProcessMsg; + std::list<CNetMessage> vProcessMsg; + size_t nProcessQueueSize; + + CCriticalSection cs_sendProcessing; std::deque<CInv> vRecvGetData; - std::deque<CNetMessage> vRecvMsg; - CCriticalSection cs_vRecvMsg; uint64_t nRecvBytes; - int nRecvVersion; - - int64_t nLastSend; - int64_t nLastRecv; - int64_t nLastSendEmpty; - int64_t nTimeConnected; - CAddress addr; - std::string addrName; - CService addrLocal; - int nVersion; + std::atomic<int> nRecvVersion; + + std::atomic<int64_t> nLastSend; + std::atomic<int64_t> nLastRecv; + const int64_t nTimeConnected; + std::atomic<int64_t> nTimeOffset; + // Address of this peer + const CAddress addr; + // Bind address of our side of the connection + const CAddress addrBind; + std::atomic<int> nVersion; // strSubVer is whatever byte array we read from the wire. However, this field is intended // to be printed out, displayed to humans in various forms and so on. So we sanitize it and // store the sanitized version in cleanSubVer. The original should be used when dealing with // the network or wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; + CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer + bool fWhitelisted; // This peer can bypass DoS banning. + bool fFeeler; // If true this node is being used as a short lived feeler. bool fOneShot; + bool m_manual_connection; bool fClient; - bool fInbound; - bool fNetworkNode; - bool fSuccessfullyConnected; - bool fDisconnect; + const bool fInbound; + std::atomic_bool fSuccessfullyConnected; + std::atomic_bool fDisconnect; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message - // b) the peer may tell us in their version message that we should not relay tx invs - // until they have initialized their bloom filter. - bool fRelayTxes; + // b) the peer may tell us in its version message that we should not relay tx invs + // unless it loads a bloom filter. + bool fRelayTxes; //protected by cs_filter + bool fSentAddr; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; - CBloomFilter* pfilter; - int nRefCount; - NodeId id; -protected: + std::unique_ptr<CBloomFilter> pfilter; + std::atomic<int> nRefCount; - // Denial-of-service detection/prevention - // Key is IP address, value is banned-until-time - static std::map<CNetAddr, int64_t> setBanned; - static CCriticalSection cs_setBanned; + const uint64_t nKeyedNetGroup; + std::atomic_bool fPauseRecv; + std::atomic_bool fPauseSend; +protected: - // Basic fuzz-testing - void Fuzz(int nChance); // modifies ssSend + mapMsgCmdSize mapSendBytesPerMsgCmd; + mapMsgCmdSize mapRecvBytesPerMsgCmd; public: uint256 hashContinue; - CBlockIndex* pindexLastGetBlocksBegin; - uint256 hashLastGetBlocksEnd; - int nStartingHeight; - bool fStartSync; + std::atomic<int> nStartingHeight; // flood relay std::vector<CAddress> vAddrToSend; - std::set<CAddress> setAddrKnown; + CRollingBloomFilter addrKnown; bool fGetAddr; std::set<uint256> setKnown; + int64_t nNextAddrSend; + int64_t nNextLocalAddrSend; // inventory based relay - mruset<CInv> setInventoryKnown; - std::vector<CInv> vInventoryToSend; + CRollingBloomFilter filterInventoryKnown; + // Set of transaction ids we still have to announce. + // They are sorted by the mempool before relay, so the order is not important. + std::set<uint256> setInventoryTxToSend; + // List of block ids we still have announce. + // There is no final sorting before sending, as they are always sent immediately + // and in the order requested. + std::vector<uint256> vInventoryBlockToSend; CCriticalSection cs_inventory; + std::set<uint256> setAskFor; std::multimap<int64_t, CInv> mapAskFor; + int64_t nNextInvSend; + // Used for headers announcements - unfiltered blocks to relay + // Also protected by cs_inventory + std::vector<uint256> vBlockHashesToAnnounce; + // Used for BIP35 mempool sending, also protected by cs_inventory + bool fSendMempool; + + // Last time a "MEMPOOL" request was serviced. + std::atomic<int64_t> timeLastMempoolReq; + + // Block and TXN accept times + std::atomic<int64_t> nLastBlockTime; + std::atomic<int64_t> nLastTXTime; + + // Ping time measurement: + // The pong reply we're expecting, or 0 if no pong expected. + std::atomic<uint64_t> nPingNonceSent; + // Time (in usec) the last ping was sent, or 0 if no ping was ever sent. + std::atomic<int64_t> nPingUsecStart; + // Last measured round-trip time. + std::atomic<int64_t> nPingUsecTime; + // Best measured round-trip time. + std::atomic<int64_t> nMinPingUsecTime; + // Whether a ping is requested. + std::atomic<bool> fPingQueued; + // Minimum fee rate with which to filter inv's to this node + CAmount minFeeFilter; + CCriticalSection cs_feeFilter; + CAmount lastSentFeeFilter; + int64_t nextSendTimeFeeFilter; + + CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false); + ~CNode(); + CNode(const CNode&) = delete; + CNode& operator=(const CNode&) = delete; - // Ping time measurement - uint64_t nPingNonceSent; - int64_t nPingUsecStart; - int64_t nPingUsecTime; - bool fPingQueued; - - CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION) - { - nServices = 0; - hSocket = hSocketIn; - nRecvVersion = INIT_PROTO_VERSION; - nLastSend = 0; - nLastRecv = 0; - nSendBytes = 0; - nRecvBytes = 0; - nLastSendEmpty = GetTime(); - nTimeConnected = GetTime(); - addr = addrIn; - addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; - nVersion = 0; - strSubVer = ""; - fOneShot = false; - fClient = false; // set by version message - fInbound = fInboundIn; - fNetworkNode = false; - fSuccessfullyConnected = false; - fDisconnect = false; - nRefCount = 0; - nSendSize = 0; - nSendOffset = 0; - hashContinue = 0; - pindexLastGetBlocksBegin = 0; - hashLastGetBlocksEnd = 0; - nStartingHeight = -1; - fStartSync = false; - fGetAddr = false; - fRelayTxes = false; - setInventoryKnown.max_size(SendBufferSize() / 1000); - pfilter = new CBloomFilter(); - nPingNonceSent = 0; - nPingUsecStart = 0; - nPingUsecTime = 0; - fPingQueued = false; - - { - LOCK(cs_nLastNodeId); - id = nLastNodeId++; - } +private: + const NodeId id; + const uint64_t nLocalHostNonce; + // Services offered to this peer + const ServiceFlags nLocalServices; + const int nMyStartingHeight; + int nSendVersion; + std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread + + mutable CCriticalSection cs_addrName; + std::string addrName; - // Be shy and don't send version until we hear - if (hSocket != INVALID_SOCKET && !fInbound) - PushVersion(); + // Our address, as reported by the peer + CService addrLocal; + mutable CCriticalSection cs_addrLocal; +public: - GetNodeSignals().InitializeNode(GetId(), this); + NodeId GetId() const { + return id; } - ~CNode() - { - if (hSocket != INVALID_SOCKET) - { - closesocket(hSocket); - hSocket = INVALID_SOCKET; - } - if (pfilter) - delete pfilter; - GetNodeSignals().FinalizeNode(GetId()); + uint64_t GetLocalNonce() const { + return nLocalHostNonce; } -private: - // Network usage totals - static CCriticalSection cs_totalBytesRecv; - static CCriticalSection cs_totalBytesSent; - static uint64_t nTotalBytesRecv; - static uint64_t nTotalBytesSent; - - CNode(const CNode&); - void operator=(const CNode&); - -public: - - NodeId GetId() const { - return id; + int GetMyStartingHeight() const { + return nMyStartingHeight; } - int GetRefCount() + int GetRefCount() const { assert(nRefCount >= 0); return nRefCount; } - // requires LOCK(cs_vRecvMsg) - unsigned int GetTotalRecvSize() - { - unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) - total += msg.vRecv.size() + 24; - return total; - } - - // requires LOCK(cs_vRecvMsg) - bool ReceiveMsgBytes(const char *pch, unsigned int nBytes); + bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete); - // requires LOCK(cs_vRecvMsg) void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; - BOOST_FOREACH(CNetMessage &msg, vRecvMsg) - msg.SetVersion(nVersionIn); } + int GetRecvVersion() const + { + return nRecvVersion; + } + void SetSendVersion(int nVersionIn); + int GetSendVersion() const; + + CService GetAddrLocal() const; + //! May not be called more than once + void SetAddrLocal(const CService& addrLocalIn); CNode* AddRef() { @@ -388,18 +788,23 @@ public: - void AddAddressKnown(const CAddress& addr) + void AddAddressKnown(const CAddress& _addr) { - setAddrKnown.insert(addr); + addrKnown.insert(_addr.GetKey()); } - void PushAddress(const CAddress& addr) + void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand) { // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. - if (addr.IsValid() && !setAddrKnown.count(addr)) - vAddrToSend.push_back(addr); + if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) { + if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { + vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr; + } else { + vAddrToSend.push_back(_addr); + } + } } @@ -407,320 +812,49 @@ public: { { LOCK(cs_inventory); - setInventoryKnown.insert(inv); + filterInventoryKnown.insert(inv.hash); } } void PushInventory(const CInv& inv) { - { - LOCK(cs_inventory); - if (!setInventoryKnown.count(inv)) - vInventoryToSend.push_back(inv); + LOCK(cs_inventory); + if (inv.type == MSG_TX) { + if (!filterInventoryKnown.contains(inv.hash)) { + setInventoryTxToSend.insert(inv.hash); + } + } else if (inv.type == MSG_BLOCK) { + vInventoryBlockToSend.push_back(inv.hash); } } - void AskFor(const CInv& inv) + void PushBlockHash(const uint256 &hash) { - // We're using mapAskFor as a priority queue, - // the key is the earliest time the request can be sent - int64_t nRequestTime; - limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv); - if (it != mapAlreadyAskedFor.end()) - nRequestTime = it->second; - else - nRequestTime = 0; - LogPrint("net", "askfor %s %d (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); - - // Make sure not to reuse time indexes to keep things in the same order - int64_t nNow = GetTimeMicros() - 1000000; - static int64_t nLastTime; - ++nLastTime; - nNow = std::max(nNow, nLastTime); - nLastTime = nNow; - - // Each retry is 2 minutes after the last - nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); - if (it != mapAlreadyAskedFor.end()) - mapAlreadyAskedFor.update(it, nRequestTime); - else - mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); - mapAskFor.insert(std::make_pair(nRequestTime, inv)); + LOCK(cs_inventory); + vBlockHashesToAnnounce.push_back(hash); } + void AskFor(const CInv& inv); - - // TODO: Document the postcondition of this function. Is cs_vSend locked? - void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) - { - ENTER_CRITICAL_SECTION(cs_vSend); - assert(ssSend.size() == 0); - ssSend << CMessageHeader(pszCommand, 0); - LogPrint("net", "sending: %s ", pszCommand); - } - - // TODO: Document the precondition of this function. Is cs_vSend locked? - void AbortMessage() UNLOCK_FUNCTION(cs_vSend) - { - ssSend.clear(); - - LEAVE_CRITICAL_SECTION(cs_vSend); - - LogPrint("net", "(aborted)\n"); - } - - // TODO: Document the precondition of this function. Is cs_vSend locked? - void EndMessage() UNLOCK_FUNCTION(cs_vSend) - { - // The -*messagestest options are intentionally not documented in the help message, - // since they are only used during development to debug the networking code and are - // not intended for end-users. - if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) - { - LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); - AbortMessage(); - return; - } - if (mapArgs.count("-fuzzmessagestest")) - Fuzz(GetArg("-fuzzmessagestest", 10)); - - if (ssSend.size() == 0) - return; - - // Set the size - unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; - memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); - - // Set the checksum - uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); - memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); - - LogPrint("net", "(%d bytes)\n", nSize); - - std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); - ssSend.GetAndClear(*it); - nSendSize += (*it).size(); - - // If write queue empty, attempt "optimistic write" - if (it == vSendMsg.begin()) - SocketSendData(this); - - LEAVE_CRITICAL_SECTION(cs_vSend); - } - - void PushVersion(); - - - void PushMessage(const char* pszCommand) - { - try - { - BeginMessage(pszCommand); - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1> - void PushMessage(const char* pszCommand, const T1& a1) - { - try - { - BeginMessage(pszCommand); - ssSend << a1; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3, typename T4> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3, typename T4, typename T5> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> - void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9) - { - try - { - BeginMessage(pszCommand); - ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; - EndMessage(); - } - catch (...) - { - AbortMessage(); - throw; - } - } - - bool IsSubscribed(unsigned int nChannel); - void Subscribe(unsigned int nChannel, unsigned int nHops=0); - void CancelSubscribe(unsigned int nChannel); void CloseSocketDisconnect(); - void Cleanup(); - - // Denial-of-service detection/prevention - // The idea is to detect peers that are behaving - // badly and disconnect/ban them, but do it in a - // one-coding-mistake-won't-shatter-the-entire-network - // way. - // IMPORTANT: There should be nothing I can give a - // node that it will forward on that will make that - // node's peers drop it. If there is, an attacker - // can isolate a node and/or try to split the network. - // Dropping a node for sending stuff that is invalid - // now but might be valid in a later version is also - // dangerous, because it can cause a network split - // between nodes running old code and nodes running - // new code. - static void ClearBanned(); // needed for unit testing - static bool IsBanned(CNetAddr ip); - static bool Ban(const CNetAddr &ip); void copyStats(CNodeStats &stats); - // Network stats - static void RecordBytesRecv(uint64_t bytes); - static void RecordBytesSent(uint64_t bytes); + ServiceFlags GetLocalServices() const + { + return nLocalServices; + } - static uint64_t GetTotalBytesRecv(); - static uint64_t GetTotalBytesSent(); + std::string GetAddrName() const; + //! Sets the addrName only if it was not previously set + void MaybeSetAddrName(const std::string& addrNameIn); }; -class CTransaction; -void RelayTransaction(const CTransaction& tx, const uint256& hash); -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); -/** Access to the (IP) address database (peers.dat) */ -class CAddrDB -{ -private: - boost::filesystem::path pathAddr; -public: - CAddrDB(); - bool Write(const CAddrMan& addr); - bool Read(CAddrMan& addr); -}; -#endif +/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */ +int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); + +#endif // BITCOIN_NET_H |