// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2017 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 #include #include #include #include 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...")); int64_t n_start = GetTimeMillis(); m_is_dirty = false; banmap_t banmap; if (m_ban_db.Read(banmap)) { SetBanned(banmap); // thread save setter SetBannedSetDirty(false); // no need to write down, just read data SweepBanned(); // sweep out unused entries LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n", m_banned.size(), GetTimeMillis() - n_start); } else { LogPrintf("Recreating banlist.dat\n"); SetBannedSetDirty(true); // force write DumpBanlist(); } } BanMan::~BanMan() { DumpBanlist(); } void BanMan::DumpBanlist() { SweepBanned(); // clean unused entries (if bantime has expired) if (!BannedSetIsDirty()) return; int64_t n_start = GetTimeMillis(); banmap_t banmap; GetBanned(banmap); if (m_ban_db.Write(banmap)) { SetBannedSetDirty(false); } LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %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(); } int BanMan::IsBannedLevel(CNetAddr net_addr) { // Returns the most severe level of banning that applies to this address. // 0 - Not banned // 1 - Automatic misbehavior ban // 2 - Any other ban int level = 0; 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)) { if (ban_entry.banReason != BanReasonNodeMisbehaving) return 2; level = 1; } } return level; } bool BanMan::IsBanned(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(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, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch) { CSubNet sub_net(net_addr); Ban(sub_net, ban_reason, ban_time_offset, since_unix_epoch); } void BanMan::Ban(const CSubNet& sub_net, const BanReason& ban_reason, int64_t ban_time_offset, bool since_unix_epoch) { CBanEntry ban_entry(GetTime(), ban_reason); 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 if user requested ban if (ban_reason == BanReasonManuallyAdded) 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::SetBanned(const banmap_t& banmap) { LOCK(m_cs_banned); m_banned = banmap; m_is_dirty = true; } 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, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, 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; }