Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto 2 : // Copyright (c) 2009-2022 The Bitcoin Core developers 3 : // Distributed under the MIT software license, see the accompanying 4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 : 6 : #include <banman.h> 7 : 8 : #include <common/system.h> 9 : #include <logging.h> 10 : #include <netaddress.h> 11 : #include <node/interface_ui.h> 12 : #include <sync.h> 13 : #include <util/time.h> 14 : #include <util/translation.h> 15 : 16 : 17 1 : BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time) 18 1 : : m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time) 19 : { 20 1 : LoadBanlist(); 21 1 : DumpBanlist(); 22 1 : } 23 : 24 1 : BanMan::~BanMan() 25 : { 26 1 : DumpBanlist(); 27 1 : } 28 : 29 1 : void BanMan::LoadBanlist() 30 : { 31 1 : LOCK(m_cs_banned); 32 : 33 1 : if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated); 34 : 35 1 : const auto start{SteadyClock::now()}; 36 1 : if (m_ban_db.Read(m_banned)) { 37 0 : SweepBanned(); // sweep out unused entries 38 : 39 0 : LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(), 40 : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); 41 0 : } else { 42 1 : LogPrintf("Recreating the banlist database\n"); 43 1 : m_banned = {}; 44 1 : m_is_dirty = true; 45 : } 46 1 : } 47 : 48 2 : void BanMan::DumpBanlist() 49 : { 50 2 : static Mutex dump_mutex; 51 2 : LOCK(dump_mutex); 52 : 53 2 : banmap_t banmap; 54 : { 55 2 : LOCK(m_cs_banned); 56 2 : SweepBanned(); 57 2 : if (!BannedSetIsDirty()) return; 58 1 : banmap = m_banned; 59 1 : SetBannedSetDirty(false); 60 2 : } 61 : 62 1 : const auto start{SteadyClock::now()}; 63 1 : if (!m_ban_db.Write(banmap)) { 64 0 : SetBannedSetDirty(true); 65 0 : } 66 : 67 1 : LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(), 68 : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); 69 2 : } 70 : 71 0 : void BanMan::ClearBanned() 72 : { 73 : { 74 2 : LOCK(m_cs_banned); 75 0 : m_banned.clear(); 76 0 : m_is_dirty = true; 77 0 : } 78 0 : DumpBanlist(); //store banlist to disk 79 0 : if (m_client_interface) m_client_interface->BannedListChanged(); 80 0 : } 81 : 82 0 : bool BanMan::IsDiscouraged(const CNetAddr& net_addr) 83 : { 84 0 : LOCK(m_cs_banned); 85 0 : return m_discouraged.contains(net_addr.GetAddrBytes()); 86 0 : } 87 : 88 0 : bool BanMan::IsBanned(const CNetAddr& net_addr) 89 : { 90 0 : auto current_time = GetTime(); 91 0 : LOCK(m_cs_banned); 92 1 : for (const auto& it : m_banned) { 93 0 : CSubNet sub_net = it.first; 94 0 : CBanEntry ban_entry = it.second; 95 : 96 1 : if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) { 97 0 : return true; 98 : } 99 0 : } 100 0 : return false; 101 0 : } 102 : 103 0 : bool BanMan::IsBanned(const CSubNet& sub_net) 104 : { 105 0 : auto current_time = GetTime(); 106 0 : LOCK(m_cs_banned); 107 0 : banmap_t::iterator i = m_banned.find(sub_net); 108 0 : if (i != m_banned.end()) { 109 0 : CBanEntry ban_entry = (*i).second; 110 0 : if (current_time < ban_entry.nBanUntil) { 111 0 : return true; 112 : } 113 0 : } 114 0 : return false; 115 0 : } 116 : 117 0 : void BanMan::Ban(const CNetAddr& net_addr, int64_t ban_time_offset, bool since_unix_epoch) 118 : { 119 0 : CSubNet sub_net(net_addr); 120 0 : Ban(sub_net, ban_time_offset, since_unix_epoch); 121 0 : } 122 : 123 0 : void BanMan::Discourage(const CNetAddr& net_addr) 124 : { 125 0 : LOCK(m_cs_banned); 126 0 : m_discouraged.insert(net_addr.GetAddrBytes()); 127 0 : } 128 : 129 0 : void BanMan::Ban(const CSubNet& sub_net, int64_t ban_time_offset, bool since_unix_epoch) 130 : { 131 0 : CBanEntry ban_entry(GetTime()); 132 : 133 0 : int64_t normalized_ban_time_offset = ban_time_offset; 134 0 : bool normalized_since_unix_epoch = since_unix_epoch; 135 0 : if (ban_time_offset <= 0) { 136 0 : normalized_ban_time_offset = m_default_ban_time; 137 0 : normalized_since_unix_epoch = false; 138 0 : } 139 0 : ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + normalized_ban_time_offset; 140 : 141 : { 142 0 : LOCK(m_cs_banned); 143 0 : if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) { 144 0 : m_banned[sub_net] = ban_entry; 145 0 : m_is_dirty = true; 146 0 : } else 147 0 : return; 148 0 : } 149 0 : if (m_client_interface) m_client_interface->BannedListChanged(); 150 : 151 : //store banlist to disk immediately 152 0 : DumpBanlist(); 153 0 : } 154 : 155 0 : bool BanMan::Unban(const CNetAddr& net_addr) 156 : { 157 0 : CSubNet sub_net(net_addr); 158 0 : return Unban(sub_net); 159 0 : } 160 : 161 0 : bool BanMan::Unban(const CSubNet& sub_net) 162 : { 163 : { 164 0 : LOCK(m_cs_banned); 165 0 : if (m_banned.erase(sub_net) == 0) return false; 166 0 : m_is_dirty = true; 167 0 : } 168 0 : if (m_client_interface) m_client_interface->BannedListChanged(); 169 0 : DumpBanlist(); //store banlist to disk immediately 170 0 : return true; 171 0 : } 172 : 173 0 : void BanMan::GetBanned(banmap_t& banmap) 174 : { 175 0 : LOCK(m_cs_banned); 176 : // Sweep the banlist so expired bans are not returned 177 0 : SweepBanned(); 178 0 : banmap = m_banned; //create a thread safe copy 179 0 : } 180 : 181 2 : void BanMan::SweepBanned() 182 : { 183 2 : AssertLockHeld(m_cs_banned); 184 : 185 2 : int64_t now = GetTime(); 186 2 : bool notify_ui = false; 187 2 : banmap_t::iterator it = m_banned.begin(); 188 2 : while (it != m_banned.end()) { 189 0 : CSubNet sub_net = (*it).first; 190 0 : CBanEntry ban_entry = (*it).second; 191 0 : if (!sub_net.IsValid() || now > ban_entry.nBanUntil) { 192 0 : m_banned.erase(it++); 193 0 : m_is_dirty = true; 194 0 : notify_ui = true; 195 0 : LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString()); 196 0 : } else { 197 0 : ++it; 198 : } 199 0 : } 200 : 201 : // update UI 202 2 : if (notify_ui && m_client_interface) { 203 0 : m_client_interface->BannedListChanged(); 204 0 : } 205 2 : } 206 : 207 2 : bool BanMan::BannedSetIsDirty() 208 : { 209 2 : LOCK(m_cs_banned); 210 2 : return m_is_dirty; 211 2 : } 212 : 213 1 : void BanMan::SetBannedSetDirty(bool dirty) 214 : { 215 1 : LOCK(m_cs_banned); //reuse m_banned lock for the m_is_dirty flag 216 1 : m_is_dirty = dirty; 217 1 : }