Permalink
Browse files

Merge #7749: Enforce expected outbound services

ecd7fd3 Introduce REQUIRED_SERVICES constant (Pieter Wuille)
ee06e04 Introduce enum ServiceFlags for service flags (Pieter Wuille)
15bf863 Don't require services in -addnode (Pieter Wuille)
5e7ab16 Only store and connect to NODE_NETWORK nodes (Pieter Wuille)
fc83f18 Verify that outbound connections have expected services (Pieter Wuille)
3764dec Keep addrman's nService bits consistent with outbound observations (Pieter Wuille)
  • Loading branch information...
laanwj committed Jun 13, 2016
2 parents 44c1b1c + ecd7fd3 commit be9711e597071c813bdd72c2866c547f117e4865
Showing with 142 additions and 78 deletions.
  1. +19 −1 src/addrman.cpp
  2. +11 −0 src/addrman.h
  3. +2 −2 src/init.cpp
  4. +19 −1 src/main.cpp
  5. +21 −11 src/net.cpp
  6. +6 −3 src/net.h
  7. +2 −2 src/protocol.cpp
  8. +8 −4 src/protocol.h
  9. +4 −4 src/test/DoS_tests.cpp
  10. +46 −46 src/test/addrman_tests.cpp
  11. +4 −4 src/test/net_tests.cpp
View
@@ -263,7 +263,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty);
// add services
pinfo->nServices |= addr.nServices;
pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
// do not update if no new information is present
if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
@@ -502,6 +502,24 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
info.nTime = nTime;
}
void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
{
CAddrInfo* pinfo = Find(addr);
// if not found, bail out
if (!pinfo)
return;
CAddrInfo& info = *pinfo;
// check whether we are talking about the exact same CService (including same port)
if (info != addr)
return;
// update info
info.nServices = nServices;
}
int CAddrMan::RandomInt(int nMax){
return GetRandInt(nMax);
}
View
@@ -256,6 +256,9 @@ class CAddrMan
//! Mark an entry as currently-connected-to.
void Connected_(const CService &addr, int64_t nTime);
//! Update an entry's service bits.
void SetServices_(const CService &addr, ServiceFlags nServices);
public:
/**
* serialized format:
@@ -589,6 +592,14 @@ class CAddrMan
}
}
void SetServices(const CService &addr, ServiceFlags nServices)
{
LOCK(cs);
Check();
SetServices_(addr, nServices);
Check();
}
};
#endif // BITCOIN_ADDRMAN_H
View
@@ -950,7 +950,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices |= NODE_BLOOM;
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
@@ -1361,7 +1361,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// after any wallet rescanning has taken place.
if (fPruneMode) {
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
nLocalServices &= ~NODE_NETWORK;
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
uiInterface.InitMessage(_("Pruning blockstore..."));
PruneAndFlush();
View
@@ -4611,7 +4611,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CAddress addrMe;
CAddress addrFrom;
uint64_t nNonce = 1;
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
uint64_t nServiceInt;
vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
pfrom->nServices = ServiceFlags(nServiceInt);
if (!pfrom->fInbound)
{
addrman.SetServices(pfrom->addr, pfrom->nServices);
}
if (pfrom->nServicesExpected & ~pfrom->nServices)
{
LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
strprintf("Expected to offer services %08x", pfrom->nServicesExpected));
pfrom->fDisconnect = true;
return false;
}
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
@@ -4772,6 +4787,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{
boost::this_thread::interruption_point();
if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
continue;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr);
View
@@ -71,12 +71,15 @@ namespace {
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
/** Services this node implementation cares about */
static const ServiceFlags nRelevantServices = NODE_NETWORK;
//
// Global state variables
//
bool fDiscover = true;
bool fListen = true;
uint64_t nLocalServices = NODE_NETWORK;
ServiceFlags nLocalServices = NODE_NETWORK;
bool fRelayTxes = true;
CCriticalSection cs_mapLocalHost;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
@@ -159,7 +162,7 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
{
struct in6_addr ip;
memcpy(&ip, i->addr, sizeof(ip));
CAddress addr(CService(ip, i->port));
CAddress addr(CService(ip, i->port), NODE_NETWORK);
addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
vSeedsOut.push_back(addr);
}
@@ -172,13 +175,12 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
// one by discovery.
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
{
CAddress ret(CService("0.0.0.0",GetListenPort()),0);
CAddress ret(CService("0.0.0.0",GetListenPort()), NODE_NONE);
CService addr;
if (GetLocal(addr, paddrPeer))
{
ret = CAddress(addr);
ret = CAddress(addr, nLocalServices);
}
ret.nServices = nLocalServices;
ret.nTime = GetAdjustedTime();
return ret;
}
@@ -409,6 +411,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure
vNodes.push_back(pnode);
}
pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);
pnode->nTimeConnected = GetTime();
return pnode;
@@ -461,14 +464,14 @@ 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)));
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));
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);
else
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe,
nLocalHostNonce, strSubVersion, nBestHeight, ::fRelayTxes);
}
@@ -1437,7 +1440,7 @@ void ThreadDNSAddressSeed()
} else {
std::vector<CNetAddr> vIPs;
std::vector<CAddress> vAdd;
uint64_t requiredServiceBits = NODE_NETWORK;
ServiceFlags requiredServiceBits = nRelevantServices;
if (LookupHost(seed.getHost(requiredServiceBits).c_str(), vIPs, 0, true))
{
BOOST_FOREACH(const CNetAddr& ip, vIPs)
@@ -1520,7 +1523,7 @@ void ThreadOpenConnections()
ProcessOneShot();
BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect"])
{
CAddress addr;
CAddress addr(CService(), NODE_NONE);
OpenNetworkConnection(addr, false, NULL, strAddr.c_str());
for (int i = 0; i < 10 && i < nLoop; i++)
{
@@ -1592,6 +1595,10 @@ void ThreadOpenConnections()
if (IsLimited(addr))
continue;
// only connect to full nodes
if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
continue;
// only consider very recently tried nodes after 30 failed attempts
if (nANow - addr.nLastTry < 600 && nTries < 30)
continue;
@@ -1666,7 +1673,9 @@ void ThreadOpenAddedConnections()
BOOST_FOREACH(std::vector<CService>& vserv, lservAddressesToAdd)
{
CSemaphoreGrant grant(*semOutbound);
OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), false, &grant);
/* We want -addnode to work even for nodes that don't provide all
* wanted services, so pass in nServices=NODE_NONE to CAddress. */
OpenNetworkConnection(CAddress(vserv[i % vserv.size()], NODE_NONE), false, &grant);
MilliSleep(500);
}
MilliSleep(120000); // Retry every 2 minutes
@@ -2324,7 +2333,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
addrKnown(5000, 0.001),
filterInventoryKnown(50000, 0.000001)
{
nServices = 0;
nServices = NODE_NONE;
nServicesExpected = NODE_NONE;
hSocket = hSocketIn;
nRecvVersion = INIT_PROTO_VERSION;
nLastSend = 0;
View
@@ -72,6 +72,8 @@ static const bool DEFAULT_FORCEDNSSEED = false;
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK;
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
@@ -152,7 +154,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
extern bool fDiscover;
extern bool fListen;
extern uint64_t nLocalServices;
extern ServiceFlags nLocalServices;
extern bool fRelayTxes;
extern uint64_t nLocalHostNonce;
extern CAddrMan addrman;
@@ -186,7 +188,7 @@ class CNodeStats
{
public:
NodeId nodeid;
uint64_t nServices;
ServiceFlags nServices;
bool fRelayTxes;
int64_t nLastSend;
int64_t nLastRecv;
@@ -316,7 +318,8 @@ class CNode
{
public:
// socket
uint64_t nServices;
ServiceFlags nServices;
ServiceFlags nServicesExpected;
SOCKET hSocket;
CDataStream ssSend;
size_t nSendSize; // total size of all vSendMsg entries
View
@@ -133,15 +133,15 @@ CAddress::CAddress() : CService()
Init();
}
CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn)
CAddress::CAddress(CService ipIn, ServiceFlags nServicesIn) : CService(ipIn)
{
Init();
nServices = nServicesIn;
}
void CAddress::Init()
{
nServices = NODE_NETWORK;
nServices = NODE_NONE;
nTime = 100000000;
}
View
@@ -223,7 +223,9 @@ extern const char *FEEFILTER;
const std::vector<std::string> &getAllNetMessageTypes();
/** nServices flags */
enum {
enum ServiceFlags : uint64_t {
// Nothing
NODE_NONE = 0,
// NODE_NETWORK means that the node is capable of serving the block chain. It is currently
// set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
// network services but don't provide them.
@@ -251,7 +253,7 @@ class CAddress : public CService
{
public:
CAddress();
explicit CAddress(CService ipIn, uint64_t nServicesIn = NODE_NETWORK);
explicit CAddress(CService ipIn, ServiceFlags nServicesIn);
void Init();
@@ -267,13 +269,15 @@ class CAddress : public CService
if ((nType & SER_DISK) ||
(nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH)))
READWRITE(nTime);
READWRITE(nServices);
uint64_t nServicesInt = nServices;
READWRITE(nServicesInt);
nServices = (ServiceFlags)nServicesInt;
READWRITE(*(CService*)this);
}
// TODO: make private (improves encapsulation)
public:
uint64_t nServices;
ServiceFlags nServices;
// disk and network only
unsigned int nTime;
View
@@ -45,15 +45,15 @@ BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(DoS_banning)
{
CNode::ClearBanned();
CAddress addr1(ip(0xa0b0c001));
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
SendMessages(&dummyNode1);
BOOST_CHECK(CNode::IsBanned(addr1));
BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
CAddress addr2(ip(0xa0b0c002));
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
CNode dummyNode2(INVALID_SOCKET, addr2, "", true);
dummyNode2.nVersion = 1;
Misbehaving(dummyNode2.GetId(), 50);
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
{
CNode::ClearBanned();
mapArgs["-banscore"] = "111"; // because 11 is my favorite number
CAddress addr1(ip(0xa0b0c001));
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100);
@@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
int64_t nStartTime = GetTime();
SetMockTime(nStartTime); // Overrides future calls to GetTime()
CAddress addr(ip(0xa0b0c001));
CAddress addr(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode(INVALID_SOCKET, addr, "", true);
dummyNode.nVersion = 1;
Oops, something went wrong.

0 comments on commit be9711e

Please sign in to comment.