Browse Source

banlist.dat: store banlist on disk

0.13
Jonas Schnelli 10 years ago
parent
commit
f581d3d656
  1. 18
      qa/rpc-tests/nodehandling.py
  2. 186
      src/net.cpp
  3. 22
      src/net.h
  4. 9
      src/netbase.h
  5. 2
      src/rpcnet.cpp

18
qa/rpc-tests/nodehandling.py

@ -49,6 +49,24 @@ class NodeHandlingTest (BitcoinTestFramework):
self.nodes[2].clearbanned() self.nodes[2].clearbanned()
assert_equal(len(self.nodes[2].listbanned()), 0) 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 # # RPC disconnectnode test #
########################### ###########################

186
src/net.cpp

@ -445,11 +445,13 @@ void CNode::PushVersion()
std::map<CSubNet, int64_t> CNode::setBanned; std::map<CSubNet, int64_t> CNode::setBanned;
CCriticalSection CNode::cs_setBanned; CCriticalSection CNode::cs_setBanned;
bool CNode::setBannedIsDirty;
void CNode::ClearBanned() void CNode::ClearBanned()
{ {
LOCK(cs_setBanned); LOCK(cs_setBanned);
setBanned.clear(); setBanned.clear();
setBannedIsDirty = true;
} }
bool CNode::IsBanned(CNetAddr ip) bool CNode::IsBanned(CNetAddr ip)
@ -498,6 +500,8 @@ void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoc
LOCK(cs_setBanned); LOCK(cs_setBanned);
if (setBanned[subNet] < banTime) if (setBanned[subNet] < banTime)
setBanned[subNet] = banTime; setBanned[subNet] = banTime;
setBannedIsDirty = true;
} }
bool CNode::Unban(const CNetAddr &addr) { bool CNode::Unban(const CNetAddr &addr) {
@ -508,7 +512,10 @@ bool CNode::Unban(const CNetAddr &addr) {
bool CNode::Unban(const CSubNet &subNet) { bool CNode::Unban(const CSubNet &subNet) {
LOCK(cs_setBanned); LOCK(cs_setBanned);
if (setBanned.erase(subNet)) if (setBanned.erase(subNet))
{
setBannedIsDirty = true;
return true; return true;
}
return false; return false;
} }
@ -518,6 +525,43 @@ void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
banMap = setBanned; //create a thread safe copy 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; std::vector<CSubNet> CNode::vWhitelistedRange;
CCriticalSection CNode::cs_vWhitelistedRange; CCriticalSection CNode::cs_vWhitelistedRange;
@ -1212,6 +1256,17 @@ void DumpAddresses()
addrman.size(), GetTimeMillis() - nStart); addrman.size(), GetTimeMillis() - nStart);
} }
void DumpData()
{
DumpAddresses();
if (CNode::BannedSetIsDirty())
{
DumpBanlist();
CNode::SetBannedSetDirty(false);
}
}
void static ProcessOneShot() void static ProcessOneShot()
{ {
string strDest; string strDest;
@ -1650,6 +1705,17 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!adb.Read(addrman)) if (!adb.Read(addrman))
LogPrintf("Invalid or missing peers.dat; recreating\n"); 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", LogPrintf("Loaded %i addresses from peers.dat %dms\n",
addrman.size(), GetTimeMillis() - nStart); addrman.size(), GetTimeMillis() - nStart);
fAddressesInitialized = true; fAddressesInitialized = true;
@ -1690,7 +1756,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler)); threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
// Dump network addresses // Dump network addresses
scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL); scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL);
} }
bool StopNode() bool StopNode()
@ -1703,7 +1769,7 @@ bool StopNode()
if (fAddressesInitialized) if (fAddressesInitialized)
{ {
DumpAddresses(); DumpData();
fAddressesInitialized = false; fAddressesInitialized = false;
} }
@ -2107,3 +2173,119 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend)
LEAVE_CRITICAL_SECTION(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

@ -287,6 +287,7 @@ protected:
// Key is IP address, value is banned-until-time // Key is IP address, value is banned-until-time
static std::map<CSubNet, int64_t> setBanned; static std::map<CSubNet, int64_t> setBanned;
static CCriticalSection cs_setBanned; static CCriticalSection cs_setBanned;
static bool setBannedIsDirty;
// Whitelisted ranges. Any node connecting from these is automatically // Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds). // whitelisted (as well as those connecting to whitelisted binds).
@ -613,6 +614,14 @@ public:
static bool Unban(const CNetAddr &ip); static bool Unban(const CNetAddr &ip);
static bool Unban(const CSubNet &ip); static bool Unban(const CSubNet &ip);
static void GetBanned(std::map<CSubNet, int64_t> &banmap); 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); void copyStats(CNodeStats &stats);
@ -644,4 +653,17 @@ public:
bool Read(CAddrMan& addr); 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 #endif // BITCOIN_NET_H

9
src/netbase.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); 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 */ /** A combination of a network address (CNetAddr) and a (TCP) port */

2
src/rpcnet.cpp

@ -527,6 +527,7 @@ UniValue setban(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
} }
DumpBanlist(); //store banlist to disk
return NullUniValue; return NullUniValue;
} }
@ -568,6 +569,7 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
); );
CNode::ClearBanned(); CNode::ClearBanned();
DumpBanlist(); //store banlist to disk
return NullUniValue; return NullUniValue;
} }

Loading…
Cancel
Save