Browse Source

Merge pull request #3185 from gavinandresen/reject

Reject p2p message
0.10
Gavin Andresen 11 years ago
parent
commit
16d5f2c5e0
  1. 10
      src/alert.cpp
  2. 169
      src/main.cpp
  3. 27
      src/main.h
  4. 10
      src/netbase.cpp
  5. 10
      src/test/alert_tests.cpp
  6. BIN
      src/test/data/alertTests.raw
  7. 13
      src/util.cpp
  8. 1
      src/util.h
  9. 2
      src/version.h

10
src/alert.cpp

@ -241,15 +241,7 @@ bool CAlert::ProcessAlert(bool fThread) @@ -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);

169
src/main.cpp

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
#include "util.h"
#include <inttypes.h>
#include <sstream>
#include <stdint.h>
#include <boost/algorithm/string/replace.hpp>
@ -667,24 +668,30 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) @@ -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) @@ -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) @@ -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 @@ -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 @@ -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) @@ -3061,7 +3104,6 @@ bool static AlreadyHave(const CInv& inv)
void static ProcessGetData(CNode* pfrom)
{
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
@ -3193,6 +3235,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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

27
src/main.h

@ -67,6 +67,16 @@ static const int fHaveUPnP = true; @@ -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: @@ -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: @@ -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. */

10
src/netbase.cpp

@ -367,13 +367,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe @@ -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 @@ -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 @@ -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;
}

10
src/test/alert_tests.cpp

@ -27,7 +27,7 @@ @@ -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) @@ -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/"));

BIN
src/test/data/alertTests.raw

Binary file not shown.

13
src/util.cpp

@ -475,6 +475,19 @@ bool ParseMoney(const char* pszIn, int64_t& nRet) @@ -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,

1
src/util.h

@ -175,6 +175,7 @@ void ParseString(const std::string& str, char c, std::vector<std::string>& v); @@ -175,6 +175,7 @@ void ParseString(const std::string& str, char c, std::vector<std::string>& 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<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
bool IsHex(const std::string& str);

2
src/version.h

@ -26,7 +26,7 @@ extern const std::string CLIENT_DATE; @@ -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;

Loading…
Cancel
Save