diff --git a/src/main.cpp b/src/main.cpp index 6c4cfe75a..a000a81fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4959,7 +4959,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); else { - CNode::Ban(pto->addr); + CNode::Ban(pto->addr, BanReasonNodeMisbehaving); } } state.fShouldBan = false; diff --git a/src/net.cpp b/src/net.cpp index 03db1f06a..ade34f575 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -443,7 +443,7 @@ void CNode::PushVersion() -std::map CNode::setBanned; +banmap_t CNode::setBanned; CCriticalSection CNode::cs_setBanned; bool CNode::setBannedIsDirty; @@ -459,12 +459,12 @@ bool CNode::IsBanned(CNetAddr ip) bool fResult = false; { LOCK(cs_setBanned); - for (std::map::iterator it = setBanned.begin(); it != setBanned.end(); it++) + for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) { 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; } } @@ -476,30 +476,36 @@ bool CNode::IsBanned(CSubNet subnet) bool fResult = false; { LOCK(cs_setBanned); - std::map::iterator i = setBanned.find(subnet); + banmap_t::iterator i = setBanned.find(subnet); if (i != setBanned.end()) { - int64_t t = (*i).second; - if (GetTime() < t) + CBanEntry banEntry = (*i).second; + if (GetTime() < banEntry.nBanUntil) fResult = true; } } 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")); - Ban(subNet, bantimeoffset, sinceUnixEpoch); + Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); } -void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) { - int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban - if (bantimeoffset > 0) - banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; +void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { + CBanEntry banEntry(GetTime()); + banEntry.banReason = banReason; + if (bantimeoffset <= 0) + { + bantimeoffset = GetArg("-bantime", 60*60*24); // Default 24-hour ban + sinceUnixEpoch = false; + } + banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; + LOCK(cs_setBanned); - if (setBanned[subNet] < banTime) - setBanned[subNet] = banTime; + if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) + setBanned[subNet] = banEntry; setBannedIsDirty = true; } @@ -519,13 +525,13 @@ bool CNode::Unban(const CSubNet &subNet) { return false; } -void CNode::GetBanned(std::map &banMap) +void CNode::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); banMap = setBanned; //create a thread safe copy } -void CNode::SetBanned(const std::map &banMap) +void CNode::SetBanned(const banmap_t &banMap) { LOCK(cs_setBanned); setBanned = banMap; @@ -537,10 +543,11 @@ void CNode::SweepBanned() int64_t now = GetTime(); LOCK(cs_setBanned); - std::map::iterator it = setBanned.begin(); + banmap_t::iterator it = setBanned.begin(); while(it != setBanned.end()) { - if(now > (*it).second) + CBanEntry banEntry = (*it).second; + if(now > banEntry.nBanUntil) { setBanned.erase(it++); setBannedIsDirty = true; @@ -1708,7 +1715,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) //try to read stored banlist CBanDB bandb; - std::map banmap; + banmap_t banmap; if (!bandb.Read(banmap)) LogPrintf("Invalid or missing banlist.dat; recreating\n"); @@ -2183,7 +2190,7 @@ CBanDB::CBanDB() pathBanlist = GetDataDir() / "banlist.dat"; } -bool CBanDB::Write(const std::map& banSet) +bool CBanDB::Write(const banmap_t& banSet) { // Generate random temporary filename unsigned short randv = 0; @@ -2221,7 +2228,7 @@ bool CBanDB::Write(const std::map& banSet) return true; } -bool CBanDB::Read(std::map& banSet) +bool CBanDB::Read(banmap_t& banSet) { // open input file, and associate with CAutoFile FILE *file = fopen(pathBanlist.string().c_str(), "rb"); @@ -2282,7 +2289,7 @@ void DumpBanlist() CNode::SweepBanned(); //clean unused entires (if bantime has expired) CBanDB bandb; - std::map banmap; + banmap_t banmap; CNode::GetBanned(banmap); bandb.Write(banmap); diff --git a/src/net.h b/src/net.h index 42c859e46..f15b85474 100644 --- a/src/net.h +++ b/src/net.h @@ -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 + 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 banmap_t; /** Information about a peer */ class CNode @@ -285,7 +343,7 @@ protected: // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time - static std::map setBanned; + static banmap_t setBanned; static CCriticalSection cs_setBanned; static bool setBannedIsDirty; @@ -609,12 +667,12 @@ public: static void ClearBanned(); // needed for unit testing static bool IsBanned(CNetAddr ip); static bool IsBanned(CSubNet subnet); - static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); - static void Ban(const CSubNet &subNet, 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, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); static bool Unban(const CNetAddr &ip); static bool Unban(const CSubNet &ip); - static void GetBanned(std::map &banmap); - static void SetBanned(const std::map &banmap); + static void GetBanned(banmap_t &banmap); + static void SetBanned(const banmap_t &banmap); //!check is the banlist has unwritten changes static bool BannedSetIsDirty(); @@ -660,8 +718,8 @@ private: boost::filesystem::path pathBanlist; public: CBanDB(); - bool Write(const std::map& banSet); - bool Read(std::map& banSet); + bool Write(const banmap_t& banSet); + bool Read(banmap_t& banSet); }; void DumpBanlist(); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 0c3745a7d..dd631905f 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -515,7 +515,7 @@ UniValue setban(const UniValue& params, bool fHelp) if (params.size() == 4 && params[3].isTrue()) 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 while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) @@ -542,15 +542,19 @@ UniValue listbanned(const UniValue& params, bool fHelp) + HelpExampleRpc("listbanned", "") ); - std::map banMap; + banmap_t banMap; CNode::GetBanned(banMap); UniValue bannedAddresses(UniValue::VARR); - for (std::map::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); 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); } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index c38df0ecf..9e99ff628 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) ar = r.get_array(); o1 = ar[0].get_obj(); 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(banned_until.get_int64(), 1607731200); // absolute time check @@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) ar = r.get_array(); o1 = ar[0].get_obj(); 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"); int64_t now = GetTime(); BOOST_CHECK(banned_until.get_int64() > now);