diff options
Diffstat (limited to 'src/net.cpp')
-rw-r--r-- | src/net.cpp | 908 |
1 files changed, 453 insertions, 455 deletions
diff --git a/src/net.cpp b/src/net.cpp index 4cbc43e4d8..cce06f2d64 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -17,6 +17,7 @@ #include "crypto/sha256.h" #include "hash.h" #include "primitives/transaction.h" +#include "netbase.h" #include "scheduler.h" #include "ui_interface.h" #include "utilstrencodings.h" @@ -42,6 +43,9 @@ // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 +// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization. +#define FEELER_SLEEP_WINDOW 1 + #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif @@ -57,62 +61,28 @@ #endif #endif - -namespace { - const int MAX_OUTBOUND_CONNECTIONS = 8; - - struct ListenSocket { - SOCKET socket; - bool whitelisted; - - ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {} - }; -} - const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; -/** Services this node implementation cares about */ -ServiceFlags nRelevantServices = NODE_NETWORK; - +static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8] // // Global state variables // bool fDiscover = true; bool fListen = true; -ServiceFlags nLocalServices = NODE_NETWORK; bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; std::map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; -uint64_t nLocalHostNonce = 0; -static std::vector<ListenSocket> vhListenSocket; -CAddrMan addrman; -int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; -bool fAddressesInitialized = false; std::string strSubVersion; -std::vector<CNode*> vNodes; -CCriticalSection cs_vNodes; limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); -static std::deque<std::string> vOneShots; -CCriticalSection cs_vOneShots; - -std::vector<std::string> vAddedNodes; -CCriticalSection cs_vAddedNodes; - -NodeId nLastNodeId = 0; -CCriticalSection cs_nLastNodeId; - -static CSemaphore *semOutbound = NULL; -boost::condition_variable messageHandlerCondition; - // Signals for message handling static CNodeSignals g_signals; CNodeSignals& GetNodeSignals() { return g_signals; } -void AddOneShot(const std::string& strDest) +void CConnman::AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); @@ -173,9 +143,9 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. -CAddress GetLocalAddress(const CNetAddr *paddrPeer) +CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) { - CAddress ret(CService("0.0.0.0",GetListenPort()), NODE_NONE); + CAddress ret(CService(CNetAddr(),GetListenPort()), NODE_NONE); CService addr; if (GetLocal(addr, paddrPeer)) { @@ -205,7 +175,7 @@ void AdvertiseLocal(CNode *pnode) { if (fListen && pnode->fSuccessfullyConnected) { - CAddress addrLocal = GetLocalAddress(&pnode->addr); + CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. @@ -216,7 +186,7 @@ void AdvertiseLocal(CNode *pnode) } if (addrLocal.IsRoutable()) { - LogPrintf("AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); + LogPrint("net", "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); pnode->PushAddress(addrLocal); } } @@ -316,23 +286,8 @@ bool IsReachable(const CNetAddr& addr) return IsReachable(net); } -void AddressCurrentlyConnected(const CService& addr) -{ - addrman.Connected(addr); -} - - -uint64_t CNode::nTotalBytesRecv = 0; -uint64_t CNode::nTotalBytesSent = 0; -CCriticalSection CNode::cs_totalBytesRecv; -CCriticalSection CNode::cs_totalBytesSent; - -uint64_t CNode::nMaxOutboundLimit = 0; -uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0; -uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day -uint64_t CNode::nMaxOutboundCycleStartTime = 0; -CNode* FindNode(const CNetAddr& ip) +CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -341,7 +296,7 @@ CNode* FindNode(const CNetAddr& ip) return NULL; } -CNode* FindNode(const CSubNet& subNet) +CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -350,7 +305,7 @@ CNode* FindNode(const CSubNet& subNet) return NULL; } -CNode* FindNode(const std::string& addrName) +CNode* CConnman::FindNode(const std::string& addrName) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -359,7 +314,7 @@ CNode* FindNode(const std::string& addrName) return NULL; } -CNode* FindNode(const CService& addr) +CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -368,17 +323,17 @@ CNode* FindNode(const CService& addr) return NULL; } -//TODO: This is used in only one place in main, and should be removed -CNode* FindNode(const NodeId nodeid) +bool CConnman::CheckIncomingNonce(uint64_t nonce) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->GetId() == nodeid) - return (pnode); - return NULL; + BOOST_FOREACH(CNode* pnode, vNodes) { + if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce) + return false; + } + return true; } -CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) +CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) { if (pszDest == NULL) { if (IsLocal(addrConnect)) @@ -433,7 +388,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure addrman.Attempt(addrConnect, fCountFailure); // Add node - CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); + CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), pszDest ? pszDest : "", false); + GetNodeSignals().InitializeNode(pnode->GetId(), pnode); pnode->AddRef(); { @@ -454,21 +410,21 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure return NULL; } -static void DumpBanlist() +void CConnman::DumpBanlist() { - CNode::SweepBanned(); // clean unused entries (if bantime has expired) + SweepBanned(); // clean unused entries (if bantime has expired) - if (!CNode::BannedSetIsDirty()) + if (!BannedSetIsDirty()) return; int64_t nStart = GetTimeMillis(); CBanDB bandb; banmap_t banmap; - CNode::SetBannedSetDirty(false); - CNode::GetBanned(banmap); + SetBannedSetDirty(false); + GetBanned(banmap); if (!bandb.Write(banmap)) - CNode::SetBannedSetDirty(true); + SetBannedSetDirty(true); LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); @@ -491,29 +447,22 @@ void CNode::CloseSocketDisconnect() void CNode::PushVersion() { - int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0); - int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); - CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0), addr.nServices)); - CAddress addrMe = GetLocalAddress(&addr); - GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices)); + CAddress addrMe = CAddress(CService(), nLocalServices); if (fLogIPs) - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), addrYou.ToString(), id); else - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), id); PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, strSubVersion, nBestHeight, ::fRelayTxes); + nLocalHostNonce, strSubVersion, nMyStartingHeight, ::fRelayTxes); } -banmap_t CNode::setBanned; -CCriticalSection CNode::cs_setBanned; -bool CNode::setBannedIsDirty; - -void CNode::ClearBanned() +void CConnman::ClearBanned() { { LOCK(cs_setBanned); @@ -521,10 +470,11 @@ void CNode::ClearBanned() setBannedIsDirty = true; } DumpBanlist(); //store banlist to disk - uiInterface.BannedListChanged(); + if(clientInterface) + clientInterface->BannedListChanged(); } -bool CNode::IsBanned(CNetAddr ip) +bool CConnman::IsBanned(CNetAddr ip) { bool fResult = false; { @@ -541,7 +491,7 @@ bool CNode::IsBanned(CNetAddr ip) return fResult; } -bool CNode::IsBanned(CSubNet subnet) +bool CConnman::IsBanned(CSubNet subnet) { bool fResult = false; { @@ -557,12 +507,12 @@ bool CNode::IsBanned(CSubNet subnet) return fResult; } -void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { +void CConnman::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CSubNet subNet(addr); Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); } -void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { +void CConnman::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CBanEntry banEntry(GetTime()); banEntry.banReason = banReason; if (bantimeoffset <= 0) @@ -581,7 +531,8 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti else return; } - uiInterface.BannedListChanged(); + if(clientInterface) + clientInterface->BannedListChanged(); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { @@ -593,37 +544,38 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti DumpBanlist(); //store banlist to disk immediately if user requested ban } -bool CNode::Unban(const CNetAddr &addr) { +bool CConnman::Unban(const CNetAddr &addr) { CSubNet subNet(addr); return Unban(subNet); } -bool CNode::Unban(const CSubNet &subNet) { +bool CConnman::Unban(const CSubNet &subNet) { { LOCK(cs_setBanned); if (!setBanned.erase(subNet)) return false; setBannedIsDirty = true; } - uiInterface.BannedListChanged(); + if(clientInterface) + clientInterface->BannedListChanged(); DumpBanlist(); //store banlist to disk immediately return true; } -void CNode::GetBanned(banmap_t &banMap) +void CConnman::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); banMap = setBanned; //create a thread safe copy } -void CNode::SetBanned(const banmap_t &banMap) +void CConnman::SetBanned(const banmap_t &banMap) { LOCK(cs_setBanned); setBanned = banMap; setBannedIsDirty = true; } -void CNode::SweepBanned() +void CConnman::SweepBanned() { int64_t now = GetTime(); @@ -644,23 +596,20 @@ void CNode::SweepBanned() } } -bool CNode::BannedSetIsDirty() +bool CConnman::BannedSetIsDirty() { LOCK(cs_setBanned); return setBannedIsDirty; } -void CNode::SetBannedSetDirty(bool dirty) +void CConnman::SetBannedSetDirty(bool dirty) { LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag setBannedIsDirty = dirty; } -std::vector<CSubNet> CNode::vWhitelistedRange; -CCriticalSection CNode::cs_vWhitelistedRange; - -bool CNode::IsWhitelistedRange(const CNetAddr &addr) { +bool CConnman::IsWhitelistedRange(const CNetAddr &addr) { LOCK(cs_vWhitelistedRange); BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) { if (subnet.Match(addr)) @@ -669,7 +618,7 @@ bool CNode::IsWhitelistedRange(const CNetAddr &addr) { return false; } -void CNode::AddWhitelistedRange(const CSubNet &subnet) { +void CConnman::AddWhitelistedRange(const CSubNet &subnet) { LOCK(cs_vWhitelistedRange); vWhitelistedRange.push_back(subnet); } @@ -718,8 +667,9 @@ void CNode::copyStats(CNodeStats &stats) #undef X // requires LOCK(cs_vRecvMsg) -bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) +bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete) { + complete = false; while (nBytes > 0) { // get current incomplete message, or create a new one @@ -758,7 +708,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; msg.nTime = GetTimeMicros(); - messageHandlerCondition.notify_one(); + complete = true; } } @@ -821,9 +771,10 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes) // requires LOCK(cs_vSend) -void SocketSendData(CNode *pnode) +size_t SocketSendData(CNode *pnode) { std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin(); + size_t nSentSize = 0; while (it != pnode->vSendMsg.end()) { const CSerializeData &data = *it; @@ -833,7 +784,7 @@ void SocketSendData(CNode *pnode) pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; - pnode->RecordBytesSent(nBytes); + nSentSize += nBytes; if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); @@ -862,10 +813,9 @@ void SocketSendData(CNode *pnode) assert(pnode->nSendSize == 0); } pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); + return nSentSize; } -static std::list<CNode*> vNodesDisconnected; - struct NodeEvictionCandidate { NodeId id; @@ -919,7 +869,8 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction * to forge. In order to partition a node the attacker must be * simultaneously better at all of them than honest peers. */ -static bool AttemptToEvictConnection() { +bool CConnman::AttemptToEvictConnection() +{ std::vector<NodeEvictionCandidate> vEvictionCandidates; { LOCK(cs_vNodes); @@ -982,11 +933,11 @@ static bool AttemptToEvictConnection() { uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; - std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapAddrCounts; + std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes; BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) { - mapAddrCounts[node.nKeyedNetGroup].push_back(node); - int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected; - size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size(); + mapNetGroupNodes[node.nKeyedNetGroup].push_back(node); + int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected; + size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size(); if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = groupsize; @@ -996,7 +947,7 @@ static bool AttemptToEvictConnection() { } // Reduce to the network group with the most connections - vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]); + vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; @@ -1010,19 +961,20 @@ static bool AttemptToEvictConnection() { return false; } -static void AcceptConnection(const ListenSocket& hListenSocket) { +void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; - int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS; + int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler); + assert(nMaxInbound > 0); if (hSocket != INVALID_SOCKET) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) LogPrintf("Warning: Unknown socket family\n"); - bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr); + bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -1054,7 +1006,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); #endif - if (CNode::IsBanned(addr) && !whitelisted) + if (IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); @@ -1071,7 +1023,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { } } - CNode* pnode = new CNode(hSocket, addr, "", true); + CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), "", true); + GetNodeSignals().InitializeNode(pnode->GetId(), pnode); pnode->AddRef(); pnode->fWhitelisted = whitelisted; @@ -1083,7 +1036,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { } } -void ThreadSocketHandler() +void CConnman::ThreadSocketHandler() { unsigned int nPrevNodeCount = 0; while (true) @@ -1141,14 +1094,15 @@ void ThreadSocketHandler() if (fDelete) { vNodesDisconnected.remove(pnode); - delete pnode; + DeleteNode(pnode); } } } } if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); + if(clientInterface) + clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount); } // @@ -1200,16 +1154,22 @@ void ThreadSocketHandler() // * We process a message in the buffer (message handler thread). { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend && !pnode->vSendMsg.empty()) { - FD_SET(pnode->hSocket, &fdsetSend); - continue; + if (lockSend) { + if (pnode->nOptimisticBytesWritten) { + RecordBytesSent(pnode->nOptimisticBytesWritten); + pnode->nOptimisticBytesWritten = 0; + } + if (!pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } } } { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv && ( pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + pnode->GetTotalRecvSize() <= GetReceiveFloodSize())) FD_SET(pnode->hSocket, &fdsetRecv); } } @@ -1274,11 +1234,14 @@ void ThreadSocketHandler() int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); if (nBytes > 0) { - if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) + bool notify = false; + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) pnode->CloseSocketDisconnect(); + if(notify) + messageHandlerCondition.notify_one(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; - pnode->RecordBytesRecv(nBytes); + RecordBytesRecv(nBytes); } else if (nBytes == 0) { @@ -1310,8 +1273,11 @@ void ThreadSocketHandler() if (FD_ISSET(pnode->hSocket, &fdsetSend)) { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - SocketSendData(pnode); + if (lockSend) { + size_t nBytes = SocketSendData(pnode); + if (nBytes) + RecordBytesSent(nBytes); + } } // @@ -1396,8 +1362,11 @@ void ThreadMapPort() { if(externalIPAddress[0]) { - LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); - AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); + CNetAddr resolved; + if(LookupHost(externalIPAddress, resolved, false)) { + LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString().c_str()); + AddLocal(resolved, LOCAL_UPNP); + } } else LogPrintf("UPnP: GetExternalIPAddress failed.\n"); @@ -1488,7 +1457,7 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe } -void ThreadDNSAddressSeed() +void CConnman::ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute if ((addrman.size() > 0) && @@ -1551,7 +1520,7 @@ void ThreadDNSAddressSeed() -void DumpAddresses() +void CConnman::DumpAddresses() { int64_t nStart = GetTimeMillis(); @@ -1562,13 +1531,13 @@ void DumpAddresses() addrman.size(), GetTimeMillis() - nStart); } -void DumpData() +void CConnman::DumpData() { DumpAddresses(); DumpBanlist(); } -void static ProcessOneShot() +void CConnman::ProcessOneShot() { std::string strDest; { @@ -1586,7 +1555,7 @@ void static ProcessOneShot() } } -void ThreadOpenConnections() +void CConnman::ThreadOpenConnections() { // Connect to specific addresses if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) @@ -1609,6 +1578,9 @@ void ThreadOpenConnections() // Initiate network connections int64_t nStart = GetTime(); + + // Minimum time before next feeler connection (in microseconds). + int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL); while (true) { ProcessOneShot(); @@ -1623,7 +1595,9 @@ void ThreadOpenConnections() static bool done = false; if (!done) { LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); - addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1")); + CNetAddr local; + LookupHost("127.0.0.1", local, false); + addrman.Add(convertSeed6(Params().FixedSeeds()), local); done = true; } } @@ -1646,13 +1620,36 @@ void ThreadOpenConnections() } } } + assert(nOutbound <= (nMaxOutbound + nMaxFeeler)); - int64_t nANow = GetAdjustedTime(); + // Feeler Connections + // + // Design goals: + // * Increase the number of connectable addresses in the tried table. + // + // Method: + // * Choose a random address from new and attempt to connect to it if we can connect + // successfully it is added to tried. + // * Start attempting feeler connections only after node finishes making outbound + // connections. + // * Only make a feeler connection once every few minutes. + // + bool fFeeler = false; + if (nOutbound >= nMaxOutbound) { + int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds). + if (nTime > nNextFeeler) { + nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); + fFeeler = true; + } else { + continue; + } + } + int64_t nANow = GetAdjustedTime(); int nTries = 0; while (true) { - CAddrInfo addr = addrman.Select(); + CAddrInfo addr = addrman.Select(fFeeler); // if we selected an invalid address, restart if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) @@ -1676,7 +1673,7 @@ void ThreadOpenConnections() if (nANow - addr.nLastTry < 600 && nTries < 30) continue; - // only consider nodes missing relevant services after 40 failed attemps + // only consider nodes missing relevant services after 40 failed attempts if ((addr.nServices & nRelevantServices) != nRelevantServices && nTries < 40) continue; @@ -1688,12 +1685,21 @@ void ThreadOpenConnections() break; } - if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant); + if (addrConnect.IsValid()) { + + if (fFeeler) { + // Add small amount of random noise before connection to avoid synchronization. + int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000); + MilliSleep(randsleep); + LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString()); + } + + OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler); + } } } -std::vector<AddedNodeInfo> GetAddedNodeInfo() +std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() { std::vector<AddedNodeInfo> ret; @@ -1722,7 +1728,7 @@ std::vector<AddedNodeInfo> GetAddedNodeInfo() } BOOST_FOREACH(const std::string& strAddNode, lAddresses) { - CService service(strAddNode, Params().GetDefaultPort()); + CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort())); if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); @@ -1745,7 +1751,7 @@ std::vector<AddedNodeInfo> GetAddedNodeInfo() return ret; } -void ThreadOpenAddedConnections() +void CConnman::ThreadOpenAddedConnections() { { LOCK(cs_vAddedNodes); @@ -1760,7 +1766,7 @@ void ThreadOpenAddedConnections() CSemaphoreGrant grant(*semOutbound); // If strAddedNode is an IP/port, decode it immediately, so // OpenNetworkConnection can detect existing connections to that IP/port. - CService service(info.strAddedNode, Params().GetDefaultPort()); + CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort())); OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false); MilliSleep(500); } @@ -1771,7 +1777,7 @@ void ThreadOpenAddedConnections() } // if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) +bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler) { // // Initiate outbound network connection @@ -1779,7 +1785,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem boost::this_thread::interruption_point(); if (!pszDest) { if (IsLocal(addrConnect) || - FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode((CNetAddr)addrConnect) || IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) return false; } else if (FindNode(std::string(pszDest))) @@ -1795,12 +1801,14 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem pnode->fNetworkNode = true; if (fOneShot) pnode->fOneShot = true; + if (fFeeler) + pnode->fFeeler = true; return true; } -void ThreadMessageHandler() +void CConnman::ThreadMessageHandler() { boost::mutex condition_mutex; boost::unique_lock<boost::mutex> lock(condition_mutex); @@ -1828,10 +1836,10 @@ void ThreadMessageHandler() TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) { - if (!GetNodeSignals().ProcessMessages(pnode)) + if (!GetNodeSignals().ProcessMessages(pnode, *this)) pnode->CloseSocketDisconnect(); - if (pnode->nSendSize < SendBufferSize()) + if (pnode->nSendSize < GetSendBufferSize()) { if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) { @@ -1846,7 +1854,7 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) - GetNodeSignals().SendMessages(pnode); + GetNodeSignals().SendMessages(pnode, *this); } boost::this_thread::interruption_point(); } @@ -1867,7 +1875,7 @@ void ThreadMessageHandler() -bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) +bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) { strError = ""; int nOne = 1; @@ -1965,7 +1973,7 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite return true; } -void static Discover(boost::thread_group& threadGroup) +void Discover(boost::thread_group& threadGroup) { if (!fDiscover) return; @@ -2016,9 +2024,49 @@ void static Discover(boost::thread_group& threadGroup) #endif } -void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) +CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In) +{ + setBannedIsDirty = false; + fAddressesInitialized = false; + nLastNodeId = 0; + nSendBufferMaxSize = 0; + nReceiveFloodSize = 0; + semOutbound = NULL; + nMaxConnections = 0; + nMaxOutbound = 0; + nBestHeight = 0; + clientInterface = NULL; +} + +NodeId CConnman::GetNewNodeId() +{ + return nLastNodeId.fetch_add(1, std::memory_order_relaxed); +} + +bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, std::string& strNodeError, Options connOptions) { - uiInterface.InitMessage(_("Loading addresses...")); + nTotalBytesRecv = 0; + nTotalBytesSent = 0; + nMaxOutboundTotalBytesSentInCycle = 0; + nMaxOutboundCycleStartTime = 0; + + nRelevantServices = connOptions.nRelevantServices; + nLocalServices = connOptions.nLocalServices; + nMaxConnections = connOptions.nMaxConnections; + nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections); + nMaxFeeler = connOptions.nMaxFeeler; + + nSendBufferMaxSize = connOptions.nSendBufferMaxSize; + nReceiveFloodSize = connOptions.nSendBufferMaxSize; + + nMaxOutboundLimit = connOptions.nMaxOutboundLimit; + nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; + + SetBestHeight(connOptions.nBestHeight); + + clientInterface = connOptions.uiInterface; + if (clientInterface) + clientInterface->InitMessage(_("Loading addresses...")); // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); { @@ -2031,37 +2079,40 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) DumpAddresses(); } } - - uiInterface.InitMessage(_("Loading banlist...")); + if (clientInterface) + clientInterface->InitMessage(_("Loading banlist...")); // Load addresses from banlist.dat nStart = GetTimeMillis(); CBanDB bandb; banmap_t banmap; if (bandb.Read(banmap)) { - CNode::SetBanned(banmap); // thread save setter - CNode::SetBannedSetDirty(false); // no need to write down, just read data - CNode::SweepBanned(); // sweep out unused entries + SetBanned(banmap); // thread save setter + SetBannedSetDirty(false); // no need to write down, just read data + SweepBanned(); // sweep out unused entries LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } else { LogPrintf("Invalid or missing banlist.dat; recreating\n"); - CNode::SetBannedSetDirty(true); // force write + SetBannedSetDirty(true); // force write DumpBanlist(); } + uiInterface.InitMessage(_("Starting network threads...")); + fAddressesInitialized = true; if (semOutbound == NULL) { // initialize semaphore - int nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); - semOutbound = new CSemaphore(nMaxOutbound); + semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); } - if (pnodeLocalHost == NULL) - pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); - - Discover(threadGroup); + if (pnodeLocalHost == NULL) { + CNetAddr local; + LookupHost("127.0.0.1", local, false); + pnodeLocalHost = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), INVALID_SOCKET, CAddress(CService(local, 0), nLocalServices), 0); + GetNodeSignals().InitializeNode(pnodeLocalHost->GetId(), pnodeLocalHost); + } // // Start threads @@ -2070,33 +2121,46 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) if (!GetBoolArg("-dnsseed", true)) LogPrintf("DNS seeding disabled\n"); else - threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed)); - - // Map ports with UPnP - MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); + threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "dnsseed", boost::function<void()>(boost::bind(&CConnman::ThreadDNSAddressSeed, this)))); // Send and receive from sockets, accept connections - threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler)); + threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "net", boost::function<void()>(boost::bind(&CConnman::ThreadSocketHandler, this)))); // Initiate outbound connections from -addnode - threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections)); + threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "addcon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenAddedConnections, this)))); // Initiate outbound connections - threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections)); + threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "opencon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenConnections, this)))); // Process messages - threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler)); + threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "msghand", boost::function<void()>(boost::bind(&CConnman::ThreadMessageHandler, this)))); // Dump network addresses - scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL); + scheduler.scheduleEvery(boost::bind(&CConnman::DumpData, this), DUMP_ADDRESSES_INTERVAL); + + return true; +} + +class CNetCleanup +{ +public: + CNetCleanup() {} + + ~CNetCleanup() + { +#ifdef WIN32 + // Shutdown Windows Sockets + WSACleanup(); +#endif + } } +instance_of_cnetcleanup; -bool StopNode() +void CConnman::Stop() { - LogPrintf("StopNode()\n"); - MapPort(false); + LogPrintf("%s\n",__func__); if (semOutbound) - for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) + for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) semOutbound->post(); if (fAddressesInitialized) @@ -2105,48 +2169,167 @@ bool StopNode() fAddressesInitialized = false; } + // Close sockets + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + CloseSocket(pnode->hSocket); + BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) + if (hListenSocket.socket != INVALID_SOCKET) + if (!CloseSocket(hListenSocket.socket)) + LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); + + // clean up some globals (to help leak detection) + BOOST_FOREACH(CNode *pnode, vNodes) { + DeleteNode(pnode); + } + BOOST_FOREACH(CNode *pnode, vNodesDisconnected) { + DeleteNode(pnode); + } + vNodes.clear(); + vNodesDisconnected.clear(); + vhListenSocket.clear(); + delete semOutbound; + semOutbound = NULL; + if(pnodeLocalHost) + DeleteNode(pnodeLocalHost); + pnodeLocalHost = NULL; +} + +void CConnman::DeleteNode(CNode* pnode) +{ + assert(pnode); + bool fUpdateConnectionTime = false; + GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime); + if(fUpdateConnectionTime) + addrman.Connected(pnode->addr); + delete pnode; +} + +CConnman::~CConnman() +{ + Stop(); +} + +size_t CConnman::GetAddressCount() const +{ + return addrman.size(); +} + +void CConnman::SetServices(const CService &addr, ServiceFlags nServices) +{ + addrman.SetServices(addr, nServices); +} + +void CConnman::MarkAddressGood(const CAddress& addr) +{ + addrman.Good(addr); +} + +void CConnman::AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty) +{ + addrman.Add(addr, addrFrom, nTimePenalty); +} + +void CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty) +{ + addrman.Add(vAddr, addrFrom, nTimePenalty); +} + +std::vector<CAddress> CConnman::GetAddresses() +{ + return addrman.GetAddr(); +} + +bool CConnman::AddNode(const std::string& strNode) +{ + LOCK(cs_vAddedNodes); + for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { + if (strNode == *it) + return false; + } + + vAddedNodes.push_back(strNode); return true; } -class CNetCleanup +bool CConnman::RemoveAddedNode(const std::string& strNode) { -public: - CNetCleanup() {} + LOCK(cs_vAddedNodes); + for(std::vector<std::string>::iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { + if (strNode == *it) { + vAddedNodes.erase(it); + return true; + } + } + return false; +} - ~CNetCleanup() - { - // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - CloseSocket(pnode->hSocket); - BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) - if (hListenSocket.socket != INVALID_SOCKET) - if (!CloseSocket(hListenSocket.socket)) - LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); - - // clean up some globals (to help leak detection) - BOOST_FOREACH(CNode *pnode, vNodes) - delete pnode; - BOOST_FOREACH(CNode *pnode, vNodesDisconnected) - delete pnode; - vNodes.clear(); - vNodesDisconnected.clear(); - vhListenSocket.clear(); - delete semOutbound; - semOutbound = NULL; - delete pnodeLocalHost; - pnodeLocalHost = NULL; +size_t CConnman::GetNodeCount(NumConnections flags) +{ + LOCK(cs_vNodes); + if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total + return vNodes.size(); -#ifdef WIN32 - // Shutdown Windows Sockets - WSACleanup(); -#endif + int nNum = 0; + for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it) + if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) + nNum++; + + return nNum; +} + +void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) +{ + vstats.clear(); + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) { + CNode* pnode = *it; + CNodeStats stats; + pnode->copyStats(stats); + vstats.push_back(stats); } } -instance_of_cnetcleanup; +bool CConnman::DisconnectAddress(const CNetAddr& netAddr) +{ + if (CNode* pnode = FindNode(netAddr)) { + pnode->fDisconnect = true; + return true; + } + return false; +} + +bool CConnman::DisconnectSubnet(const CSubNet& subNet) +{ + if (CNode* pnode = FindNode(subNet)) { + pnode->fDisconnect = true; + return true; + } + return false; +} + +bool CConnman::DisconnectNode(const std::string& strNode) +{ + if (CNode* pnode = FindNode(strNode)) { + pnode->fDisconnect = true; + return true; + } + return false; +} +bool CConnman::DisconnectNode(NodeId id) +{ + LOCK(cs_vNodes); + for(CNode* pnode : vNodes) { + if (id == pnode->id) { + pnode->fDisconnect = true; + return true; + } + } + return false; +} -void RelayTransaction(const CTransaction& tx) +void CConnman::RelayTransaction(const CTransaction& tx) { CInv inv(MSG_TX, tx.GetHash()); LOCK(cs_vNodes); @@ -2156,13 +2339,13 @@ void RelayTransaction(const CTransaction& tx) } } -void CNode::RecordBytesRecv(uint64_t bytes) +void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); nTotalBytesRecv += bytes; } -void CNode::RecordBytesSent(uint64_t bytes) +void CConnman::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; @@ -2179,29 +2362,25 @@ void CNode::RecordBytesSent(uint64_t bytes) nMaxOutboundTotalBytesSentInCycle += bytes; } -void CNode::SetMaxOutboundTarget(uint64_t limit) +void CConnman::SetMaxOutboundTarget(uint64_t limit) { LOCK(cs_totalBytesSent); - uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SERIALIZED_SIZE; nMaxOutboundLimit = limit; - - if (limit > 0 && limit < recommendedMinimum) - LogPrintf("Max outbound target is very small (%s bytes) and will be overshot. Recommended minimum is %s bytes.\n", nMaxOutboundLimit, recommendedMinimum); } -uint64_t CNode::GetMaxOutboundTarget() +uint64_t CConnman::GetMaxOutboundTarget() { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } -uint64_t CNode::GetMaxOutboundTimeframe() +uint64_t CConnman::GetMaxOutboundTimeframe() { LOCK(cs_totalBytesSent); return nMaxOutboundTimeframe; } -uint64_t CNode::GetMaxOutboundTimeLeftInCycle() +uint64_t CConnman::GetMaxOutboundTimeLeftInCycle() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -2215,7 +2394,7 @@ uint64_t CNode::GetMaxOutboundTimeLeftInCycle() return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); } -void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) +void CConnman::SetMaxOutboundTimeframe(uint64_t timeframe) { LOCK(cs_totalBytesSent); if (nMaxOutboundTimeframe != timeframe) @@ -2227,7 +2406,7 @@ void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) nMaxOutboundTimeframe = timeframe; } -bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) +bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -2247,7 +2426,7 @@ bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) return false; } -uint64_t CNode::GetOutboundTargetBytesLeft() +uint64_t CConnman::GetOutboundTargetBytesLeft() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -2256,18 +2435,33 @@ uint64_t CNode::GetOutboundTargetBytesLeft() return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } -uint64_t CNode::GetTotalBytesRecv() +uint64_t CConnman::GetTotalBytesRecv() { LOCK(cs_totalBytesRecv); return nTotalBytesRecv; } -uint64_t CNode::GetTotalBytesSent() +uint64_t CConnman::GetTotalBytesSent() { LOCK(cs_totalBytesSent); return nTotalBytesSent; } +ServiceFlags CConnman::GetLocalServices() const +{ + return nLocalServices; +} + +void CConnman::SetBestHeight(int height) +{ + nBestHeight.store(height, std::memory_order_release); +} + +int CConnman::GetBestHeight() const +{ + return nBestHeight.load(std::memory_order_acquire); +} + void CNode::Fuzz(int nChance) { if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake @@ -2303,121 +2497,13 @@ void CNode::Fuzz(int nChance) Fuzz(2); } -// -// CAddrDB -// - -CAddrDB::CAddrDB() -{ - pathAddr = GetDataDir() / "peers.dat"; -} - -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); - - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(Params().MessageStart()); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathTmp.string()); - - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } - FileCommit(fileout.Get()); - fileout.fclose(); - - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("%s: Rename-into-place failed", __func__); - - return true; -} - -bool CAddrDB::Read(CAddrMan& addr) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathAddr.string().c_str(), "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathAddr.string()); - - // use file size to size memory buffer - uint64_t fileSize = boost::filesystem::file_size(pathAddr); - uint64_t dataSize = 0; - // Don't try to resize to a negative number if file is small - if (fileSize >= sizeof(uint256)) - dataSize = fileSize - sizeof(uint256); - std::vector<unsigned char> vchData; - vchData.resize(dataSize); - uint256 hashIn; +unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; } +unsigned int CConnman::GetSendBufferSize() const{ return nSendBufferMaxSize; } - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - return Read(addr, ssPeers); -} - -bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) -{ - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssPeers >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("%s: Invalid network magic number", __func__); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (const std::exception& e) { - // de-serialization has failed, ensure addrman is left in a clean state - addr.Clear(); - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - - return true; -} - -unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); } -unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); } - -CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : +CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), addr(addrIn), - nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), + nKeyedNetGroup(nKeyedNetGroupIn), addrKnown(5000, 0.001), filterInventoryKnown(50000, 0.000001) { @@ -2437,6 +2523,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa fWhitelisted = false; fOneShot = false; fClient = false; // set by version message + fFeeler = false; fInbound = fInboundIn; fNetworkNode = false; fSuccessfullyConnected = false; @@ -2466,16 +2553,17 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa minFeeFilter = 0; lastSentFeeFilter = 0; nextSendTimeFeeFilter = 0; + id = idIn; + nOptimisticBytesWritten = 0; + nLocalServices = nLocalServicesIn; + + GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + nMyStartingHeight = nMyStartingHeightIn; BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; - { - LOCK(cs_nLastNodeId); - id = nLastNodeId++; - } - if (fLogIPs) LogPrint("net", "Added connection to %s peer=%d\n", addrName, id); else @@ -2484,8 +2572,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) PushVersion(); - - GetNodeSignals().InitializeNode(GetId(), this); } CNode::~CNode() @@ -2494,8 +2580,6 @@ CNode::~CNode() if (pfilter) delete pfilter; - - GetNodeSignals().FinalizeNode(GetId()); } void CNode::AskFor(const CInv& inv) @@ -2590,122 +2674,36 @@ void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) // If write queue empty, attempt "optimistic write" if (it == vSendMsg.begin()) - SocketSendData(this); + nOptimisticBytesWritten += SocketSendData(this); LEAVE_CRITICAL_SECTION(cs_vSend); } -// -// CBanDB -// - -CBanDB::CBanDB() -{ - pathBanlist = GetDataDir() / "banlist.dat"; -} - -bool CBanDB::Write(const banmap_t& banSet) +bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func) { - // Generate random temporary filename - unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("banlist.dat.%04x", randv); - - // serialize banlist, checksum data up to that point, then append csum - CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); - ssBanlist << FLATDATA(Params().MessageStart()); - ssBanlist << banSet; - uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); - ssBanlist << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathTmp.string()); - - // Write and commit header, data - try { - fileout << ssBanlist; - } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } - FileCommit(fileout.Get()); - fileout.fclose(); - - // replace existing banlist.dat, if any, with new banlist.dat.XXXX - if (!RenameOver(pathTmp, pathBanlist)) - return error("%s: Rename-into-place failed", __func__); - - return true; -} - -bool CBanDB::Read(banmap_t& banSet) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathBanlist.string().c_str(), "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathBanlist.string()); - - // use file size to size memory buffer - uint64_t fileSize = boost::filesystem::file_size(pathBanlist); - uint64_t dataSize = 0; - // Don't try to resize to a negative number if file is small - if (fileSize >= sizeof(uint256)) - dataSize = fileSize - sizeof(uint256); - std::vector<unsigned char> vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssBanlist >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("%s: Invalid network magic number", __func__); - - // de-serialize address data into one CAddrMan object - ssBanlist >> banSet; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + CNode* found = nullptr; + LOCK(cs_vNodes); + for (auto&& pnode : vNodes) { + if(pnode->id == id) { + found = pnode; + break; + } } - - return true; + return found != nullptr && func(found); } int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); } -/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad) +CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) { - static const uint64_t k0 = GetRand(std::numeric_limits<uint64_t>::max()); - static const uint64_t k1 = GetRand(std::numeric_limits<uint64_t>::max()); + return CSipHasher(nSeed0, nSeed1).Write(id); +} +uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) +{ std::vector<unsigned char> vchNetGroup(ad.GetGroup()); - return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); + return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); } |