mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
50287e2403
99a6b699cd650f13d7200d344bf5e2d4b45b20ac Fix race condition for SetBannedSetDirty() calls (Hennadii Stepanov) 83c76467157bbca023bffda0f0bc2f01eb76a040 Avoid calling BanMan::SweepBanned() twice in a row (Hennadii Stepanov) 33bda6ab87cc1b569e96da337296eb3e9ce6db1a Fix data race condition in BanMan::DumpBanlist() (Hennadii Stepanov) 5e20e9ec3859205c220867ca49efb752b8edaacc Prevent possible concurrent CBanDB::Write() calls (Hennadii Stepanov) Pull request description: This PR split from bitcoin/bitcoin#24097 with some additions. This makes the following switch from `RecursiveMutex` to `Mutex` a pure refactoring. See details in commit messages. ACKs for top commit: w0xlt: reACK 99a6b69 shaavan: ACK 99a6b699cd650f13d7200d344bf5e2d4b45b20ac Tree-SHA512: da4e7268c7bd3424491f446145f18af4ccfc804023d0a7fe70e1462baab550a5e44f9159f8b9f9c7820d2c6cb6447b63883616199e4d9d439ab9ab1b67c7201b
220 lines
5.6 KiB
C++
220 lines
5.6 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2019 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <banman.h>
|
|
|
|
#include <netaddress.h>
|
|
#include <node/ui_interface.h>
|
|
#include <sync.h>
|
|
#include <util/system.h>
|
|
#include <util/time.h>
|
|
#include <util/translation.h>
|
|
|
|
|
|
BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time)
|
|
: m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time)
|
|
{
|
|
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist...").translated);
|
|
|
|
int64_t n_start = GetTimeMillis();
|
|
if (m_ban_db.Read(m_banned)) {
|
|
SweepBanned(); // sweep out unused entries
|
|
|
|
LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(),
|
|
GetTimeMillis() - n_start);
|
|
} else {
|
|
LogPrintf("Recreating the banlist database\n");
|
|
m_banned = {};
|
|
m_is_dirty = true;
|
|
}
|
|
|
|
DumpBanlist();
|
|
}
|
|
|
|
BanMan::~BanMan()
|
|
{
|
|
DumpBanlist();
|
|
}
|
|
|
|
void BanMan::DumpBanlist()
|
|
{
|
|
static Mutex dump_mutex;
|
|
LOCK(dump_mutex);
|
|
|
|
banmap_t banmap;
|
|
{
|
|
LOCK(m_cs_banned);
|
|
SweepBanned();
|
|
if (!BannedSetIsDirty()) return;
|
|
banmap = m_banned;
|
|
SetBannedSetDirty(false);
|
|
}
|
|
|
|
int64_t n_start = GetTimeMillis();
|
|
if (!m_ban_db.Write(banmap)) {
|
|
SetBannedSetDirty(true);
|
|
}
|
|
|
|
LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(),
|
|
GetTimeMillis() - n_start);
|
|
}
|
|
|
|
void BanMan::ClearBanned()
|
|
{
|
|
{
|
|
LOCK(m_cs_banned);
|
|
m_banned.clear();
|
|
m_is_dirty = true;
|
|
}
|
|
DumpBanlist(); //store banlist to disk
|
|
if (m_client_interface) m_client_interface->BannedListChanged();
|
|
}
|
|
|
|
void BanMan::ClearDiscouraged()
|
|
{
|
|
{
|
|
LOCK(m_cs_banned);
|
|
m_discouraged.reset();
|
|
m_is_dirty = true;
|
|
}
|
|
if (m_client_interface) m_client_interface->BannedListChanged();
|
|
}
|
|
|
|
bool BanMan::IsDiscouraged(const CNetAddr& net_addr)
|
|
{
|
|
LOCK(m_cs_banned);
|
|
return m_discouraged.contains(net_addr.GetAddrBytes());
|
|
}
|
|
|
|
bool BanMan::IsBanned(const CNetAddr& net_addr)
|
|
{
|
|
auto current_time = GetTime();
|
|
LOCK(m_cs_banned);
|
|
for (const auto& it : m_banned) {
|
|
CSubNet sub_net = it.first;
|
|
CBanEntry ban_entry = it.second;
|
|
|
|
if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BanMan::IsBanned(const CSubNet& sub_net)
|
|
{
|
|
auto current_time = GetTime();
|
|
LOCK(m_cs_banned);
|
|
banmap_t::iterator i = m_banned.find(sub_net);
|
|
if (i != m_banned.end()) {
|
|
CBanEntry ban_entry = (*i).second;
|
|
if (current_time < ban_entry.nBanUntil) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BanMan::Ban(const CNetAddr& net_addr, int64_t ban_time_offset, bool since_unix_epoch)
|
|
{
|
|
CSubNet sub_net(net_addr);
|
|
Ban(sub_net, ban_time_offset, since_unix_epoch);
|
|
}
|
|
|
|
void BanMan::Discourage(const CNetAddr& net_addr)
|
|
{
|
|
LOCK(m_cs_banned);
|
|
m_discouraged.insert(net_addr.GetAddrBytes());
|
|
}
|
|
|
|
void BanMan::Ban(const CSubNet& sub_net, int64_t ban_time_offset, bool since_unix_epoch)
|
|
{
|
|
CBanEntry ban_entry(GetTime());
|
|
|
|
int64_t normalized_ban_time_offset = ban_time_offset;
|
|
bool normalized_since_unix_epoch = since_unix_epoch;
|
|
if (ban_time_offset <= 0) {
|
|
normalized_ban_time_offset = m_default_ban_time;
|
|
normalized_since_unix_epoch = false;
|
|
}
|
|
ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + normalized_ban_time_offset;
|
|
|
|
{
|
|
LOCK(m_cs_banned);
|
|
if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) {
|
|
m_banned[sub_net] = ban_entry;
|
|
m_is_dirty = true;
|
|
} else
|
|
return;
|
|
}
|
|
if (m_client_interface) m_client_interface->BannedListChanged();
|
|
|
|
//store banlist to disk immediately
|
|
DumpBanlist();
|
|
}
|
|
|
|
bool BanMan::Unban(const CNetAddr& net_addr)
|
|
{
|
|
CSubNet sub_net(net_addr);
|
|
return Unban(sub_net);
|
|
}
|
|
|
|
bool BanMan::Unban(const CSubNet& sub_net)
|
|
{
|
|
{
|
|
LOCK(m_cs_banned);
|
|
if (m_banned.erase(sub_net) == 0) return false;
|
|
m_is_dirty = true;
|
|
}
|
|
if (m_client_interface) m_client_interface->BannedListChanged();
|
|
DumpBanlist(); //store banlist to disk immediately
|
|
return true;
|
|
}
|
|
|
|
void BanMan::GetBanned(banmap_t& banmap)
|
|
{
|
|
LOCK(m_cs_banned);
|
|
// Sweep the banlist so expired bans are not returned
|
|
SweepBanned();
|
|
banmap = m_banned; //create a thread safe copy
|
|
}
|
|
|
|
void BanMan::SweepBanned()
|
|
{
|
|
int64_t now = GetTime();
|
|
bool notify_ui = false;
|
|
{
|
|
LOCK(m_cs_banned);
|
|
banmap_t::iterator it = m_banned.begin();
|
|
while (it != m_banned.end()) {
|
|
CSubNet sub_net = (*it).first;
|
|
CBanEntry ban_entry = (*it).second;
|
|
if (!sub_net.IsValid() || now > ban_entry.nBanUntil) {
|
|
m_banned.erase(it++);
|
|
m_is_dirty = true;
|
|
notify_ui = true;
|
|
LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString());
|
|
} else
|
|
++it;
|
|
}
|
|
}
|
|
// update UI
|
|
if (notify_ui && m_client_interface) {
|
|
m_client_interface->BannedListChanged();
|
|
}
|
|
}
|
|
|
|
bool BanMan::BannedSetIsDirty()
|
|
{
|
|
LOCK(m_cs_banned);
|
|
return m_is_dirty;
|
|
}
|
|
|
|
void BanMan::SetBannedSetDirty(bool dirty)
|
|
{
|
|
LOCK(m_cs_banned); //reuse m_banned lock for the m_is_dirty flag
|
|
m_is_dirty = dirty;
|
|
}
|