Skip to content

Commit be9711e

Browse files
committed
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)
2 parents 44c1b1c + ecd7fd3 commit be9711e

File tree

11 files changed

+142
-78
lines changed

11 files changed

+142
-78
lines changed

src/addrman.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
263263
pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty);
264264

265265
// add services
266-
pinfo->nServices |= addr.nServices;
266+
pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
267267

268268
// do not update if no new information is present
269269
if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
@@ -502,6 +502,24 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime)
502502
info.nTime = nTime;
503503
}
504504

505+
void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
506+
{
507+
CAddrInfo* pinfo = Find(addr);
508+
509+
// if not found, bail out
510+
if (!pinfo)
511+
return;
512+
513+
CAddrInfo& info = *pinfo;
514+
515+
// check whether we are talking about the exact same CService (including same port)
516+
if (info != addr)
517+
return;
518+
519+
// update info
520+
info.nServices = nServices;
521+
}
522+
505523
int CAddrMan::RandomInt(int nMax){
506524
return GetRandInt(nMax);
507525
}

src/addrman.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ class CAddrMan
256256
//! Mark an entry as currently-connected-to.
257257
void Connected_(const CService &addr, int64_t nTime);
258258

259+
//! Update an entry's service bits.
260+
void SetServices_(const CService &addr, ServiceFlags nServices);
261+
259262
public:
260263
/**
261264
* serialized format:
@@ -589,6 +592,14 @@ class CAddrMan
589592
}
590593
}
591594

595+
void SetServices(const CService &addr, ServiceFlags nServices)
596+
{
597+
LOCK(cs);
598+
Check();
599+
SetServices_(addr, nServices);
600+
Check();
601+
}
602+
592603
};
593604

594605
#endif // BITCOIN_ADDRMAN_H

src/init.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
950950
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
951951

952952
if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
953-
nLocalServices |= NODE_BLOOM;
953+
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
954954

955955
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
956956

@@ -1361,7 +1361,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
13611361
// after any wallet rescanning has taken place.
13621362
if (fPruneMode) {
13631363
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
1364-
nLocalServices &= ~NODE_NETWORK;
1364+
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
13651365
if (!fReindex) {
13661366
uiInterface.InitMessage(_("Pruning blockstore..."));
13671367
PruneAndFlush();

src/main.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4611,7 +4611,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
46114611
CAddress addrMe;
46124612
CAddress addrFrom;
46134613
uint64_t nNonce = 1;
4614-
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
4614+
uint64_t nServiceInt;
4615+
vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
4616+
pfrom->nServices = ServiceFlags(nServiceInt);
4617+
if (!pfrom->fInbound)
4618+
{
4619+
addrman.SetServices(pfrom->addr, pfrom->nServices);
4620+
}
4621+
if (pfrom->nServicesExpected & ~pfrom->nServices)
4622+
{
4623+
LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
4624+
pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
4625+
strprintf("Expected to offer services %08x", pfrom->nServicesExpected));
4626+
pfrom->fDisconnect = true;
4627+
return false;
4628+
}
4629+
46154630
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
46164631
{
46174632
// disconnect from peers older than this proto version
@@ -4772,6 +4787,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
47724787
{
47734788
boost::this_thread::interruption_point();
47744789

4790+
if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
4791+
continue;
4792+
47754793
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
47764794
addr.nTime = nNow - 5 * 24 * 60 * 60;
47774795
pfrom->AddAddressKnown(addr);

src/net.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,15 @@ namespace {
7171

7272
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
7373

74+
/** Services this node implementation cares about */
75+
static const ServiceFlags nRelevantServices = NODE_NETWORK;
76+
7477
//
7578
// Global state variables
7679
//
7780
bool fDiscover = true;
7881
bool fListen = true;
79-
uint64_t nLocalServices = NODE_NETWORK;
82+
ServiceFlags nLocalServices = NODE_NETWORK;
8083
bool fRelayTxes = true;
8184
CCriticalSection cs_mapLocalHost;
8285
std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
@@ -159,7 +162,7 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
159162
{
160163
struct in6_addr ip;
161164
memcpy(&ip, i->addr, sizeof(ip));
162-
CAddress addr(CService(ip, i->port));
165+
CAddress addr(CService(ip, i->port), NODE_NETWORK);
163166
addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
164167
vSeedsOut.push_back(addr);
165168
}
@@ -172,13 +175,12 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
172175
// one by discovery.
173176
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
174177
{
175-
CAddress ret(CService("0.0.0.0",GetListenPort()),0);
178+
CAddress ret(CService("0.0.0.0",GetListenPort()), NODE_NONE);
176179
CService addr;
177180
if (GetLocal(addr, paddrPeer))
178181
{
179-
ret = CAddress(addr);
182+
ret = CAddress(addr, nLocalServices);
180183
}
181-
ret.nServices = nLocalServices;
182184
ret.nTime = GetAdjustedTime();
183185
return ret;
184186
}
@@ -409,6 +411,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure
409411
vNodes.push_back(pnode);
410412
}
411413

414+
pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices);
412415
pnode->nTimeConnected = GetTime();
413416

414417
return pnode;
@@ -461,14 +464,14 @@ void CNode::PushVersion()
461464
int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0);
462465

463466
int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
464-
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0)));
467+
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0), addr.nServices));
465468
CAddress addrMe = GetLocalAddress(&addr);
466469
GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
467470
if (fLogIPs)
468471
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
469472
else
470473
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
471-
PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
474+
PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe,
472475
nLocalHostNonce, strSubVersion, nBestHeight, ::fRelayTxes);
473476
}
474477

@@ -1437,7 +1440,7 @@ void ThreadDNSAddressSeed()
14371440
} else {
14381441
std::vector<CNetAddr> vIPs;
14391442
std::vector<CAddress> vAdd;
1440-
uint64_t requiredServiceBits = NODE_NETWORK;
1443+
ServiceFlags requiredServiceBits = nRelevantServices;
14411444
if (LookupHost(seed.getHost(requiredServiceBits).c_str(), vIPs, 0, true))
14421445
{
14431446
BOOST_FOREACH(const CNetAddr& ip, vIPs)
@@ -1520,7 +1523,7 @@ void ThreadOpenConnections()
15201523
ProcessOneShot();
15211524
BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect"])
15221525
{
1523-
CAddress addr;
1526+
CAddress addr(CService(), NODE_NONE);
15241527
OpenNetworkConnection(addr, false, NULL, strAddr.c_str());
15251528
for (int i = 0; i < 10 && i < nLoop; i++)
15261529
{
@@ -1592,6 +1595,10 @@ void ThreadOpenConnections()
15921595
if (IsLimited(addr))
15931596
continue;
15941597

1598+
// only connect to full nodes
1599+
if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES)
1600+
continue;
1601+
15951602
// only consider very recently tried nodes after 30 failed attempts
15961603
if (nANow - addr.nLastTry < 600 && nTries < 30)
15971604
continue;
@@ -1666,7 +1673,9 @@ void ThreadOpenAddedConnections()
16661673
BOOST_FOREACH(std::vector<CService>& vserv, lservAddressesToAdd)
16671674
{
16681675
CSemaphoreGrant grant(*semOutbound);
1669-
OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), false, &grant);
1676+
/* We want -addnode to work even for nodes that don't provide all
1677+
* wanted services, so pass in nServices=NODE_NONE to CAddress. */
1678+
OpenNetworkConnection(CAddress(vserv[i % vserv.size()], NODE_NONE), false, &grant);
16701679
MilliSleep(500);
16711680
}
16721681
MilliSleep(120000); // Retry every 2 minutes
@@ -2324,7 +2333,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
23242333
addrKnown(5000, 0.001),
23252334
filterInventoryKnown(50000, 0.000001)
23262335
{
2327-
nServices = 0;
2336+
nServices = NODE_NONE;
2337+
nServicesExpected = NODE_NONE;
23282338
hSocket = hSocketIn;
23292339
nRecvVersion = INIT_PROTO_VERSION;
23302340
nLastSend = 0;

src/net.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ static const bool DEFAULT_FORCEDNSSEED = false;
7272
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
7373
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
7474

75+
static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK;
76+
7577
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
7678
static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
7779

@@ -152,7 +154,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
152154

153155
extern bool fDiscover;
154156
extern bool fListen;
155-
extern uint64_t nLocalServices;
157+
extern ServiceFlags nLocalServices;
156158
extern bool fRelayTxes;
157159
extern uint64_t nLocalHostNonce;
158160
extern CAddrMan addrman;
@@ -186,7 +188,7 @@ class CNodeStats
186188
{
187189
public:
188190
NodeId nodeid;
189-
uint64_t nServices;
191+
ServiceFlags nServices;
190192
bool fRelayTxes;
191193
int64_t nLastSend;
192194
int64_t nLastRecv;
@@ -316,7 +318,8 @@ class CNode
316318
{
317319
public:
318320
// socket
319-
uint64_t nServices;
321+
ServiceFlags nServices;
322+
ServiceFlags nServicesExpected;
320323
SOCKET hSocket;
321324
CDataStream ssSend;
322325
size_t nSendSize; // total size of all vSendMsg entries

src/protocol.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,15 @@ CAddress::CAddress() : CService()
133133
Init();
134134
}
135135

136-
CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn)
136+
CAddress::CAddress(CService ipIn, ServiceFlags nServicesIn) : CService(ipIn)
137137
{
138138
Init();
139139
nServices = nServicesIn;
140140
}
141141

142142
void CAddress::Init()
143143
{
144-
nServices = NODE_NETWORK;
144+
nServices = NODE_NONE;
145145
nTime = 100000000;
146146
}
147147

src/protocol.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ extern const char *FEEFILTER;
223223
const std::vector<std::string> &getAllNetMessageTypes();
224224

225225
/** nServices flags */
226-
enum {
226+
enum ServiceFlags : uint64_t {
227+
// Nothing
228+
NODE_NONE = 0,
227229
// NODE_NETWORK means that the node is capable of serving the block chain. It is currently
228230
// set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
229231
// network services but don't provide them.
@@ -251,7 +253,7 @@ class CAddress : public CService
251253
{
252254
public:
253255
CAddress();
254-
explicit CAddress(CService ipIn, uint64_t nServicesIn = NODE_NETWORK);
256+
explicit CAddress(CService ipIn, ServiceFlags nServicesIn);
255257

256258
void Init();
257259

@@ -267,13 +269,15 @@ class CAddress : public CService
267269
if ((nType & SER_DISK) ||
268270
(nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH)))
269271
READWRITE(nTime);
270-
READWRITE(nServices);
272+
uint64_t nServicesInt = nServices;
273+
READWRITE(nServicesInt);
274+
nServices = (ServiceFlags)nServicesInt;
271275
READWRITE(*(CService*)this);
272276
}
273277

274278
// TODO: make private (improves encapsulation)
275279
public:
276-
uint64_t nServices;
280+
ServiceFlags nServices;
277281

278282
// disk and network only
279283
unsigned int nTime;

src/test/DoS_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
4545
BOOST_AUTO_TEST_CASE(DoS_banning)
4646
{
4747
CNode::ClearBanned();
48-
CAddress addr1(ip(0xa0b0c001));
48+
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
4949
CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
5050
dummyNode1.nVersion = 1;
5151
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
5252
SendMessages(&dummyNode1);
5353
BOOST_CHECK(CNode::IsBanned(addr1));
5454
BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
5555

56-
CAddress addr2(ip(0xa0b0c002));
56+
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
5757
CNode dummyNode2(INVALID_SOCKET, addr2, "", true);
5858
dummyNode2.nVersion = 1;
5959
Misbehaving(dummyNode2.GetId(), 50);
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
6969
{
7070
CNode::ClearBanned();
7171
mapArgs["-banscore"] = "111"; // because 11 is my favorite number
72-
CAddress addr1(ip(0xa0b0c001));
72+
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
7373
CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
7474
dummyNode1.nVersion = 1;
7575
Misbehaving(dummyNode1.GetId(), 100);
@@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
9090
int64_t nStartTime = GetTime();
9191
SetMockTime(nStartTime); // Overrides future calls to GetTime()
9292

93-
CAddress addr(ip(0xa0b0c001));
93+
CAddress addr(ip(0xa0b0c001), NODE_NONE);
9494
CNode dummyNode(INVALID_SOCKET, addr, "", true);
9595
dummyNode.nVersion = 1;
9696

0 commit comments

Comments
 (0)