From 0f90613cbe69dfa422e8802c63844f816c3ca588 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Tue, 29 Oct 2013 08:00:13 +1000 Subject: [PATCH 1/5] Improve logging of failed connections --- src/netbase.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index 88c58f854..1392fa823 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -367,13 +367,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); if (nRet == 0) { - LogPrint("net", "connection timeout\n"); + LogPrint("net", "connection to %s timeout\n", addrConnect.ToString().c_str()); closesocket(hSocket); return false; } if (nRet == SOCKET_ERROR) { - LogPrintf("select() for connection failed: %i\n",WSAGetLastError()); + LogPrintf("select() for %s failed: %i\n", addrConnect.ToString().c_str(), WSAGetLastError()); closesocket(hSocket); return false; } @@ -384,13 +384,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) #endif { - LogPrintf("getsockopt() for connection failed: %i\n",WSAGetLastError()); + LogPrintf("getsockopt() for %s failed: %i\n", addrConnect.ToString().c_str(), WSAGetLastError()); closesocket(hSocket); return false; } if (nRet != 0) { - LogPrintf("connect() failed after select(): %s\n",strerror(nRet)); + LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString().c_str(), strerror(nRet)); closesocket(hSocket); return false; } @@ -401,7 +401,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe else #endif { - LogPrintf("connect() failed: %i\n",WSAGetLastError()); + LogPrintf("connect() to %s failed: %i\n", addrConnect.ToString().c_str(), WSAGetLastError()); closesocket(hSocket); return false; } From 17faf562629cd27f00fc138e218ebcc1ce071765 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sat, 2 Nov 2013 05:27:42 +1000 Subject: [PATCH 2/5] Refactor: pull alert string sanitization into util --- src/alert.cpp | 10 +--------- src/util.cpp | 13 +++++++++++++ src/util.h | 1 + 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/alert.cpp b/src/alert.cpp index b900fe41e..7f7e59ee1 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -241,15 +241,7 @@ bool CAlert::ProcessAlert(bool fThread) // be safe we first strip anything not in safeChars, then add single quotes around // the whole string before passing it to the shell: std::string singleQuote("'"); - // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything - // even possibly remotely dangerous like & or > - std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); - std::string safeStatus; - for (std::string::size_type i = 0; i < strStatusBar.size(); i++) - { - if (safeChars.find(strStatusBar[i]) != std::string::npos) - safeStatus.push_back(strStatusBar[i]); - } + std::string safeStatus = SanitizeString(strStatusBar); safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); diff --git a/src/util.cpp b/src/util.cpp index 9562cf310..5411bb2fe 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -475,6 +475,19 @@ bool ParseMoney(const char* pszIn, int64_t& nRet) return true; } +// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything +// even possibly remotely dangerous like & or > +static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); +string SanitizeString(const string& str) +{ + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} const signed char p_util_hexdigit[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, diff --git a/src/util.h b/src/util.h index e52e6986b..7fae5cc7e 100644 --- a/src/util.h +++ b/src/util.h @@ -175,6 +175,7 @@ void ParseString(const std::string& str, char c, std::vector& v); std::string FormatMoney(int64_t n, bool fPlus=false); bool ParseMoney(const std::string& str, int64_t& nRet); bool ParseMoney(const char* pszIn, int64_t& nRet); +std::string SanitizeString(const std::string& str); std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); bool IsHex(const std::string& str); From 358ce2664d27da5bd8ae4c80655368219dcf18b3 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Mon, 28 Oct 2013 16:36:11 +1000 Subject: [PATCH 3/5] New reject p2p message --- src/main.cpp | 169 +++++++++++++++++++++++++++++++++++++-------------- src/main.h | 27 ++++++-- 2 files changed, 146 insertions(+), 50 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2a133b3ea..11f262d9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "util.h" #include +#include #include #include @@ -667,24 +668,30 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction() : vin empty")); + return state.DoS(10, error("CheckTransaction() : vin empty"), + REJECT_INVALID, "vin empty"); if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction() : vout empty")); + return state.DoS(10, error("CheckTransaction() : vout empty"), + REJECT_INVALID, "vout empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"), + REJECT_INVALID, "oversize"); // Check for negative or overflow output values int64_t nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), + REJECT_INVALID, "vout negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), + REJECT_INVALID, "vout too large"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"), + REJECT_INVALID, "txout total too large"); } // Check for duplicate inputs @@ -692,20 +699,23 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); + return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"), + REJECT_INVALID, "duplicate inputs"); vInOutPoints.insert(txin.prevout); } if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction() : coinbase script size")); + return state.DoS(100, error("CheckTransaction() : coinbase script size"), + REJECT_INVALID, "coinbase script too large"); } else { BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction() : prevout is null")); + return state.DoS(10, error("CheckTransaction() : prevout is null"), + REJECT_INVALID, "prevout null"); } return true; @@ -758,13 +768,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx")); + return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"), + REJECT_INVALID, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason)) - return error("AcceptToMemoryPool: : nonstandard transaction: %s", - reason.c_str()); + return state.DoS(0, + error("AcceptToMemoryPool : nonstandard transaction: %s", reason.c_str()), + REJECT_NONSTANDARD, reason); // is it already in the memory pool? uint256 hash = tx.GetHash(); @@ -828,7 +840,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool: : inputs already spent")); + return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), + REJECT_DUPLICATE, "inputs spent"); // Bring the best block into scope view.GetBestBlock(); @@ -851,9 +864,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Don't accept it if it can't get into a block int64_t txMinFee = GetMinFee(tx, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) - return error("AcceptToMemoryPool: : not enough fees %s, %"PRId64" < %"PRId64, - hash.ToString().c_str(), - nFees, txMinFee); + return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %"PRId64" < %"PRId64, + hash.ToString().c_str(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to @@ -873,7 +886,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return error("AcceptToMemoryPool: : free transaction rejected by rate limiter"); + return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), + REJECT_INSUFFICIENTFEE, "insufficient priority"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } @@ -1509,26 +1523,32 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) - return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); + return state.Invalid( + error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight), + REJECT_INVALID, "premature spend of coinbase"); } // Check for negative or overflow input values nValueIn += coins.vout[prevout.n].nValue; if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs() : txin values out of range")); + return state.DoS(100, error("CheckInputs() : txin values out of range"), + REJECT_INVALID, "input values out of range"); } if (nValueIn < GetValueOut(tx)) - return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str())); + return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), + REJECT_INVALID, "in < out"); // Tally transaction fees int64_t nTxFee = nValueIn - GetValueOut(tx); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), + REJECT_INVALID, "fee < 0"); nFees += nTxFee; if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs() : nFees out of range")); + return state.DoS(100, error("CheckInputs() : nFees out of range"), + REJECT_INVALID, "fee out of range"); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -1553,9 +1573,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // encodings or not; if so, don't trigger DoS protection. CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); if (check()) - return state.Invalid(); + return state.Invalid(false, REJECT_NONSTANDARD, "non-canonical"); } - return state.DoS(100,false); + return state.DoS(100,false, REJECT_NONSTANDARD, "non-canonical"); } } } @@ -1723,7 +1743,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C for (unsigned int i = 0; i < block.vtx.size(); i++) { uint256 hash = block.GetTxHash(i); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) - return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); + return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), + REJECT_INVALID, "BIP30"); } } @@ -1752,12 +1773,14 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops"), + REJECT_INVALID, "too many sigops"); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); + return state.DoS(100, error("ConnectBlock() : inputs missing/spent"), + REJECT_INVALID, "inputs missing/spent"); if (fStrictPayToScriptHash) { @@ -1766,7 +1789,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // an incredibly-expensive-to-validate block. nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops"), + REJECT_INVALID, "too many sigops"); } nFees += view.GetValueIn(tx)-GetValueOut(tx); @@ -1790,7 +1814,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) - return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees))); + return state.DoS(100, + error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", + GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)), + REJECT_INVALID, "coinbase too large"); if (!control.Wait()) return state.DoS(100, false); @@ -2161,22 +2188,27 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock() : size limits failed")); + return state.DoS(100, error("CheckBlock() : size limits failed"), + REJECT_INVALID, "block size too large"); // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) - return state.DoS(50, error("CheckBlock() : proof of work failed")); + return state.DoS(50, error("CheckBlock() : proof of work failed"), + REJECT_INVALID, "invalid pow"); // Check timestamp if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); + return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), + REJECT_INVALID, "time in future"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); + return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), + REJECT_INVALID, "no coinbase"); for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : more than one coinbase")); + return state.DoS(100, error("CheckBlock() : more than one coinbase"), + REJECT_INVALID, "duplicate coinbase"); // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2195,7 +2227,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo uniqueTx.insert(block.GetTxHash(i)); } if (uniqueTx.size() != block.vtx.size()) - return state.DoS(100, error("CheckBlock() : duplicate transaction"), true); + return state.DoS(100, error("CheckBlock() : duplicate transaction"), + REJECT_INVALID, "duplicate transaction", true); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2203,11 +2236,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), + REJECT_INVALID, "sig op count", true); // Check merkle root if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back()) - return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), + REJECT_INVALID, "bad merkle root", true); return true; } @@ -2231,20 +2266,24 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block)) - return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); + return state.DoS(100, error("AcceptBlock() : incorrect proof of work"), + REJECT_INVALID, "bad pow"); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); + return state.Invalid(error("AcceptBlock() : block's timestamp is too early"), + REJECT_INVALID, "timestamp too early"); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) - return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); + return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"), + REJECT_INVALID, "non-final tx"); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight), + REJECT_CHECKPOINT, "checkpoint mismatch"); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2) @@ -2252,7 +2291,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { - return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); + return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), + REJECT_OBSOLETE, "version 1 blocks obsolete"); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height @@ -2265,7 +2305,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), + REJECT_INVALID, "height incorrect in coinbase"); } } } @@ -2355,7 +2396,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { - return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"), + REJECT_CHECKPOINT, "timestamp before checkpoint"); } CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); @@ -2363,7 +2405,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { - return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"), + REJECT_INVALID, "invalid pow"); } } @@ -3061,7 +3104,6 @@ bool static AlreadyHave(const CInv& inv) - void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); @@ -3193,6 +3235,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Each connection can only send one version message if (pfrom->nVersion != 0) { + pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); pfrom->Misbehaving(1); return false; } @@ -3206,6 +3249,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // disconnect from peers older than this proto version LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); pfrom->fDisconnect = true; return false; } @@ -3589,8 +3634,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } int nDoS = 0; if (state.IsInvalid(nDoS)) + { + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason(), inv.hash); if (nDoS > 0) pfrom->Misbehaving(nDoS); + } } @@ -3612,8 +3661,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) mapAlreadyAskedFor.erase(inv); int nDoS = 0; if (state.IsInvalid(nDoS)) + { + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason(), inv.hash); if (nDoS > 0) pfrom->Misbehaving(nDoS); + } } @@ -3809,6 +3862,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "reject") + { + if (fDebug) + { + string strMsg; unsigned char ccode; string strReason; + vRecv >> strMsg >> ccode >> strReason; + + ostringstream ss; + ss << strMsg << " code " << itostr(ccode) << ": " << strReason; + + if (strMsg == "block" || strMsg == "tx") + { + uint256 hash; + vRecv >> hash; + ss << ": hash " << hash.ToString(); + } + // Truncate to reasonable length and sanitize before printing: + string s = ss.str(); + if (s.size() > 111) s.erase(111, string::npos); + LogPrint("net", "Reject %s\n", SanitizeString(s).c_str()); + } + } + else { // Ignore unknown commands for extensibility @@ -3907,6 +3983,7 @@ bool ProcessMessages(CNode* pfrom) } catch (std::ios_base::failure& e) { + pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message")); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv diff --git a/src/main.h b/src/main.h index d71780261..460929d62 100644 --- a/src/main.h +++ b/src/main.h @@ -67,6 +67,16 @@ static const int fHaveUPnP = true; static const int fHaveUPnP = false; #endif +/** "reject" message codes **/ +static const unsigned char REJECT_MALFORMED = 0x01; +static const unsigned char REJECT_INVALID = 0x10; +static const unsigned char REJECT_OBSOLETE = 0x11; +static const unsigned char REJECT_DUPLICATE = 0x12; +static const unsigned char REJECT_NONSTANDARD = 0x40; +static const unsigned char REJECT_DUST = 0x41; +static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; +static const unsigned char REJECT_CHECKPOINT = 0x43; + extern CScript COINBASE_FLAGS; @@ -926,19 +936,26 @@ private: MODE_ERROR, // run-time error } mode; int nDoS; + std::string strRejectReason; + unsigned char chRejectCode; bool corruptionPossible; public: CValidationState() : mode(MODE_VALID), nDoS(0) {} - bool DoS(int level, bool ret = false, bool corruptionIn = false) { + bool DoS(int level, bool ret = false, + unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="", + bool corruptionIn=false) { + chRejectCode = chRejectCodeIn; + strRejectReason = strRejectReasonIn; + corruptionPossible = corruptionIn; if (mode == MODE_ERROR) return ret; nDoS += level; mode = MODE_INVALID; - corruptionPossible = corruptionIn; return ret; } - bool Invalid(bool ret = false) { - return DoS(0, ret); + bool Invalid(bool ret = false, + unsigned char _chRejectCode=0, std::string _strRejectReason="") { + return DoS(0, ret, _chRejectCode, _strRejectReason); } bool Error() { mode = MODE_ERROR; @@ -967,6 +984,8 @@ public: bool CorruptionPossible() { return corruptionPossible; } + unsigned char GetRejectCode() const { return chRejectCode; } + std::string GetRejectReason() const { return strRejectReason; } }; /** An in-memory indexed chain of blocks. */ From feaec80cb033bd3db754c783ad483fc67751ec92 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sun, 3 Nov 2013 14:46:34 +1000 Subject: [PATCH 4/5] Test alerts high at high PROTOCOL_VERSIONs I regenerated the alert test data; now alerts are tested against a protocol version way above the current protocol version. So we won't have to regenerate them every time we bump PROTOCOL_VERSION in the future. --- src/test/alert_tests.cpp | 10 +++++----- src/test/data/alertTests.raw | Bin 1283 -> 1279 bytes 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index adfbec9de..a7e668222 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -27,7 +27,7 @@ alert.nID = 1; alert.nCancel = 0; // cancels previous messages up to this ID number alert.nMinVer = 0; // These versions are protocol versions - alert.nMaxVer = 70001; + alert.nMaxVer = 999001; alert.nPriority = 1; alert.strComment = "Alert comment"; alert.strStatusBar = "Alert 1"; @@ -121,25 +121,25 @@ BOOST_AUTO_TEST_CASE(AlertApplies) // Matches: BOOST_CHECK(alerts[0].AppliesTo(1, "")); - BOOST_CHECK(alerts[0].AppliesTo(70001, "")); + BOOST_CHECK(alerts[0].AppliesTo(999001, "")); BOOST_CHECK(alerts[0].AppliesTo(1, "/Satoshi:11.11.11/")); BOOST_CHECK(alerts[1].AppliesTo(1, "/Satoshi:0.1.0/")); - BOOST_CHECK(alerts[1].AppliesTo(70001, "/Satoshi:0.1.0/")); + BOOST_CHECK(alerts[1].AppliesTo(999001, "/Satoshi:0.1.0/")); BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.1.0/")); BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.2.0/")); // Don't match: BOOST_CHECK(!alerts[0].AppliesTo(-1, "")); - BOOST_CHECK(!alerts[0].AppliesTo(70002, "")); + BOOST_CHECK(!alerts[0].AppliesTo(999002, "")); BOOST_CHECK(!alerts[1].AppliesTo(1, "")); BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0")); BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.1.0")); BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0/")); BOOST_CHECK(!alerts[1].AppliesTo(-1, "/Satoshi:0.1.0/")); - BOOST_CHECK(!alerts[1].AppliesTo(70002, "/Satoshi:0.1.0/")); + BOOST_CHECK(!alerts[1].AppliesTo(999002, "/Satoshi:0.1.0/")); BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.2.0/")); BOOST_CHECK(!alerts[2].AppliesTo(1, "/Satoshi:0.3.0/")); diff --git a/src/test/data/alertTests.raw b/src/test/data/alertTests.raw index 7fc45289617e1c4b0e40f2244ffa3fab0f9849ce..01f50680b95aa307a1a013b643ded4ae0bc47162 100644 GIT binary patch delta 633 zcmV-<0*3vA3jYa^A_G}I50ND!LxqfWw;S@{)&Y3;w^u{1?sDfliP4PDpAZjs4RU^d z$pRoRm4V=6zXU+&h_r9*Z*vFWf891fmfN&4Kyq3Cs&|lMkpUqCSw0VuuS7!{+0(`k zNhYa2rUCcr@YTUv-uVBvRuUCK+WJJk2}+CtAPpN_I@E8v=tZuFUintl&l~8)aUp@ zUm`BbuD&(`ATf^=g87GZ(T&>tjU1NZDX5|vVZWm)ebMJ7*F~UCHcXKL6q5}C4Fg#| z50f1NC`U#xL;@f#*Rq=*q!kH8@OG&`dnyHyemm`c1i!uG*D+PovaX~8AU8w2H$Nvs zT41Ja^PbA~V7ZWHz0Ctqm^7@&R%H-bCzFi=A_G}I50f1NE<%Tu{q|EZ-MQ$8_;$cx zXW3%=diB+jEX?@on6wyO(;xyMMh~mW!Ai!fEbt3iQ6suCpTF6jf3GSzyEz2H)+y87 zlOh9CLb*4;UE*aml`oG+jA3)t8#ezUM-sC&(S>fFnRu}6<^mv86DIjio&imfTihyR zn`96x8FAL!*$D7xp_ T10|D9eAIQ$P>(A*sE>gw!q^=z delta 642 zcmV-|0)74e34;odA_H*|0g)vmL+htH7>)1Q{y(RoB#U1{>kGL9+u-8|9tjGY!CrQT zT>>C8K(nRz0Y<|6enA-JkRcVc_RW|i_j~dsdj2l(2G&1gkpUqCaS;KLuS7#^(@yLB zR(ZYvJSK=(Z61LBjAuk}x14Me{n##^fdrWXAVUgO%gE{!_*x_c)#|UHOOp6^-Tfm= zUU(+TEet(j{(6xCAp>y{0h3w*R!K)NMFJrJw%uVXpB?YimKR*VHP#qBVIrVVHBc8t zjB_KX45;aB0w7rKTlf@1RMN8ZL!8n)I6Tt}CKeXexc*XKxLn^A{B2B;0Th!A0u2Lk z5do910xm+2K^<36@f87eG1d>cDT|;7sv@fd#0Sqk85|ic+ODMnAR)JXDu1fGSzxEX z)ioQYHl#$uSJzQs5J;o5yICWunUf#`6_cO>4Fhoz0h6!-EJPqfFo#w7iEId|Uw#ve z{gRuU%*27%g?;~iM5(O6?eOvfApq=1l#L2aQ(i&Qz-6=v|D{D;)6z6TYXGnP@CW&t z&&`t{15-oif4+xtQ@%Fto%@X-xV3=Guc=|0(c`EM-=`24DsDmoAPjA2;OuV!f_OGz zyAQehQVVxJ`|X0(@0$erBUm$)kYke}0~C|W15ZgvFh&9)0KpH84eNq)+R+A6$YHs4 zHc!9zLNQ|W&FYNLLf!buumT|f%7<~PuiIpC&UG`>4L`!`cL?Pehx8#sv$ldoj!)yA zlVJo`NJlV50wDm1e?x&mZJ!(2rZ~mCL7)G;=?9#a&guEhBUzdM&e0bFAVlzeKw%R0 cq{30c4JI;%y3T^xogr#?WGlZP*3{cL1TPIA=>Px# From 69aada346f74c978b5d8be59a5d7c79be966ef8c Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sun, 3 Nov 2013 11:42:38 +1000 Subject: [PATCH 5/5] Bump protocol version to 70002 --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 9ae929c0a..3d1abacb9 100644 --- a/src/version.h +++ b/src/version.h @@ -26,7 +26,7 @@ extern const std::string CLIENT_DATE; // network protocol versioning // -static const int PROTOCOL_VERSION = 70001; +static const int PROTOCOL_VERSION = 70002; // intial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209;