diff options
author | Jonas Schnelli <jonas.schnelli@include7.ch> | 2015-06-19 15:27:37 +0200 |
---|---|---|
committer | Jonas Schnelli <jonas.schnelli@include7.ch> | 2015-07-02 20:29:36 +0200 |
commit | f581d3d656cf269ea09ac6f130f4bd70b40a9e55 (patch) | |
tree | d598d6e1842273946fbbfb7b91744b9e6ca73c4b /src | |
parent | d6db1157bcab7ace13f046bdfa107b456c1dfbe3 (diff) |
banlist.dat: store banlist on disk
Diffstat (limited to 'src')
-rw-r--r-- | src/net.cpp | 186 | ||||
-rw-r--r-- | src/net.h | 22 | ||||
-rw-r--r-- | src/netbase.h | 9 | ||||
-rw-r--r-- | src/rpcnet.cpp | 2 |
4 files changed, 217 insertions, 2 deletions
diff --git a/src/net.cpp b/src/net.cpp index 0511256e55..3a549d65c4 100644 --- a/src/net.cpp +++ b/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); +}
\ No newline at end of file @@ -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 diff --git a/src/netbase.h b/src/netbase.h index 27f0eac2a2..48c2d3455e 100644 --- a/src/netbase.h +++ b/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); + + 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 */ diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 1572b16687..0c3745a7da 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -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; } |