Skip to content

Commit 4966917

Browse files
committed
Merge #9609: net: fix remaining net assertions
08bb6f4 net: log an error rather than asserting if send version is misused (Cory Fields) 7a8c251 net: Disallow sending messages until the version handshake is complete (Cory Fields) 12752af net: don't run callbacks on nodes that haven't completed the version handshake (Cory Fields) 2046617 net: deserialize the entire version message locally (Cory Fields) 80ff034 Dont deserialize nVersion into CNode, should fix #9212 (Matt Corallo)
2 parents a351162 + 08bb6f4 commit 4966917

File tree

4 files changed

+102
-111
lines changed

4 files changed

+102
-111
lines changed

src/net.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,33 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
689689
return true;
690690
}
691691

692+
void CNode::SetSendVersion(int nVersionIn)
693+
{
694+
// Send version may only be changed in the version message, and
695+
// only one version message is allowed per session. We can therefore
696+
// treat this value as const and even atomic as long as it's only used
697+
// once a version message has been successfully processed. Any attempt to
698+
// set this twice is an error.
699+
if (nSendVersion != 0) {
700+
error("Send version already set for node: %i. Refusing to change from %i to %i", id, nSendVersion, nVersionIn);
701+
} else {
702+
nSendVersion = nVersionIn;
703+
}
704+
}
705+
706+
int CNode::GetSendVersion() const
707+
{
708+
// The send version should always be explicitly set to
709+
// INIT_PROTO_VERSION rather than using this value until SetSendVersion
710+
// has been called.
711+
if (nSendVersion == 0) {
712+
error("Requesting unset send version for node: %i. Using %i", id, INIT_PROTO_VERSION);
713+
return INIT_PROTO_VERSION;
714+
}
715+
return nSendVersion;
716+
}
717+
718+
692719
int CNetMessage::readHeader(const char *pch, unsigned int nBytes)
693720
{
694721
// copy data to temporary parsing buffer
@@ -2630,6 +2657,11 @@ void CNode::AskFor(const CInv& inv)
26302657
mapAskFor.insert(std::make_pair(nRequestTime, inv));
26312658
}
26322659

2660+
bool CConnman::NodeFullyConnected(const CNode* pnode)
2661+
{
2662+
return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect;
2663+
}
2664+
26332665
void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
26342666
{
26352667
size_t nMessageSize = msg.data.size();
@@ -2680,7 +2712,7 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
26802712
break;
26812713
}
26822714
}
2683-
return found != nullptr && func(found);
2715+
return found != nullptr && NodeFullyConnected(found) && func(found);
26842716
}
26852717

26862718
int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) {

src/net.h

Lines changed: 23 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -161,85 +161,45 @@ class CConnman
161161

162162
void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
163163

164-
template<typename Callable>
165-
bool ForEachNodeContinueIf(Callable&& func)
166-
{
167-
LOCK(cs_vNodes);
168-
for (auto&& node : vNodes)
169-
if(!func(node))
170-
return false;
171-
return true;
172-
};
173-
174-
template<typename Callable>
175-
bool ForEachNodeContinueIf(Callable&& func) const
176-
{
177-
LOCK(cs_vNodes);
178-
for (const auto& node : vNodes)
179-
if(!func(node))
180-
return false;
181-
return true;
182-
};
183-
184-
template<typename Callable, typename CallableAfter>
185-
bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post)
186-
{
187-
bool ret = true;
188-
LOCK(cs_vNodes);
189-
for (auto&& node : vNodes)
190-
if(!pre(node)) {
191-
ret = false;
192-
break;
193-
}
194-
post();
195-
return ret;
196-
};
197-
198-
template<typename Callable, typename CallableAfter>
199-
bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post) const
200-
{
201-
bool ret = true;
202-
LOCK(cs_vNodes);
203-
for (const auto& node : vNodes)
204-
if(!pre(node)) {
205-
ret = false;
206-
break;
207-
}
208-
post();
209-
return ret;
210-
};
211-
212164
template<typename Callable>
213165
void ForEachNode(Callable&& func)
214166
{
215167
LOCK(cs_vNodes);
216-
for (auto&& node : vNodes)
217-
func(node);
168+
for (auto&& node : vNodes) {
169+
if (NodeFullyConnected(node))
170+
func(node);
171+
}
218172
};
219173

220174
template<typename Callable>
221175
void ForEachNode(Callable&& func) const
222176
{
223177
LOCK(cs_vNodes);
224-
for (const auto& node : vNodes)
225-
func(node);
178+
for (auto&& node : vNodes) {
179+
if (NodeFullyConnected(node))
180+
func(node);
181+
}
226182
};
227183

228184
template<typename Callable, typename CallableAfter>
229185
void ForEachNodeThen(Callable&& pre, CallableAfter&& post)
230186
{
231187
LOCK(cs_vNodes);
232-
for (auto&& node : vNodes)
233-
pre(node);
188+
for (auto&& node : vNodes) {
189+
if (NodeFullyConnected(node))
190+
pre(node);
191+
}
234192
post();
235193
};
236194

237195
template<typename Callable, typename CallableAfter>
238196
void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const
239197
{
240198
LOCK(cs_vNodes);
241-
for (const auto& node : vNodes)
242-
pre(node);
199+
for (auto&& node : vNodes) {
200+
if (NodeFullyConnected(node))
201+
pre(node);
202+
}
243203
post();
244204
};
245205

@@ -372,6 +332,9 @@ class CConnman
372332
void RecordBytesRecv(uint64_t bytes);
373333
void RecordBytesSent(uint64_t bytes);
374334

335+
// Whether the node should be passed out in ForEach* callbacks
336+
static bool NodeFullyConnected(const CNode* pnode);
337+
375338
// Network usage totals
376339
CCriticalSection cs_totalBytesRecv;
377340
CCriticalSection cs_totalBytesSent;
@@ -627,7 +590,7 @@ class CNode
627590
const CAddress addr;
628591
std::string addrName;
629592
CService addrLocal;
630-
int nVersion;
593+
std::atomic<int> nVersion;
631594
// strSubVer is whatever byte array we read from the wire. However, this field is intended
632595
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
633596
// store the sanitized version in cleanSubVer. The original should be used when dealing with
@@ -639,7 +602,7 @@ class CNode
639602
bool fAddnode;
640603
bool fClient;
641604
const bool fInbound;
642-
bool fSuccessfullyConnected;
605+
std::atomic_bool fSuccessfullyConnected;
643606
std::atomic_bool fDisconnect;
644607
// We use fRelayTxes for two purposes -
645608
// a) it allows us to not relay tx invs before receiving the peer's version message
@@ -760,25 +723,8 @@ class CNode
760723
{
761724
return nRecvVersion;
762725
}
763-
void SetSendVersion(int nVersionIn)
764-
{
765-
// Send version may only be changed in the version message, and
766-
// only one version message is allowed per session. We can therefore
767-
// treat this value as const and even atomic as long as it's only used
768-
// once the handshake is complete. Any attempt to set this twice is an
769-
// error.
770-
assert(nSendVersion == 0);
771-
nSendVersion = nVersionIn;
772-
}
773-
774-
int GetSendVersion() const
775-
{
776-
// The send version should always be explicitly set to
777-
// INIT_PROTO_VERSION rather than using this value until the handshake
778-
// is complete.
779-
assert(nSendVersion != 0);
780-
return nSendVersion;
781-
}
726+
void SetSendVersion(int nVersionIn);
727+
int GetSendVersion() const;
782728

783729
CNode* AddRef()
784730
{

src/net_processing.cpp

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,50 +1199,51 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
11991199
CAddress addrFrom;
12001200
uint64_t nNonce = 1;
12011201
uint64_t nServiceInt;
1202-
vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe;
1203-
pfrom->nServices = ServiceFlags(nServiceInt);
1202+
ServiceFlags nServices;
1203+
int nVersion;
1204+
int nSendVersion;
1205+
std::string strSubVer;
1206+
int nStartingHeight = -1;
1207+
bool fRelay = true;
1208+
1209+
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
1210+
nSendVersion = std::min(nVersion, PROTOCOL_VERSION);
1211+
nServices = ServiceFlags(nServiceInt);
12041212
if (!pfrom->fInbound)
12051213
{
1206-
connman.SetServices(pfrom->addr, pfrom->nServices);
1214+
connman.SetServices(pfrom->addr, nServices);
12071215
}
1208-
if (pfrom->nServicesExpected & ~pfrom->nServices)
1216+
if (pfrom->nServicesExpected & ~nServices)
12091217
{
1210-
LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected);
1218+
LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, nServices, pfrom->nServicesExpected);
12111219
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
12121220
strprintf("Expected to offer services %08x", pfrom->nServicesExpected)));
12131221
pfrom->fDisconnect = true;
12141222
return false;
12151223
}
12161224

1217-
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
1225+
if (nVersion < MIN_PEER_PROTO_VERSION)
12181226
{
12191227
// disconnect from peers older than this proto version
1220-
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
1228+
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
12211229
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
12221230
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
12231231
pfrom->fDisconnect = true;
12241232
return false;
12251233
}
12261234

1227-
if (pfrom->nVersion == 10300)
1228-
pfrom->nVersion = 300;
1235+
if (nVersion == 10300)
1236+
nVersion = 300;
12291237
if (!vRecv.empty())
12301238
vRecv >> addrFrom >> nNonce;
12311239
if (!vRecv.empty()) {
1232-
vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH);
1233-
pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer);
1240+
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
12341241
}
12351242
if (!vRecv.empty()) {
1236-
vRecv >> pfrom->nStartingHeight;
1243+
vRecv >> nStartingHeight;
12371244
}
1238-
{
1239-
LOCK(pfrom->cs_filter);
1240-
if (!vRecv.empty())
1241-
vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
1242-
else
1243-
pfrom->fRelayTxes = true;
1244-
}
1245-
1245+
if (!vRecv.empty())
1246+
vRecv >> fRelay;
12461247
// Disconnect if we connected to ourself
12471248
if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
12481249
{
@@ -1251,7 +1252,6 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
12511252
return true;
12521253
}
12531254

1254-
pfrom->addrLocal = addrMe;
12551255
if (pfrom->fInbound && addrMe.IsRoutable())
12561256
{
12571257
SeenLocal(addrMe);
@@ -1261,9 +1261,24 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
12611261
if (pfrom->fInbound)
12621262
PushNodeVersion(pfrom, connman, GetAdjustedTime());
12631263

1264-
pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
1264+
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
12651265

1266-
if((pfrom->nServices & NODE_WITNESS))
1266+
pfrom->nServices = nServices;
1267+
pfrom->addrLocal = addrMe;
1268+
pfrom->strSubVer = strSubVer;
1269+
pfrom->cleanSubVer = SanitizeString(strSubVer);
1270+
pfrom->nStartingHeight = nStartingHeight;
1271+
pfrom->fClient = !(nServices & NODE_NETWORK);
1272+
{
1273+
LOCK(pfrom->cs_filter);
1274+
pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message
1275+
}
1276+
1277+
// Change version
1278+
pfrom->SetSendVersion(nSendVersion);
1279+
pfrom->nVersion = nVersion;
1280+
1281+
if((nServices & NODE_WITNESS))
12671282
{
12681283
LOCK(cs_main);
12691284
State(pfrom->GetId())->fHaveWitness = true;
@@ -1275,11 +1290,6 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
12751290
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
12761291
}
12771292

1278-
// Change version
1279-
connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK));
1280-
int nSendVersion = std::min(pfrom->nVersion, PROTOCOL_VERSION);
1281-
pfrom->SetSendVersion(nSendVersion);
1282-
12831293
if (!pfrom->fInbound)
12841294
{
12851295
// Advertise our address
@@ -1307,8 +1317,6 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
13071317
connman.MarkAddressGood(pfrom->addr);
13081318
}
13091319

1310-
pfrom->fSuccessfullyConnected = true;
1311-
13121320
std::string remoteAddr;
13131321
if (fLogIPs)
13141322
remoteAddr = ", peeraddr=" + pfrom->addr.ToString();
@@ -1350,7 +1358,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
13501358

13511359
if (strCommand == NetMsgType::VERACK)
13521360
{
1353-
pfrom->SetRecvVersion(std::min(pfrom->nVersion, PROTOCOL_VERSION));
1361+
pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION));
13541362

13551363
if (!pfrom->fInbound) {
13561364
// Mark this node as currently connected, so we update its timestamp later.
@@ -1378,6 +1386,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
13781386
nCMPCTBLOCKVersion = 1;
13791387
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion));
13801388
}
1389+
pfrom->fSuccessfullyConnected = true;
13811390
}
13821391

13831392

@@ -2716,8 +2725,8 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg
27162725
{
27172726
const Consensus::Params& consensusParams = Params().GetConsensus();
27182727
{
2719-
// Don't send anything until we get its version message
2720-
if (pto->nVersion == 0 || pto->fDisconnect)
2728+
// Don't send anything until the version handshake is complete
2729+
if (!pto->fSuccessfullyConnected || pto->fDisconnect)
27212730
return true;
27222731

27232732
// If we get here, the outgoing message serialization version is set and can't change.

0 commit comments

Comments
 (0)