Skip to content

Commit 66e5465

Browse files
committed
Merge pull request #6310
177a0e4 Adding CSubNet constructor over a single CNetAddr (Jonas Schnelli) 409bccf use CBanEntry as object container for banned nodes (Jonas Schnelli) dfa174c CAddrDB/CBanDB: change filesize variables from int to uint64_t (Jonas Schnelli) f581d3d banlist.dat: store banlist on disk (Jonas Schnelli)
2 parents 3f16971 + 177a0e4 commit 66e5465

File tree

9 files changed

+359
-36
lines changed

9 files changed

+359
-36
lines changed

qa/rpc-tests/nodehandling.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,25 @@ def run_test(self):
4848
assert_equal(len(self.nodes[2].listbanned()), 0)
4949
self.nodes[2].clearbanned()
5050
assert_equal(len(self.nodes[2].listbanned()), 0)
51-
51+
52+
##test persisted banlist
53+
self.nodes[2].setban("127.0.0.0/32", "add")
54+
self.nodes[2].setban("127.0.0.0/24", "add")
55+
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
56+
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
57+
listBeforeShutdown = self.nodes[2].listbanned();
58+
assert_equal("192.168.0.1/255.255.255.255", listBeforeShutdown[2]['address']) #must be here
59+
time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
60+
61+
#stop node
62+
stop_node(self.nodes[2], 2)
63+
64+
self.nodes[2] = start_node(2, self.options.tmpdir)
65+
listAfterShutdown = self.nodes[2].listbanned();
66+
assert_equal("127.0.0.0/255.255.255.0", listAfterShutdown[0]['address'])
67+
assert_equal("127.0.0.0/255.255.255.255", listAfterShutdown[1]['address'])
68+
assert_equal("2001:4000::/ffff:e000:0:0:0:0:0:0", listAfterShutdown[2]['address'])
69+
5270
###########################
5371
# RPC disconnectnode test #
5472
###########################

src/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4959,7 +4959,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
49594959
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
49604960
else
49614961
{
4962-
CNode::Ban(pto->addr);
4962+
CNode::Ban(pto->addr, BanReasonNodeMisbehaving);
49634963
}
49644964
}
49654965
state.fShouldBan = false;

src/net.cpp

Lines changed: 213 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -443,26 +443,28 @@ void CNode::PushVersion()
443443

444444

445445

446-
std::map<CSubNet, int64_t> CNode::setBanned;
446+
banmap_t CNode::setBanned;
447447
CCriticalSection CNode::cs_setBanned;
448+
bool CNode::setBannedIsDirty;
448449

449450
void CNode::ClearBanned()
450451
{
451452
LOCK(cs_setBanned);
452453
setBanned.clear();
454+
setBannedIsDirty = true;
453455
}
454456

455457
bool 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

503513
bool CNode::Unban(const CNetAddr &addr) {
504-
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
514+
CSubNet subNet(addr);
505515
return Unban(subNet);
506516
}
507517

508518
bool 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

522573
std::vector<CSubNet> CNode::vWhitelistedRange;
523574
CCriticalSection 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+
12151277
void 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

16961769
bool 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

Comments
 (0)