Skip to content

Commit

Permalink
banlist.dat: store banlist on disk
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasschnelli committed Jul 2, 2015
1 parent d6db115 commit f581d3d
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 3 deletions.
20 changes: 19 additions & 1 deletion qa/rpc-tests/nodehandling.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,25 @@ def run_test(self):
assert_equal(len(self.nodes[2].listbanned()), 0)
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 #
###########################
Expand Down
186 changes: 184 additions & 2 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand All @@ -1703,7 +1769,7 @@ bool StopNode()

if (fAddressesInitialized)
{
DumpAddresses();
DumpData();
fAddressesInitialized = false;
}

Expand Down Expand Up @@ -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 changes: 22 additions & 0 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class CNode
// 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).
Expand Down Expand Up @@ -613,6 +614,14 @@ class CNode
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);

Expand Down Expand Up @@ -644,4 +653,17 @@ class CAddrDB
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
9 changes: 9 additions & 0 deletions src/netbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions src/rpcnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -568,6 +569,7 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
);

CNode::ClearBanned();
DumpBanlist(); //store banlist to disk

return NullUniValue;
}

0 comments on commit f581d3d

Please sign in to comment.