use CBanEntry as object container for banned nodes

- added a reason enum for a ban
- added creation time for a ban

Using CBanEntry as container will keep banlist.dat extenable.
This commit is contained in:
Jonas Schnelli 2015-06-26 21:38:33 +02:00
parent dfa174c295
commit 409bccfbf5
5 changed files with 106 additions and 37 deletions

View File

@ -4959,7 +4959,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
else else
{ {
CNode::Ban(pto->addr); CNode::Ban(pto->addr, BanReasonNodeMisbehaving);
} }
} }
state.fShouldBan = false; state.fShouldBan = false;

View File

@ -443,7 +443,7 @@ void CNode::PushVersion()
std::map<CSubNet, int64_t> CNode::setBanned; banmap_t CNode::setBanned;
CCriticalSection CNode::cs_setBanned; CCriticalSection CNode::cs_setBanned;
bool CNode::setBannedIsDirty; bool CNode::setBannedIsDirty;
@ -459,12 +459,12 @@ bool CNode::IsBanned(CNetAddr ip)
bool fResult = false; bool fResult = false;
{ {
LOCK(cs_setBanned); LOCK(cs_setBanned);
for (std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); it != setBanned.end(); it++) for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++)
{ {
CSubNet subNet = (*it).first; CSubNet subNet = (*it).first;
int64_t t = (*it).second; CBanEntry banEntry = (*it).second;
if(subNet.Match(ip) && GetTime() < t) if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil)
fResult = true; fResult = true;
} }
} }
@ -476,30 +476,36 @@ bool CNode::IsBanned(CSubNet subnet)
bool fResult = false; bool fResult = false;
{ {
LOCK(cs_setBanned); LOCK(cs_setBanned);
std::map<CSubNet, int64_t>::iterator i = setBanned.find(subnet); banmap_t::iterator i = setBanned.find(subnet);
if (i != setBanned.end()) if (i != setBanned.end())
{ {
int64_t t = (*i).second; CBanEntry banEntry = (*i).second;
if (GetTime() < t) if (GetTime() < banEntry.nBanUntil)
fResult = true; fResult = true;
} }
} }
return fResult; return fResult;
} }
void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) { void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128")); CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
Ban(subNet, bantimeoffset, sinceUnixEpoch); Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch);
} }
void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) { void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban CBanEntry banEntry(GetTime());
if (bantimeoffset > 0) banEntry.banReason = banReason;
banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; if (bantimeoffset <= 0)
{
bantimeoffset = GetArg("-bantime", 60*60*24); // Default 24-hour ban
sinceUnixEpoch = false;
}
banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
LOCK(cs_setBanned); LOCK(cs_setBanned);
if (setBanned[subNet] < banTime) if (setBanned[subNet].nBanUntil < banEntry.nBanUntil)
setBanned[subNet] = banTime; setBanned[subNet] = banEntry;
setBannedIsDirty = true; setBannedIsDirty = true;
} }
@ -519,13 +525,13 @@ bool CNode::Unban(const CSubNet &subNet) {
return false; return false;
} }
void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap) void CNode::GetBanned(banmap_t &banMap)
{ {
LOCK(cs_setBanned); LOCK(cs_setBanned);
banMap = setBanned; //create a thread safe copy banMap = setBanned; //create a thread safe copy
} }
void CNode::SetBanned(const std::map<CSubNet, int64_t> &banMap) void CNode::SetBanned(const banmap_t &banMap)
{ {
LOCK(cs_setBanned); LOCK(cs_setBanned);
setBanned = banMap; setBanned = banMap;
@ -537,10 +543,11 @@ void CNode::SweepBanned()
int64_t now = GetTime(); int64_t now = GetTime();
LOCK(cs_setBanned); LOCK(cs_setBanned);
std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); banmap_t::iterator it = setBanned.begin();
while(it != setBanned.end()) while(it != setBanned.end())
{ {
if(now > (*it).second) CBanEntry banEntry = (*it).second;
if(now > banEntry.nBanUntil)
{ {
setBanned.erase(it++); setBanned.erase(it++);
setBannedIsDirty = true; setBannedIsDirty = true;
@ -1708,7 +1715,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
//try to read stored banlist //try to read stored banlist
CBanDB bandb; CBanDB bandb;
std::map<CSubNet, int64_t> banmap; banmap_t banmap;
if (!bandb.Read(banmap)) if (!bandb.Read(banmap))
LogPrintf("Invalid or missing banlist.dat; recreating\n"); LogPrintf("Invalid or missing banlist.dat; recreating\n");
@ -2183,7 +2190,7 @@ CBanDB::CBanDB()
pathBanlist = GetDataDir() / "banlist.dat"; pathBanlist = GetDataDir() / "banlist.dat";
} }
bool CBanDB::Write(const std::map<CSubNet, int64_t>& banSet) bool CBanDB::Write(const banmap_t& banSet)
{ {
// Generate random temporary filename // Generate random temporary filename
unsigned short randv = 0; unsigned short randv = 0;
@ -2221,7 +2228,7 @@ bool CBanDB::Write(const std::map<CSubNet, int64_t>& banSet)
return true; return true;
} }
bool CBanDB::Read(std::map<CSubNet, int64_t>& banSet) bool CBanDB::Read(banmap_t& banSet)
{ {
// open input file, and associate with CAutoFile // open input file, and associate with CAutoFile
FILE *file = fopen(pathBanlist.string().c_str(), "rb"); FILE *file = fopen(pathBanlist.string().c_str(), "rb");
@ -2282,7 +2289,7 @@ void DumpBanlist()
CNode::SweepBanned(); //clean unused entires (if bantime has expired) CNode::SweepBanned(); //clean unused entires (if bantime has expired)
CBanDB bandb; CBanDB bandb;
std::map<CSubNet, int64_t> banmap; banmap_t banmap;
CNode::GetBanned(banmap); CNode::GetBanned(banmap);
bandb.Write(banmap); bandb.Write(banmap);

View File

@ -228,8 +228,66 @@ public:
}; };
typedef enum BanReason
{
BanReasonUnknown = 0,
BanReasonNodeMisbehaving = 1,
BanReasonManuallyAdded = 2
} BanReason;
class CBanEntry
{
public:
static const int CURRENT_VERSION=1;
int nVersion;
int64_t nCreateTime;
int64_t nBanUntil;
uint8_t banReason;
CBanEntry()
{
SetNull();
}
CBanEntry(int64_t nCreateTimeIn)
{
SetNull();
nCreateTime = nCreateTimeIn;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nCreateTime);
READWRITE(nBanUntil);
READWRITE(banReason);
}
void SetNull()
{
nVersion = CBanEntry::CURRENT_VERSION;
nCreateTime = 0;
nBanUntil = 0;
banReason = BanReasonUnknown;
}
std::string banReasonToString()
{
switch (banReason) {
case BanReasonNodeMisbehaving:
return "node misbehabing";
case BanReasonManuallyAdded:
return "manually added";
default:
return "unknown";
}
}
};
typedef std::map<CSubNet, CBanEntry> banmap_t;
/** Information about a peer */ /** Information about a peer */
class CNode class CNode
@ -285,7 +343,7 @@ protected:
// Denial-of-service detection/prevention // Denial-of-service detection/prevention
// 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 banmap_t setBanned;
static CCriticalSection cs_setBanned; static CCriticalSection cs_setBanned;
static bool setBannedIsDirty; static bool setBannedIsDirty;
@ -609,12 +667,12 @@ public:
static void ClearBanned(); // needed for unit testing static void ClearBanned(); // needed for unit testing
static bool IsBanned(CNetAddr ip); static bool IsBanned(CNetAddr ip);
static bool IsBanned(CSubNet subnet); static bool IsBanned(CSubNet subnet);
static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); static void Ban(const CNetAddr &ip, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); static void Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
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(banmap_t &banmap);
static void SetBanned(const std::map<CSubNet, int64_t> &banmap); static void SetBanned(const banmap_t &banmap);
//!check is the banlist has unwritten changes //!check is the banlist has unwritten changes
static bool BannedSetIsDirty(); static bool BannedSetIsDirty();
@ -660,8 +718,8 @@ private:
boost::filesystem::path pathBanlist; boost::filesystem::path pathBanlist;
public: public:
CBanDB(); CBanDB();
bool Write(const std::map<CSubNet, int64_t>& banSet); bool Write(const banmap_t& banSet);
bool Read(std::map<CSubNet, int64_t>& banSet); bool Read(banmap_t& banSet);
}; };
void DumpBanlist(); void DumpBanlist();

View File

@ -515,7 +515,7 @@ UniValue setban(const UniValue& params, bool fHelp)
if (params.size() == 4 && params[3].isTrue()) if (params.size() == 4 && params[3].isTrue())
absolute = true; absolute = true;
isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute); isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
//disconnect possible nodes //disconnect possible nodes
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
@ -542,15 +542,19 @@ UniValue listbanned(const UniValue& params, bool fHelp)
+ HelpExampleRpc("listbanned", "") + HelpExampleRpc("listbanned", "")
); );
std::map<CSubNet, int64_t> banMap; banmap_t banMap;
CNode::GetBanned(banMap); CNode::GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR); UniValue bannedAddresses(UniValue::VARR);
for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++) for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
{ {
CBanEntry banEntry = (*it).second;
UniValue rec(UniValue::VOBJ); UniValue rec(UniValue::VOBJ);
rec.push_back(Pair("address", (*it).first.ToString())); rec.push_back(Pair("address", (*it).first.ToString()));
rec.push_back(Pair("banned_untill", (*it).second)); rec.push_back(Pair("banned_until", banEntry.nBanUntil));
rec.push_back(Pair("ban_created", banEntry.nCreateTime));
rec.push_back(Pair("ban_reason", banEntry.banReasonToString()));
bannedAddresses.push_back(rec); bannedAddresses.push_back(rec);
} }

View File

@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array(); ar = r.get_array();
o1 = ar[0].get_obj(); o1 = ar[0].get_obj();
adr = find_value(o1, "address"); adr = find_value(o1, "address");
UniValue banned_until = find_value(o1, "banned_untill"); UniValue banned_until = find_value(o1, "banned_until");
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check
@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
ar = r.get_array(); ar = r.get_array();
o1 = ar[0].get_obj(); o1 = ar[0].get_obj();
adr = find_value(o1, "address"); adr = find_value(o1, "address");
banned_until = find_value(o1, "banned_untill"); banned_until = find_value(o1, "banned_until");
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
int64_t now = GetTime(); int64_t now = GetTime();
BOOST_CHECK(banned_until.get_int64() > now); BOOST_CHECK(banned_until.get_int64() > now);