@@ -443,26 +443,28 @@ void CNode::PushVersion()
443443
444444
445445
446- std::map<CSubNet, int64_t > CNode::setBanned;
446+ banmap_t CNode::setBanned;
447447CCriticalSection CNode::cs_setBanned;
448+ bool CNode::setBannedIsDirty;
448449
449450void CNode::ClearBanned ()
450451{
451452 LOCK (cs_setBanned);
452453 setBanned.clear ();
454+ setBannedIsDirty = true ;
453455}
454456
455457bool CNode::IsBanned (CNetAddr ip)
456458{
457459 bool fResult = false ;
458460 {
459461 LOCK (cs_setBanned);
460- for (std::map<CSubNet, int64_t > ::iterator it = setBanned.begin (); it != setBanned.end (); it++)
462+ for (banmap_t ::iterator it = setBanned.begin (); it != setBanned.end (); it++)
461463 {
462464 CSubNet subNet = (*it).first ;
463- int64_t t = (*it).second ;
465+ CBanEntry banEntry = (*it).second ;
464466
465- if (subNet.Match (ip) && GetTime () < t )
467+ if (subNet.Match (ip) && GetTime () < banEntry. nBanUntil )
466468 fResult = true ;
467469 }
468470 }
@@ -474,50 +476,99 @@ bool CNode::IsBanned(CSubNet subnet)
474476 bool fResult = false ;
475477 {
476478 LOCK (cs_setBanned);
477- std::map<CSubNet, int64_t > ::iterator i = setBanned.find (subnet);
479+ banmap_t ::iterator i = setBanned.find (subnet);
478480 if (i != setBanned.end ())
479481 {
480- int64_t t = (*i).second ;
481- if (GetTime () < t )
482+ CBanEntry banEntry = (*i).second ;
483+ if (GetTime () < banEntry. nBanUntil )
482484 fResult = true ;
483485 }
484486 }
485487 return fResult ;
486488}
487489
488- void CNode::Ban (const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) {
489- CSubNet subNet (addr. ToString ()+(addr. IsIPv4 () ? " /32 " : " /128 " ) );
490- Ban (subNet, bantimeoffset, sinceUnixEpoch);
490+ void CNode::Ban (const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
491+ CSubNet subNet (addr);
492+ Ban (subNet, banReason, bantimeoffset, sinceUnixEpoch);
491493}
492494
493- void CNode::Ban (const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) {
494- int64_t banTime = GetTime ()+GetArg (" -bantime" , 60 *60 *24 ); // Default 24-hour ban
495- if (bantimeoffset > 0 )
496- banTime = (sinceUnixEpoch ? 0 : GetTime () )+bantimeoffset;
495+ void CNode::Ban (const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
496+ CBanEntry banEntry (GetTime ());
497+ banEntry.banReason = banReason;
498+ if (bantimeoffset <= 0 )
499+ {
500+ bantimeoffset = GetArg (" -bantime" , 60 *60 *24 ); // Default 24-hour ban
501+ sinceUnixEpoch = false ;
502+ }
503+ banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime () )+bantimeoffset;
504+
497505
498506 LOCK (cs_setBanned);
499- if (setBanned[subNet] < banTime)
500- setBanned[subNet] = banTime;
507+ if (setBanned[subNet].nBanUntil < banEntry.nBanUntil )
508+ setBanned[subNet] = banEntry;
509+
510+ setBannedIsDirty = true ;
501511}
502512
503513bool CNode::Unban (const CNetAddr &addr) {
504- CSubNet subNet (addr. ToString ()+(addr. IsIPv4 () ? " /32 " : " /128 " ) );
514+ CSubNet subNet (addr);
505515 return Unban (subNet);
506516}
507517
508518bool CNode::Unban (const CSubNet &subNet) {
509519 LOCK (cs_setBanned);
510520 if (setBanned.erase (subNet))
521+ {
522+ setBannedIsDirty = true ;
511523 return true ;
524+ }
512525 return false ;
513526}
514527
515- void CNode::GetBanned (std::map<CSubNet, int64_t > &banMap)
528+ void CNode::GetBanned (banmap_t &banMap)
516529{
517530 LOCK (cs_setBanned);
518531 banMap = setBanned; // create a thread safe copy
519532}
520533
534+ void CNode::SetBanned (const banmap_t &banMap)
535+ {
536+ LOCK (cs_setBanned);
537+ setBanned = banMap;
538+ setBannedIsDirty = true ;
539+ }
540+
541+ void CNode::SweepBanned ()
542+ {
543+ int64_t now = GetTime ();
544+
545+ LOCK (cs_setBanned);
546+ banmap_t ::iterator it = setBanned.begin ();
547+ while (it != setBanned.end ())
548+ {
549+ CBanEntry banEntry = (*it).second ;
550+ if (now > banEntry.nBanUntil )
551+ {
552+ setBanned.erase (it++);
553+ setBannedIsDirty = true ;
554+ }
555+ else
556+ ++it;
557+ }
558+ }
559+
560+ bool CNode::BannedSetIsDirty ()
561+ {
562+ LOCK (cs_setBanned);
563+ return setBannedIsDirty;
564+ }
565+
566+ void CNode::SetBannedSetDirty (bool dirty)
567+ {
568+ LOCK (cs_setBanned); // reuse setBanned lock for the isDirty flag
569+ setBannedIsDirty = dirty;
570+ }
571+
521572
522573std::vector<CSubNet> CNode::vWhitelistedRange;
523574CCriticalSection CNode::cs_vWhitelistedRange;
@@ -1212,6 +1263,17 @@ void DumpAddresses()
12121263 addrman.size (), GetTimeMillis () - nStart);
12131264}
12141265
1266+ void DumpData ()
1267+ {
1268+ DumpAddresses ();
1269+
1270+ if (CNode::BannedSetIsDirty ())
1271+ {
1272+ DumpBanlist ();
1273+ CNode::SetBannedSetDirty (false );
1274+ }
1275+ }
1276+
12151277void static ProcessOneShot ()
12161278{
12171279 string strDest;
@@ -1650,6 +1712,17 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
16501712 if (!adb.Read (addrman))
16511713 LogPrintf (" Invalid or missing peers.dat; recreating\n " );
16521714 }
1715+
1716+ // try to read stored banlist
1717+ CBanDB bandb;
1718+ banmap_t banmap;
1719+ if (!bandb.Read (banmap))
1720+ LogPrintf (" Invalid or missing banlist.dat; recreating\n " );
1721+
1722+ CNode::SetBanned (banmap); // thread save setter
1723+ CNode::SetBannedSetDirty (false ); // no need to write down just read or nonexistent data
1724+ CNode::SweepBanned (); // sweap out unused entries
1725+
16531726 LogPrintf (" Loaded %i addresses from peers.dat %dms\n " ,
16541727 addrman.size (), GetTimeMillis () - nStart);
16551728 fAddressesInitialized = true ;
@@ -1690,7 +1763,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
16901763 threadGroup.create_thread (boost::bind (&TraceThread<void (*)()>, " msghand" , &ThreadMessageHandler));
16911764
16921765 // Dump network addresses
1693- scheduler.scheduleEvery (&DumpAddresses , DUMP_ADDRESSES_INTERVAL);
1766+ scheduler.scheduleEvery (&DumpData , DUMP_ADDRESSES_INTERVAL);
16941767}
16951768
16961769bool StopNode ()
@@ -1703,7 +1776,7 @@ bool StopNode()
17031776
17041777 if (fAddressesInitialized )
17051778 {
1706- DumpAddresses ();
1779+ DumpData ();
17071780 fAddressesInitialized = false ;
17081781 }
17091782
@@ -1907,11 +1980,11 @@ bool CAddrDB::Read(CAddrMan& addr)
19071980 return error (" %s: Failed to open file %s" , __func__, pathAddr.string ());
19081981
19091982 // use file size to size memory buffer
1910- int fileSize = boost::filesystem::file_size (pathAddr);
1911- int dataSize = fileSize - sizeof (uint256) ;
1983+ uint64_t fileSize = boost::filesystem::file_size (pathAddr);
1984+ uint64_t dataSize = 0 ;
19121985 // Don't try to resize to a negative number if file is small
1913- if (dataSize < 0 )
1914- dataSize = 0 ;
1986+ if (fileSize >= sizeof (uint256) )
1987+ dataSize = fileSize - sizeof (uint256) ;
19151988 vector<unsigned char > vchData;
19161989 vchData.resize (dataSize);
19171990 uint256 hashIn;
@@ -2107,3 +2180,119 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
21072180
21082181 LEAVE_CRITICAL_SECTION (cs_vSend);
21092182}
2183+
2184+ //
2185+ // CBanDB
2186+ //
2187+
2188+ CBanDB::CBanDB ()
2189+ {
2190+ pathBanlist = GetDataDir () / " banlist.dat" ;
2191+ }
2192+
2193+ bool CBanDB::Write (const banmap_t & banSet)
2194+ {
2195+ // Generate random temporary filename
2196+ unsigned short randv = 0 ;
2197+ GetRandBytes ((unsigned char *)&randv, sizeof (randv));
2198+ std::string tmpfn = strprintf (" banlist.dat.%04x" , randv);
2199+
2200+ // serialize banlist, checksum data up to that point, then append csum
2201+ CDataStream ssBanlist (SER_DISK, CLIENT_VERSION);
2202+ ssBanlist << FLATDATA (Params ().MessageStart ());
2203+ ssBanlist << banSet;
2204+ uint256 hash = Hash (ssBanlist.begin (), ssBanlist.end ());
2205+ ssBanlist << hash;
2206+
2207+ // open temp output file, and associate with CAutoFile
2208+ boost::filesystem::path pathTmp = GetDataDir () / tmpfn;
2209+ FILE *file = fopen (pathTmp.string ().c_str (), " wb" );
2210+ CAutoFile fileout (file, SER_DISK, CLIENT_VERSION);
2211+ if (fileout.IsNull ())
2212+ return error (" %s: Failed to open file %s" , __func__, pathTmp.string ());
2213+
2214+ // Write and commit header, data
2215+ try {
2216+ fileout << ssBanlist;
2217+ }
2218+ catch (const std::exception& e) {
2219+ return error (" %s: Serialize or I/O error - %s" , __func__, e.what ());
2220+ }
2221+ FileCommit (fileout.Get ());
2222+ fileout.fclose ();
2223+
2224+ // replace existing banlist.dat, if any, with new banlist.dat.XXXX
2225+ if (!RenameOver (pathTmp, pathBanlist))
2226+ return error (" %s: Rename-into-place failed" , __func__);
2227+
2228+ return true ;
2229+ }
2230+
2231+ bool CBanDB::Read (banmap_t & banSet)
2232+ {
2233+ // open input file, and associate with CAutoFile
2234+ FILE *file = fopen (pathBanlist.string ().c_str (), " rb" );
2235+ CAutoFile filein (file, SER_DISK, CLIENT_VERSION);
2236+ if (filein.IsNull ())
2237+ return error (" %s: Failed to open file %s" , __func__, pathBanlist.string ());
2238+
2239+ // use file size to size memory buffer
2240+ uint64_t fileSize = boost::filesystem::file_size (pathBanlist);
2241+ uint64_t dataSize = 0 ;
2242+ // Don't try to resize to a negative number if file is small
2243+ if (fileSize >= sizeof (uint256))
2244+ dataSize = fileSize - sizeof (uint256);
2245+ vector<unsigned char > vchData;
2246+ vchData.resize (dataSize);
2247+ uint256 hashIn;
2248+
2249+ // read data and checksum from file
2250+ try {
2251+ filein.read ((char *)&vchData[0 ], dataSize);
2252+ filein >> hashIn;
2253+ }
2254+ catch (const std::exception& e) {
2255+ return error (" %s: Deserialize or I/O error - %s" , __func__, e.what ());
2256+ }
2257+ filein.fclose ();
2258+
2259+ CDataStream ssBanlist (vchData, SER_DISK, CLIENT_VERSION);
2260+
2261+ // verify stored checksum matches input data
2262+ uint256 hashTmp = Hash (ssBanlist.begin (), ssBanlist.end ());
2263+ if (hashIn != hashTmp)
2264+ return error (" %s: Checksum mismatch, data corrupted" , __func__);
2265+
2266+ unsigned char pchMsgTmp[4 ];
2267+ try {
2268+ // de-serialize file header (network specific magic number) and ..
2269+ ssBanlist >> FLATDATA (pchMsgTmp);
2270+
2271+ // ... verify the network matches ours
2272+ if (memcmp (pchMsgTmp, Params ().MessageStart (), sizeof (pchMsgTmp)))
2273+ return error (" %s: Invalid network magic number" , __func__);
2274+
2275+ // de-serialize address data into one CAddrMan object
2276+ ssBanlist >> banSet;
2277+ }
2278+ catch (const std::exception& e) {
2279+ return error (" %s: Deserialize or I/O error - %s" , __func__, e.what ());
2280+ }
2281+
2282+ return true ;
2283+ }
2284+
2285+ void DumpBanlist ()
2286+ {
2287+ int64_t nStart = GetTimeMillis ();
2288+
2289+ CNode::SweepBanned (); // clean unused entires (if bantime has expired)
2290+
2291+ CBanDB bandb;
2292+ banmap_t banmap;
2293+ CNode::GetBanned (banmap);
2294+ bandb.Write (banmap);
2295+
2296+ LogPrint (" net" , " Flushed %d banned node ips/subnets to banlist.dat %dms\n " ,
2297+ banmap.size (), GetTimeMillis () - nStart);
2298+ }
0 commit comments