@@ -443,26 +443,28 @@ void CNode::PushVersion()
443
443
444
444
445
445
446
- std::map<CSubNet, int64_t > CNode::setBanned;
446
+ banmap_t CNode::setBanned;
447
447
CCriticalSection CNode::cs_setBanned;
448
+ bool CNode::setBannedIsDirty;
448
449
449
450
void CNode::ClearBanned ()
450
451
{
451
452
LOCK (cs_setBanned);
452
453
setBanned.clear ();
454
+ setBannedIsDirty = true ;
453
455
}
454
456
455
457
bool CNode::IsBanned (CNetAddr ip)
456
458
{
457
459
bool fResult = false ;
458
460
{
459
461
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++)
461
463
{
462
464
CSubNet subNet = (*it).first ;
463
- int64_t t = (*it).second ;
465
+ CBanEntry banEntry = (*it).second ;
464
466
465
- if (subNet.Match (ip) && GetTime () < t )
467
+ if (subNet.Match (ip) && GetTime () < banEntry. nBanUntil )
466
468
fResult = true ;
467
469
}
468
470
}
@@ -474,50 +476,99 @@ bool CNode::IsBanned(CSubNet subnet)
474
476
bool fResult = false ;
475
477
{
476
478
LOCK (cs_setBanned);
477
- std::map<CSubNet, int64_t > ::iterator i = setBanned.find (subnet);
479
+ banmap_t ::iterator i = setBanned.find (subnet);
478
480
if (i != setBanned.end ())
479
481
{
480
- int64_t t = (*i).second ;
481
- if (GetTime () < t )
482
+ CBanEntry banEntry = (*i).second ;
483
+ if (GetTime () < banEntry. nBanUntil )
482
484
fResult = true ;
483
485
}
484
486
}
485
487
return fResult ;
486
488
}
487
489
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);
491
493
}
492
494
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
+
497
505
498
506
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 ;
501
511
}
502
512
503
513
bool CNode::Unban (const CNetAddr &addr) {
504
- CSubNet subNet (addr. ToString ()+(addr. IsIPv4 () ? " /32 " : " /128 " ) );
514
+ CSubNet subNet (addr);
505
515
return Unban (subNet);
506
516
}
507
517
508
518
bool CNode::Unban (const CSubNet &subNet) {
509
519
LOCK (cs_setBanned);
510
520
if (setBanned.erase (subNet))
521
+ {
522
+ setBannedIsDirty = true ;
511
523
return true ;
524
+ }
512
525
return false ;
513
526
}
514
527
515
- void CNode::GetBanned (std::map<CSubNet, int64_t > &banMap)
528
+ void CNode::GetBanned (banmap_t &banMap)
516
529
{
517
530
LOCK (cs_setBanned);
518
531
banMap = setBanned; // create a thread safe copy
519
532
}
520
533
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
+
521
572
522
573
std::vector<CSubNet> CNode::vWhitelistedRange;
523
574
CCriticalSection CNode::cs_vWhitelistedRange;
@@ -1212,6 +1263,17 @@ void DumpAddresses()
1212
1263
addrman.size (), GetTimeMillis () - nStart);
1213
1264
}
1214
1265
1266
+ void DumpData ()
1267
+ {
1268
+ DumpAddresses ();
1269
+
1270
+ if (CNode::BannedSetIsDirty ())
1271
+ {
1272
+ DumpBanlist ();
1273
+ CNode::SetBannedSetDirty (false );
1274
+ }
1275
+ }
1276
+
1215
1277
void static ProcessOneShot ()
1216
1278
{
1217
1279
string strDest;
@@ -1650,6 +1712,17 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
1650
1712
if (!adb.Read (addrman))
1651
1713
LogPrintf (" Invalid or missing peers.dat; recreating\n " );
1652
1714
}
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
+
1653
1726
LogPrintf (" Loaded %i addresses from peers.dat %dms\n " ,
1654
1727
addrman.size (), GetTimeMillis () - nStart);
1655
1728
fAddressesInitialized = true ;
@@ -1690,7 +1763,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
1690
1763
threadGroup.create_thread (boost::bind (&TraceThread<void (*)()>, " msghand" , &ThreadMessageHandler));
1691
1764
1692
1765
// Dump network addresses
1693
- scheduler.scheduleEvery (&DumpAddresses , DUMP_ADDRESSES_INTERVAL);
1766
+ scheduler.scheduleEvery (&DumpData , DUMP_ADDRESSES_INTERVAL);
1694
1767
}
1695
1768
1696
1769
bool StopNode ()
@@ -1703,7 +1776,7 @@ bool StopNode()
1703
1776
1704
1777
if (fAddressesInitialized )
1705
1778
{
1706
- DumpAddresses ();
1779
+ DumpData ();
1707
1780
fAddressesInitialized = false ;
1708
1781
}
1709
1782
@@ -1907,11 +1980,11 @@ bool CAddrDB::Read(CAddrMan& addr)
1907
1980
return error (" %s: Failed to open file %s" , __func__, pathAddr.string ());
1908
1981
1909
1982
// 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 ;
1912
1985
// 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) ;
1915
1988
vector<unsigned char > vchData;
1916
1989
vchData.resize (dataSize);
1917
1990
uint256 hashIn;
@@ -2107,3 +2180,119 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
2107
2180
2108
2181
LEAVE_CRITICAL_SECTION (cs_vSend);
2109
2182
}
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