@@ -275,6 +275,89 @@ struct CBlockReject {
275275 uint256 hashBlock;
276276};
277277
278+ class CNodeHeaders
279+ {
280+ public:
281+ CNodeHeaders ():
282+ maxSize (0 ),
283+ maxAvg (0 )
284+ {
285+ maxSize = GetArg (" -headerspamfiltermaxsize" , DEFAULT_HEADER_SPAM_FILTER_MAX_SIZE);
286+ maxAvg = GetArg (" -headerspamfiltermaxavg" , DEFAULT_HEADER_SPAM_FILTER_MAX_AVG);
287+ }
288+
289+ bool addHeaders (int nBegin, int nEnd)
290+ {
291+
292+ if (nBegin > 0 && nEnd > 0 && maxSize && maxAvg)
293+ {
294+
295+ for (int point = nBegin; point<= nEnd; point++)
296+ {
297+ addPoint (point);
298+ }
299+
300+ return true ;
301+ }
302+
303+ return false ;
304+ }
305+
306+ bool updateState (CValidationState& state, bool ret)
307+ {
308+ // No headers
309+ size_t size = points.size ();
310+ if (size == 0 )
311+ return ret;
312+
313+ // Compute the number of the received headers
314+ size_t nHeaders = 0 ;
315+ for (auto point : points)
316+ {
317+ nHeaders += point.second ;
318+ }
319+
320+ // Compute the average value per height
321+ double nAvgValue = (double )nHeaders / size;
322+
323+ // Ban the node if try to spam
324+ bool banNode = (nAvgValue >= 1.5 * maxAvg && size >= maxAvg) ||
325+ (nAvgValue >= maxAvg && nHeaders >= maxSize) ||
326+ (nHeaders >= maxSize * 3 );
327+ if (banNode)
328+ {
329+ // Clear the points and ban the node
330+ points.clear ();
331+ return state.DoS (100 , false , REJECT_INVALID, " header-spam" , false , " ban node for sending spam" );
332+ }
333+
334+ return ret;
335+ }
336+
337+ private:
338+ void addPoint (int height)
339+ {
340+ // Erace the last element in the list
341+ if (points.size () == maxSize)
342+ {
343+ points.erase (points.begin ());
344+ }
345+
346+ // Add the point to the list
347+ int occurrence = 0 ;
348+ auto mi = points.find (height);
349+ if (mi != points.end ())
350+ occurrence = (*mi).second ;
351+ occurrence++;
352+ points[height] = occurrence;
353+ }
354+
355+ private:
356+ std::map<int ,int > points;
357+ size_t maxSize;
358+ size_t maxAvg;
359+ };
360+
278361/* *
279362 * Maintain validation-specific state about nodes, protected by cs_main, instead
280363 * by CNode's own locks. This simplifies asynchronous operation, where
@@ -324,6 +407,8 @@ struct CNodeState {
324407 // ! Whether this peer can give us witnesses
325408 bool fHaveWitness ;
326409
410+ CNodeHeaders headers;
411+
327412 CNodeState () {
328413 fCurrentlyConnected = false ;
329414 nMisbehavior = 0 ;
@@ -7194,25 +7279,63 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
71947279 return true ;
71957280 }
71967281
7282+ bool ret = true ;
7283+ bool bFirst = true ;
7284+ string strError = " " ;
7285+
7286+ int nFirst = 0 ;
7287+ int nLast = 0 ;
7288+
71977289 CBlockIndex *pindexLast = NULL ;
7290+
71987291 BOOST_FOREACH (const CBlock& header, headers) {
71997292 CValidationState state;
72007293 if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash ()) {
72017294 Misbehaving (pfrom->GetId (), 20 );
7202- return error (" non-continuous headers sequence" );
7295+ ret = false ;
7296+ strError = " non-continuous headers sequence" ;
7297+ break ;
72037298 }
72047299 CBlockHeader pblockheader = CBlockHeader (header);
72057300 if (!AcceptBlockHeader (pblockheader, state, chainparams, &pindexLast)) {
7206-
72077301 int nDoS;
72087302 if (state.IsInvalid (nDoS)) {
72097303 if (nDoS > 0 )
72107304 Misbehaving (pfrom->GetId (), nDoS);
7211- return error (" invalid header received" );
7305+ ret = false ;
7306+ strError = " invalid header received" ;
7307+ break ;
72127308 }
72137309 }
7310+ if (pindexLast) {
7311+ nLast = pindexLast->nHeight ;
7312+ if (bFirst){
7313+ nFirst = pindexLast->nHeight ;
7314+ bFirst = false ;
7315+ }
7316+ }
7317+ }
7318+
7319+ if (GetBoolArg (" -headerspamfilter" , DEFAULT_HEADER_SPAM_FILTER))
7320+ {
7321+ LOCK (cs_main);
7322+ CValidationState state;
7323+ CNodeState *nodestate = State (pfrom->GetId ());
7324+ nodestate->headers .addHeaders (nFirst, nLast);
7325+ int nDoS;
7326+ ret = nodestate->headers .updateState (state, ret);
7327+ if (state.IsInvalid (nDoS)) {
7328+ if (nDoS > 0 )
7329+ Misbehaving (pfrom->GetId (), nDoS);
7330+ ret = false ;
7331+ strError = strError!=" " ? strError + " / " : " " ;
7332+ strError = " header spam protection" ;
7333+ }
72147334 }
72157335
7336+ if (!ret)
7337+ return error (strError.c_str ());
7338+
72167339 if (nodestate->nUnconnectingHeaders > 0 ) {
72177340 LogPrint (" net" , " peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n " , pfrom->id , nodestate->nUnconnectingHeaders );
72187341 }
0 commit comments