Changeset View
Changeset View
Standalone View
Standalone View
src/banman.cpp
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2017 The Bitcoin Core developers | // Copyright (c) 2009-2017 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <banman.h> | #include <banman.h> | ||||
#include <netaddress.h> | #include <netaddress.h> | ||||
#include <ui_interface.h> | #include <ui_interface.h> | ||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <util/time.h> | #include <util/time.h> | ||||
BanMan::BanMan(fs::path ban_file, const CChainParams &chainParams, | BanMan::BanMan(fs::path ban_file, const CChainParams &chainparams, | ||||
CClientUIInterface *client_interface, int64_t default_ban_time) | CClientUIInterface *client_interface, int64_t default_ban_time) | ||||
: m_client_interface(client_interface), | : m_client_interface(client_interface), | ||||
m_ban_db(std::move(ban_file), chainParams), | m_ban_db(std::move(ban_file), chainparams), | ||||
m_default_ban_time(default_ban_time) { | m_default_ban_time(default_ban_time) { | ||||
if (m_client_interface) { | if (m_client_interface) { | ||||
m_client_interface->InitMessage(_("Loading banlist...")); | m_client_interface->InitMessage(_("Loading banlist...")); | ||||
} | } | ||||
int64_t nStart = GetTimeMillis(); | int64_t n_start = GetTimeMillis(); | ||||
m_is_dirty = false; | m_is_dirty = false; | ||||
banmap_t banmap; | banmap_t banmap; | ||||
if (m_ban_db.Read(banmap)) { | if (m_ban_db.Read(banmap)) { | ||||
// thread save setter | // thread save setter | ||||
SetBanned(banmap); | SetBanned(banmap); | ||||
// no need to write down, just read data | // no need to write down, just read data | ||||
SetBannedSetDirty(false); | SetBannedSetDirty(false); | ||||
// sweep out unused entries | // sweep out unused entries | ||||
SweepBanned(); | SweepBanned(); | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Loaded %d banned node ips/subnets from banlist.dat %dms\n", | "Loaded %d banned node ips/subnets from banlist.dat %dms\n", | ||||
banmap.size(), GetTimeMillis() - nStart); | banmap.size(), GetTimeMillis() - n_start); | ||||
} else { | } else { | ||||
LogPrintf("Invalid or missing banlist.dat; recreating\n"); | LogPrintf("Invalid or missing banlist.dat; recreating\n"); | ||||
// force write | // force write | ||||
SetBannedSetDirty(true); | SetBannedSetDirty(true); | ||||
DumpBanlist(); | DumpBanlist(); | ||||
} | } | ||||
} | } | ||||
BanMan::~BanMan() { | BanMan::~BanMan() { | ||||
DumpBanlist(); | DumpBanlist(); | ||||
} | } | ||||
void BanMan::DumpBanlist() { | void BanMan::DumpBanlist() { | ||||
// clean unused entries (if bantime has expired) | // clean unused entries (if bantime has expired) | ||||
SweepBanned(); | SweepBanned(); | ||||
if (!BannedSetIsDirty()) { | if (!BannedSetIsDirty()) { | ||||
return; | return; | ||||
} | } | ||||
int64_t nStart = GetTimeMillis(); | int64_t n_start = GetTimeMillis(); | ||||
banmap_t banmap; | banmap_t banmap; | ||||
GetBanned(banmap); | GetBanned(banmap); | ||||
if (m_ban_db.Write(banmap)) { | if (m_ban_db.Write(banmap)) { | ||||
SetBannedSetDirty(false); | SetBannedSetDirty(false); | ||||
} | } | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Flushed %d banned node ips/subnets to banlist.dat %dms\n", | "Flushed %d banned node ips/subnets to banlist.dat %dms\n", | ||||
banmap.size(), GetTimeMillis() - nStart); | banmap.size(), GetTimeMillis() - n_start); | ||||
} | } | ||||
void BanMan::ClearBanned() { | void BanMan::ClearBanned() { | ||||
{ | { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
m_banned.clear(); | m_banned.clear(); | ||||
m_is_dirty = true; | m_is_dirty = true; | ||||
} | } | ||||
// store banlist to disk | // store banlist to disk | ||||
DumpBanlist(); | DumpBanlist(); | ||||
if (m_client_interface) { | if (m_client_interface) { | ||||
m_client_interface->BannedListChanged(); | m_client_interface->BannedListChanged(); | ||||
} | } | ||||
} | } | ||||
bool BanMan::IsBanned(CNetAddr netAddr) { | bool BanMan::IsBanned(CNetAddr net_addr) { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
for (const auto &it : m_banned) { | for (const auto &it : m_banned) { | ||||
CSubNet subNet = it.first; | CSubNet sub_net = it.first; | ||||
CBanEntry banEntry = it.second; | CBanEntry ban_entry = it.second; | ||||
if (subNet.Match(netAddr) && GetTime() < banEntry.nBanUntil) { | if (sub_net.Match(net_addr) && GetTime() < ban_entry.nBanUntil) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool BanMan::IsBanned(CSubNet subNet) { | bool BanMan::IsBanned(CSubNet sub_net) { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
banmap_t::iterator i = m_banned.find(subNet); | banmap_t::iterator i = m_banned.find(sub_net); | ||||
if (i != m_banned.end()) { | if (i != m_banned.end()) { | ||||
CBanEntry banEntry = (*i).second; | CBanEntry ban_entry = (*i).second; | ||||
if (GetTime() < banEntry.nBanUntil) { | if (GetTime() < ban_entry.nBanUntil) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void BanMan::Ban(const CNetAddr &netAddr, const BanReason &banReason, | void BanMan::Ban(const CNetAddr &net_addr, const BanReason &ban_reason, | ||||
int64_t bantimeoffset, bool sinceUnixEpoch) { | int64_t ban_time_offset, bool since_unix_epoch) { | ||||
CSubNet subNet(netAddr); | CSubNet sub_net(net_addr); | ||||
Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); | Ban(sub_net, ban_reason, ban_time_offset, since_unix_epoch); | ||||
} | } | ||||
void BanMan::Ban(const CSubNet &subNet, const BanReason &banReason, | void BanMan::Ban(const CSubNet &sub_net, const BanReason &ban_reason, | ||||
int64_t bantimeoffset, bool sinceUnixEpoch) { | int64_t ban_time_offset, bool since_unix_epoch) { | ||||
CBanEntry banEntry(GetTime(), banReason); | CBanEntry ban_entry(GetTime(), ban_reason); | ||||
int64_t normalized_bantimeoffset = bantimeoffset; | int64_t normalized_ban_time_offset = ban_time_offset; | ||||
bool normalized_sinceUnixEpoch = sinceUnixEpoch; | bool normalized_since_unix_epoch = since_unix_epoch; | ||||
if (bantimeoffset <= 0) { | if (ban_time_offset <= 0) { | ||||
normalized_bantimeoffset = m_default_ban_time; | normalized_ban_time_offset = m_default_ban_time; | ||||
normalized_sinceUnixEpoch = false; | normalized_since_unix_epoch = false; | ||||
} | } | ||||
banEntry.nBanUntil = | ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + | ||||
(normalized_sinceUnixEpoch ? 0 : GetTime()) + normalized_bantimeoffset; | normalized_ban_time_offset; | ||||
{ | { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
if (m_banned[subNet].nBanUntil < banEntry.nBanUntil) { | if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) { | ||||
m_banned[subNet] = banEntry; | m_banned[sub_net] = ban_entry; | ||||
m_is_dirty = true; | m_is_dirty = true; | ||||
} else { | } else { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
if (m_client_interface) { | if (m_client_interface) { | ||||
m_client_interface->BannedListChanged(); | m_client_interface->BannedListChanged(); | ||||
} | } | ||||
// store banlist to disk immediately if user requested ban | // store banlist to disk immediately if user requested ban | ||||
if (banReason == BanReasonManuallyAdded) { | if (ban_reason == BanReasonManuallyAdded) { | ||||
DumpBanlist(); | DumpBanlist(); | ||||
} | } | ||||
} | } | ||||
bool BanMan::Unban(const CNetAddr &netAddr) { | bool BanMan::Unban(const CNetAddr &net_addr) { | ||||
CSubNet subNet(netAddr); | CSubNet sub_net(net_addr); | ||||
return Unban(subNet); | return Unban(sub_net); | ||||
} | } | ||||
bool BanMan::Unban(const CSubNet &subNet) { | bool BanMan::Unban(const CSubNet &sub_net) { | ||||
{ | { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
if (m_banned.erase(subNet) == 0) { | if (m_banned.erase(sub_net) == 0) { | ||||
return false; | return false; | ||||
} | } | ||||
m_is_dirty = true; | m_is_dirty = true; | ||||
} | } | ||||
if (m_client_interface) { | if (m_client_interface) { | ||||
m_client_interface->BannedListChanged(); | m_client_interface->BannedListChanged(); | ||||
} | } | ||||
// store banlist to disk immediately | // store banlist to disk immediately | ||||
DumpBanlist(); | DumpBanlist(); | ||||
return true; | return true; | ||||
} | } | ||||
void BanMan::GetBanned(banmap_t &banMap) { | void BanMan::GetBanned(banmap_t &banmap) { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
// Sweep the banlist so expired bans are not returned | // Sweep the banlist so expired bans are not returned | ||||
SweepBanned(); | SweepBanned(); | ||||
// create a thread safe copy | // create a thread safe copy | ||||
banMap = m_banned; | banmap = m_banned; | ||||
} | } | ||||
void BanMan::SetBanned(const banmap_t &banMap) { | void BanMan::SetBanned(const banmap_t &banmap) { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
m_banned = banMap; | m_banned = banmap; | ||||
m_is_dirty = true; | m_is_dirty = true; | ||||
} | } | ||||
void BanMan::SweepBanned() { | void BanMan::SweepBanned() { | ||||
int64_t now = GetTime(); | int64_t now = GetTime(); | ||||
bool notifyUI = false; | bool notify_ui = false; | ||||
{ | { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
banmap_t::iterator it = m_banned.begin(); | banmap_t::iterator it = m_banned.begin(); | ||||
while (it != m_banned.end()) { | while (it != m_banned.end()) { | ||||
CSubNet subNet = (*it).first; | CSubNet sub_net = (*it).first; | ||||
CBanEntry banEntry = (*it).second; | CBanEntry ban_entry = (*it).second; | ||||
if (now > banEntry.nBanUntil) { | if (now > ban_entry.nBanUntil) { | ||||
m_banned.erase(it++); | m_banned.erase(it++); | ||||
m_is_dirty = true; | m_is_dirty = true; | ||||
notifyUI = true; | notify_ui = true; | ||||
LogPrint( | LogPrint( | ||||
BCLog::NET, | BCLog::NET, | ||||
"%s: Removed banned node ip/subnet from banlist.dat: %s\n", | "%s: Removed banned node ip/subnet from banlist.dat: %s\n", | ||||
__func__, subNet.ToString()); | __func__, sub_net.ToString()); | ||||
} else { | } else { | ||||
++it; | ++it; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// update UI | // update UI | ||||
if (notifyUI && m_client_interface) { | if (notify_ui && m_client_interface) { | ||||
m_client_interface->BannedListChanged(); | m_client_interface->BannedListChanged(); | ||||
} | } | ||||
} | } | ||||
bool BanMan::BannedSetIsDirty() { | bool BanMan::BannedSetIsDirty() { | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
return m_is_dirty; | return m_is_dirty; | ||||
} | } | ||||
void BanMan::SetBannedSetDirty(bool dirty) { | void BanMan::SetBannedSetDirty(bool dirty) { | ||||
// reuse m_banned lock for the m_is_dirty flag | // reuse m_banned lock for the m_is_dirty flag | ||||
LOCK(m_cs_banned); | LOCK(m_cs_banned); | ||||
m_is_dirty = dirty; | m_is_dirty = dirty; | ||||
} | } |