Skip to content

Commit

Permalink
Merge pull request zcash#6781 from daira/disconnect-on-invalid-versio…
Browse files Browse the repository at this point in the history
…n-message

Disconnect on a malformed version message, and reject duplicate verack messages
  • Loading branch information
str4d authored May 22, 2024
2 parents 77c5cfe + abe934c commit e78b319
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 63 deletions.
145 changes: 83 additions & 62 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6796,80 +6796,95 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"));
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 1);
return false;
}

int64_t nTime;
// It's necessary to initialize nVersion because if the message is less than 4 bytes,
// it will not be set (but is read in the catch block for std::ios_base::failure).
int32_t nVersion = 0;
int64_t nTime = 0;
CAddress addrMe;
CAddress addrFrom;
uint64_t nNonce = 1;
std::string strSubVer;
std::string cleanSubVer;
uint64_t nServices;
vRecv >> pfrom->nVersion >> nServices >> nTime >> addrMe;
pfrom->nServices = nServices;
if (pfrom->nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
return false;
}
try {
uint64_t nServices;
vRecv >> nVersion >> nServices >> nTime >> addrMe;

if (chainparams.NetworkIDString() == "test" &&
pfrom->nVersion < MIN_TESTNET_PEER_PROTO_VERSION)
{
// disconnect from testnet peers older than this proto version
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_TESTNET_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
return false;
}
if (nVersion < MIN_PEER_PROTO_VERSION)
{
// disconnect from peers older than this proto version
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
return false;
}

// Reject incoming connections from nodes that don't know about the current epoch
const Consensus::Params& consensusParams = chainparams.GetConsensus();
auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams);
if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion &&
!(
chainparams.NetworkIDString() == "regtest" &&
!GetBoolArg("-nurejectoldversions", DEFAULT_NU_REJECT_OLD_VERSIONS)
)
) {
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater",
consensusParams.vUpgrades[currentEpoch].nProtocolVersion));
if (chainparams.NetworkIDString() == "test" &&
nVersion < MIN_TESTNET_PEER_PROTO_VERSION)
{
// disconnect from testnet peers older than this proto version
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_TESTNET_PEER_PROTO_VERSION));
pfrom->fDisconnect = true;
return false;
}

// Reject incoming connections from nodes that don't know about the current epoch
const Consensus::Params& consensusParams = chainparams.GetConsensus();
auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams);
if (nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion &&
!(
chainparams.NetworkIDString() == "regtest" &&
!GetBoolArg("-nurejectoldversions", DEFAULT_NU_REJECT_OLD_VERSIONS)
)
) {
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater",
consensusParams.vUpgrades[currentEpoch].nProtocolVersion));
pfrom->fDisconnect = true;
return false;
}

// We've successfully parsed the mandatory fields and checked the version.
// It's safe to leave these set even if subsequent parsing fails.
pfrom->nVersion = nVersion;
pfrom->nServices = nServices;

if (!vRecv.empty()) {
vRecv >> addrFrom >> nNonce;
}
if (!vRecv.empty()) {
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
cleanSubVer = SanitizeString(strSubVer, SAFE_CHARS_SUBVERSION);
}
if (!vRecv.empty()) {
int32_t nStartingHeight;
vRecv >> nStartingHeight;
pfrom->nStartingHeight = nStartingHeight;
}
{
LOCK(pfrom->cs_filter);
if (!vRecv.empty()) {
vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
} else {
pfrom->fRelayTxes = true;
}
}
} catch (const std::ios_base::failure&) {
LogPrintf("peer=%d using version %i sent malformed version message; disconnecting\n", pfrom->id, nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, std::string("Malformed version message"));
pfrom->fDisconnect = true;
return false;
}

if (pfrom->nVersion == 10300)
pfrom->nVersion = 300;
if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
if (!vRecv.empty()) {
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
cleanSubVer = SanitizeString(strSubVer, SAFE_CHARS_SUBVERSION);
}
if (!vRecv.empty()) {
int nStartingHeight;
vRecv >> nStartingHeight;
pfrom->nStartingHeight = nStartingHeight;
}
{
LOCK(pfrom->cs_filter);
if (!vRecv.empty())
vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
else
pfrom->fRelayTxes = true;
}

// Disconnect if we connected to ourself
if (nNonce == nLocalHostNonce && nNonce > 1)
{
Expand Down Expand Up @@ -6949,6 +6964,7 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
item.second.RelayTo(pfrom);
}

pfrom->nTimeOffset = timeWarning.AddTimeData(pfrom->addr, nTime, GetTime());
pfrom->fSuccessfullyConnected = true;

string remoteAddr;
Expand All @@ -6959,8 +6975,6 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string
cleanSubVer, pfrom->nVersion,
pfrom->nStartingHeight, addrMe.ToString(), pfrom->id,
remoteAddr);

pfrom->nTimeOffset = timeWarning.AddTimeData(pfrom->addr, nTime, GetTime());
}


Expand All @@ -6975,12 +6989,19 @@ bool static ProcessMessage(const CChainParams& chainparams, CNode* pfrom, string

else if (strCommand == "verack")
{
LOCK(cs_main);
CNodeState* state = State(pfrom->GetId());
assert(state != nullptr);
if (state->fCurrentlyConnected) {
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, std::string("Duplicate verack message"));
Misbehaving(pfrom->GetId(), 1);
return false;
}
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));

// Mark this node as currently connected, so we update its timestamp later.
if (pfrom->fNetworkNode) {
LOCK(cs_main);
State(pfrom->GetId())->fCurrentlyConnected = true;
state->fCurrentlyConnected = true;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler);
bool StopNode();
void SocketSendData(CNode *pnode);

typedef int NodeId;
typedef int64_t NodeId;

struct CombinerAll
{
Expand Down

0 comments on commit e78b319

Please sign in to comment.