aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2013-11-10 17:11:39 -0800
committerGavin Andresen <gavinandresen@gmail.com>2013-11-10 17:11:39 -0800
commit16d5f2c5e0a7276fb2b09d8dd51b7bac2753c4cc (patch)
treee2abbe951c7b5533452ed8459c6bf7539c5caab7 /src
parent3a6faabecf719633481253a0d2f874494ad126a5 (diff)
parent69aada346f74c978b5d8be59a5d7c79be966ef8c (diff)
Merge pull request #3185 from gavinandresen/reject
Reject p2p message
Diffstat (limited to 'src')
-rw-r--r--src/alert.cpp10
-rw-r--r--src/main.cpp169
-rw-r--r--src/main.h27
-rw-r--r--src/netbase.cpp10
-rw-r--r--src/test/alert_tests.cpp10
-rw-r--r--src/test/data/alertTests.rawbin1283 -> 1279 bytes
-rw-r--r--src/util.cpp13
-rw-r--r--src/util.h1
-rw-r--r--src/version.h2
9 files changed, 172 insertions, 70 deletions
diff --git a/src/alert.cpp b/src/alert.cpp
index b900fe41e5..7f7e59ee10 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/main.cpp b/src/main.cpp
index 2a133b3eae..11f262d9cb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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)
{
// 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<CInv>::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 d717802618..460929d624 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. */
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 88c58f854e..1392fa8233 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;
}
diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp
index adfbec9dec..a7e668222d 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 7fc4528961..01f50680b9 100644
--- a/src/test/data/alertTests.raw
+++ b/src/test/data/alertTests.raw
Binary files differ
diff --git a/src/util.cpp b/src/util.cpp
index 9562cf310a..5411bb2fe3 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 e52e6986b5..7fae5cc7ec 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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);
diff --git a/src/version.h b/src/version.h
index 9ae929c0ac..3d1abacb94 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;