mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-10 23:27:54 +00:00
banlist.dat: store banlist on disk
This commit is contained in:
parent
d6db1157bc
commit
f581d3d656
@ -49,6 +49,24 @@ class NodeHandlingTest (BitcoinTestFramework):
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
|
||||
##test persisted banlist
|
||||
self.nodes[2].setban("127.0.0.0/32", "add")
|
||||
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
|
||||
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
|
||||
listBeforeShutdown = self.nodes[2].listbanned();
|
||||
assert_equal("192.168.0.1/255.255.255.255", listBeforeShutdown[2]['address']) #must be here
|
||||
time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
|
||||
|
||||
#stop node
|
||||
stop_node(self.nodes[2], 2)
|
||||
|
||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
||||
listAfterShutdown = self.nodes[2].listbanned();
|
||||
assert_equal("127.0.0.0/255.255.255.0", listAfterShutdown[0]['address'])
|
||||
assert_equal("127.0.0.0/255.255.255.255", listAfterShutdown[1]['address'])
|
||||
assert_equal("2001:4000::/ffff:e000:0:0:0:0:0:0", listAfterShutdown[2]['address'])
|
||||
|
||||
###########################
|
||||
# RPC disconnectnode test #
|
||||
###########################
|
||||
|
186
src/net.cpp
186
src/net.cpp
@ -445,11 +445,13 @@ void CNode::PushVersion()
|
||||
|
||||
std::map<CSubNet, int64_t> CNode::setBanned;
|
||||
CCriticalSection CNode::cs_setBanned;
|
||||
bool CNode::setBannedIsDirty;
|
||||
|
||||
void CNode::ClearBanned()
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
setBanned.clear();
|
||||
setBannedIsDirty = true;
|
||||
}
|
||||
|
||||
bool CNode::IsBanned(CNetAddr ip)
|
||||
@ -498,6 +500,8 @@ void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoc
|
||||
LOCK(cs_setBanned);
|
||||
if (setBanned[subNet] < banTime)
|
||||
setBanned[subNet] = banTime;
|
||||
|
||||
setBannedIsDirty = true;
|
||||
}
|
||||
|
||||
bool CNode::Unban(const CNetAddr &addr) {
|
||||
@ -508,7 +512,10 @@ bool CNode::Unban(const CNetAddr &addr) {
|
||||
bool CNode::Unban(const CSubNet &subNet) {
|
||||
LOCK(cs_setBanned);
|
||||
if (setBanned.erase(subNet))
|
||||
{
|
||||
setBannedIsDirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -518,6 +525,43 @@ void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
|
||||
banMap = setBanned; //create a thread safe copy
|
||||
}
|
||||
|
||||
void CNode::SetBanned(const std::map<CSubNet, int64_t> &banMap)
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
setBanned = banMap;
|
||||
setBannedIsDirty = true;
|
||||
}
|
||||
|
||||
void CNode::SweepBanned()
|
||||
{
|
||||
int64_t now = GetTime();
|
||||
|
||||
LOCK(cs_setBanned);
|
||||
std::map<CSubNet, int64_t>::iterator it = setBanned.begin();
|
||||
while(it != setBanned.end())
|
||||
{
|
||||
if(now > (*it).second)
|
||||
{
|
||||
setBanned.erase(it++);
|
||||
setBannedIsDirty = true;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool CNode::BannedSetIsDirty()
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
return setBannedIsDirty;
|
||||
}
|
||||
|
||||
void CNode::SetBannedSetDirty(bool dirty)
|
||||
{
|
||||
LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag
|
||||
setBannedIsDirty = dirty;
|
||||
}
|
||||
|
||||
|
||||
std::vector<CSubNet> CNode::vWhitelistedRange;
|
||||
CCriticalSection CNode::cs_vWhitelistedRange;
|
||||
@ -1212,6 +1256,17 @@ void DumpAddresses()
|
||||
addrman.size(), GetTimeMillis() - nStart);
|
||||
}
|
||||
|
||||
void DumpData()
|
||||
{
|
||||
DumpAddresses();
|
||||
|
||||
if (CNode::BannedSetIsDirty())
|
||||
{
|
||||
DumpBanlist();
|
||||
CNode::SetBannedSetDirty(false);
|
||||
}
|
||||
}
|
||||
|
||||
void static ProcessOneShot()
|
||||
{
|
||||
string strDest;
|
||||
@ -1650,6 +1705,17 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
if (!adb.Read(addrman))
|
||||
LogPrintf("Invalid or missing peers.dat; recreating\n");
|
||||
}
|
||||
|
||||
//try to read stored banlist
|
||||
CBanDB bandb;
|
||||
std::map<CSubNet, int64_t> banmap;
|
||||
if (!bandb.Read(banmap))
|
||||
LogPrintf("Invalid or missing banlist.dat; recreating\n");
|
||||
|
||||
CNode::SetBanned(banmap); //thread save setter
|
||||
CNode::SetBannedSetDirty(false); //no need to write down just read or nonexistent data
|
||||
CNode::SweepBanned(); //sweap out unused entries
|
||||
|
||||
LogPrintf("Loaded %i addresses from peers.dat %dms\n",
|
||||
addrman.size(), GetTimeMillis() - nStart);
|
||||
fAddressesInitialized = true;
|
||||
@ -1690,7 +1756,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
|
||||
|
||||
// Dump network addresses
|
||||
scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL);
|
||||
scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL);
|
||||
}
|
||||
|
||||
bool StopNode()
|
||||
@ -1703,7 +1769,7 @@ bool StopNode()
|
||||
|
||||
if (fAddressesInitialized)
|
||||
{
|
||||
DumpAddresses();
|
||||
DumpData();
|
||||
fAddressesInitialized = false;
|
||||
}
|
||||
|
||||
@ -2107,3 +2173,119 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
|
||||
|
||||
LEAVE_CRITICAL_SECTION(cs_vSend);
|
||||
}
|
||||
|
||||
//
|
||||
// CBanDB
|
||||
//
|
||||
|
||||
CBanDB::CBanDB()
|
||||
{
|
||||
pathBanlist = GetDataDir() / "banlist.dat";
|
||||
}
|
||||
|
||||
bool CBanDB::Write(const std::map<CSubNet, int64_t>& banSet)
|
||||
{
|
||||
// Generate random temporary filename
|
||||
unsigned short randv = 0;
|
||||
GetRandBytes((unsigned char*)&randv, sizeof(randv));
|
||||
std::string tmpfn = strprintf("banlist.dat.%04x", randv);
|
||||
|
||||
// serialize banlist, checksum data up to that point, then append csum
|
||||
CDataStream ssBanlist(SER_DISK, CLIENT_VERSION);
|
||||
ssBanlist << FLATDATA(Params().MessageStart());
|
||||
ssBanlist << banSet;
|
||||
uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end());
|
||||
ssBanlist << hash;
|
||||
|
||||
// open temp output file, and associate with CAutoFile
|
||||
boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
|
||||
FILE *file = fopen(pathTmp.string().c_str(), "wb");
|
||||
CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
|
||||
if (fileout.IsNull())
|
||||
return error("%s: Failed to open file %s", __func__, pathTmp.string());
|
||||
|
||||
// Write and commit header, data
|
||||
try {
|
||||
fileout << ssBanlist;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return error("%s: Serialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
FileCommit(fileout.Get());
|
||||
fileout.fclose();
|
||||
|
||||
// replace existing banlist.dat, if any, with new banlist.dat.XXXX
|
||||
if (!RenameOver(pathTmp, pathBanlist))
|
||||
return error("%s: Rename-into-place failed", __func__);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBanDB::Read(std::map<CSubNet, int64_t>& banSet)
|
||||
{
|
||||
// open input file, and associate with CAutoFile
|
||||
FILE *file = fopen(pathBanlist.string().c_str(), "rb");
|
||||
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
|
||||
if (filein.IsNull())
|
||||
return error("%s: Failed to open file %s", __func__, pathBanlist.string());
|
||||
|
||||
// use file size to size memory buffer
|
||||
int fileSize = boost::filesystem::file_size(pathBanlist);
|
||||
int dataSize = fileSize - sizeof(uint256);
|
||||
// Don't try to resize to a negative number if file is small
|
||||
if (dataSize < 0)
|
||||
dataSize = 0;
|
||||
vector<unsigned char> vchData;
|
||||
vchData.resize(dataSize);
|
||||
uint256 hashIn;
|
||||
|
||||
// read data and checksum from file
|
||||
try {
|
||||
filein.read((char *)&vchData[0], dataSize);
|
||||
filein >> hashIn;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
filein.fclose();
|
||||
|
||||
CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
// verify stored checksum matches input data
|
||||
uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end());
|
||||
if (hashIn != hashTmp)
|
||||
return error("%s: Checksum mismatch, data corrupted", __func__);
|
||||
|
||||
unsigned char pchMsgTmp[4];
|
||||
try {
|
||||
// de-serialize file header (network specific magic number) and ..
|
||||
ssBanlist >> FLATDATA(pchMsgTmp);
|
||||
|
||||
// ... verify the network matches ours
|
||||
if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
|
||||
return error("%s: Invalid network magic number", __func__);
|
||||
|
||||
// de-serialize address data into one CAddrMan object
|
||||
ssBanlist >> banSet;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpBanlist()
|
||||
{
|
||||
int64_t nStart = GetTimeMillis();
|
||||
|
||||
CNode::SweepBanned(); //clean unused entires (if bantime has expired)
|
||||
|
||||
CBanDB bandb;
|
||||
std::map<CSubNet, int64_t> banmap;
|
||||
CNode::GetBanned(banmap);
|
||||
bandb.Write(banmap);
|
||||
|
||||
LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
|
||||
banmap.size(), GetTimeMillis() - nStart);
|
||||
}
|
22
src/net.h
22
src/net.h
@ -287,6 +287,7 @@ protected:
|
||||
// Key is IP address, value is banned-until-time
|
||||
static std::map<CSubNet, int64_t> setBanned;
|
||||
static CCriticalSection cs_setBanned;
|
||||
static bool setBannedIsDirty;
|
||||
|
||||
// Whitelisted ranges. Any node connecting from these is automatically
|
||||
// whitelisted (as well as those connecting to whitelisted binds).
|
||||
@ -613,6 +614,14 @@ public:
|
||||
static bool Unban(const CNetAddr &ip);
|
||||
static bool Unban(const CSubNet &ip);
|
||||
static void GetBanned(std::map<CSubNet, int64_t> &banmap);
|
||||
static void SetBanned(const std::map<CSubNet, int64_t> &banmap);
|
||||
|
||||
//!check is the banlist has unwritten changes
|
||||
static bool BannedSetIsDirty();
|
||||
//!set the "dirty" flag for the banlist
|
||||
static void SetBannedSetDirty(bool dirty=true);
|
||||
//!clean unused entires (if bantime has expired)
|
||||
static void SweepBanned();
|
||||
|
||||
void copyStats(CNodeStats &stats);
|
||||
|
||||
@ -644,4 +653,17 @@ public:
|
||||
bool Read(CAddrMan& addr);
|
||||
};
|
||||
|
||||
/** Access to the banlist database (banlist.dat) */
|
||||
class CBanDB
|
||||
{
|
||||
private:
|
||||
boost::filesystem::path pathBanlist;
|
||||
public:
|
||||
CBanDB();
|
||||
bool Write(const std::map<CSubNet, int64_t>& banSet);
|
||||
bool Read(std::map<CSubNet, int64_t>& banSet);
|
||||
};
|
||||
|
||||
void DumpBanlist();
|
||||
|
||||
#endif // BITCOIN_NET_H
|
||||
|
@ -126,6 +126,15 @@ class CSubNet
|
||||
friend bool operator==(const CSubNet& a, const CSubNet& b);
|
||||
friend bool operator!=(const CSubNet& a, const CSubNet& b);
|
||||
friend bool operator<(const CSubNet& a, const CSubNet& b);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(network);
|
||||
READWRITE(FLATDATA(netmask));
|
||||
READWRITE(FLATDATA(valid));
|
||||
}
|
||||
};
|
||||
|
||||
/** A combination of a network address (CNetAddr) and a (TCP) port */
|
||||
|
@ -527,6 +527,7 @@ UniValue setban(const UniValue& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
|
||||
}
|
||||
|
||||
DumpBanlist(); //store banlist to disk
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
@ -568,6 +569,7 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
|
||||
);
|
||||
|
||||
CNode::ClearBanned();
|
||||
DumpBanlist(); //store banlist to disk
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user