Branch data 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 : 1554 : BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t default_ban_time) 18 [ + - ]: 1554 : : m_client_interface(client_interface), m_ban_db(std::move(ban_file)), m_default_ban_time(default_ban_time) 19 : : { 20 [ + - ]: 1554 : LoadBanlist(); 21 [ + - ]: 1554 : DumpBanlist(); 22 : 1554 : } 23 : : 24 : 1554 : BanMan::~BanMan() 25 : : { 26 [ + - ]: 1554 : DumpBanlist(); 27 : 1554 : } 28 : : 29 : 1554 : void BanMan::LoadBanlist() 30 : : { 31 : 1554 : LOCK(m_cs_banned); 32 : : 33 [ - + # # : 1554 : if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated); # # ] 34 : : 35 : 1554 : const auto start{SteadyClock::now()}; 36 [ + - + + ]: 1554 : if (m_ban_db.Read(m_banned)) { 37 [ + - ]: 762 : SweepBanned(); // sweep out unused entries 38 : : 39 [ + - + - : 762 : LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(), # # # # # # # # # # ] 40 : : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); 41 : 762 : } else { 42 [ + - + - : 792 : LogPrintf("Recreating the banlist database\n"); + - ] 43 [ + - ]: 792 : m_banned = {}; 44 : 792 : m_is_dirty = true; 45 : : } 46 : 1554 : } 47 : : 48 : 17000 : void BanMan::DumpBanlist() 49 : : { 50 [ + + - + ]: 17000 : static Mutex dump_mutex; 51 : 17000 : LOCK(dump_mutex); 52 : : 53 : 17000 : banmap_t banmap; 54 : : { 55 [ + - + - ]: 17000 : LOCK(m_cs_banned); 56 [ + - ]: 17000 : SweepBanned(); 57 [ + - + + ]: 17000 : if (!BannedSetIsDirty()) return; 58 [ + - ]: 14140 : banmap = m_banned; 59 [ + - ]: 14140 : SetBannedSetDirty(false); 60 [ + + ]: 17000 : } 61 : : 62 : 14140 : const auto start{SteadyClock::now()}; 63 [ + - + + ]: 14140 : if (!m_ban_db.Write(banmap)) { 64 [ + - ]: 1258 : SetBannedSetDirty(true); 65 : 1258 : } 66 : : 67 [ + - + + : 14140 : LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(), + - + - + - + - + - ] 68 : : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); 69 [ - + ]: 17000 : } 70 : : 71 : 988 : void BanMan::ClearBanned() 72 : : { 73 : : { 74 : 1161 : LOCK(m_cs_banned); 75 : 988 : m_banned.clear(); 76 : 988 : m_is_dirty = true; 77 : 988 : } 78 : 988 : DumpBanlist(); //store banlist to disk 79 [ - + ]: 988 : if (m_client_interface) m_client_interface->BannedListChanged(); 80 : 988 : } 81 : : 82 : 370895 : bool BanMan::IsDiscouraged(const CNetAddr& net_addr) 83 : : { 84 : 370895 : LOCK(m_cs_banned); 85 [ + - + - : 370895 : return m_discouraged.contains(net_addr.GetAddrBytes()); + - ] 86 : 370895 : } 87 : : 88 : 372842 : bool BanMan::IsBanned(const CNetAddr& net_addr) 89 : : { 90 : 372842 : auto current_time = GetTime(); 91 : 372842 : LOCK(m_cs_banned); 92 [ + + ]: 392961 : for (const auto& it : m_banned) { 93 [ + - ]: 18996 : CSubNet sub_net = it.first; 94 : 18996 : CBanEntry ban_entry = it.second; 95 : : 96 [ - + + + : 20550 : if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) { + - + + ] 97 : 431 : return true; 98 : : } 99 [ + + ]: 18996 : } 100 : 372411 : return false; 101 : 372842 : } 102 : : 103 : 863 : bool BanMan::IsBanned(const CSubNet& sub_net) 104 : : { 105 : 863 : auto current_time = GetTime(); 106 : 863 : LOCK(m_cs_banned); 107 [ + - ]: 863 : banmap_t::iterator i = m_banned.find(sub_net); 108 [ + + ]: 863 : if (i != m_banned.end()) { 109 : 234 : CBanEntry ban_entry = (*i).second; 110 [ + + ]: 234 : if (current_time < ban_entry.nBanUntil) { 111 : 149 : return true; 112 : : } 113 : 85 : } 114 : 714 : return false; 115 : 863 : } 116 : : 117 : 4835 : void BanMan::Ban(const CNetAddr& net_addr, int64_t ban_time_offset, bool since_unix_epoch) 118 : : { 119 : 4835 : CSubNet sub_net(net_addr); 120 [ + - ]: 4835 : Ban(sub_net, ban_time_offset, since_unix_epoch); 121 : 4835 : } 122 : : 123 : 913 : void BanMan::Discourage(const CNetAddr& net_addr) 124 : : { 125 : 913 : LOCK(m_cs_banned); 126 [ + - + - : 913 : m_discouraged.insert(net_addr.GetAddrBytes()); + - ] 127 : 913 : } 128 : : 129 : 13895 : void BanMan::Ban(const CSubNet& sub_net, int64_t ban_time_offset, bool since_unix_epoch) 130 : : { 131 : 13895 : CBanEntry ban_entry(GetTime()); 132 : : 133 : 13895 : int64_t normalized_ban_time_offset = ban_time_offset; 134 : 13895 : bool normalized_since_unix_epoch = since_unix_epoch; 135 [ + + ]: 13895 : if (ban_time_offset <= 0) { 136 : 13107 : normalized_ban_time_offset = m_default_ban_time; 137 : 13107 : normalized_since_unix_epoch = false; 138 : 13107 : } 139 [ + + ]: 13895 : ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + normalized_ban_time_offset; 140 : : 141 : : { 142 : 13895 : LOCK(m_cs_banned); 143 [ + - + + ]: 13895 : if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) { 144 [ + - ]: 11144 : m_banned[sub_net] = ban_entry; 145 : 11144 : m_is_dirty = true; 146 : 11144 : } else 147 : 2751 : return; 148 [ - + + ]: 13895 : } 149 [ + - ]: 11144 : if (m_client_interface) m_client_interface->BannedListChanged(); 150 : : 151 : : //store banlist to disk immediately 152 : 11144 : DumpBanlist(); 153 : 13895 : } 154 : : 155 : 4239 : bool BanMan::Unban(const CNetAddr& net_addr) 156 : : { 157 : 4239 : CSubNet sub_net(net_addr); 158 [ + - ]: 4239 : return Unban(sub_net); 159 : 4239 : } 160 : : 161 : 4621 : bool BanMan::Unban(const CSubNet& sub_net) 162 : : { 163 : : { 164 : 4621 : LOCK(m_cs_banned); 165 [ + - + + ]: 4621 : if (m_banned.erase(sub_net) == 0) return false; 166 : 1054 : m_is_dirty = true; 167 [ - + + ]: 4621 : } 168 [ - + ]: 1054 : if (m_client_interface) m_client_interface->BannedListChanged(); 169 : 1054 : DumpBanlist(); //store banlist to disk immediately 170 : 1054 : return true; 171 : 4621 : } 172 : : 173 : 5821 : void BanMan::GetBanned(banmap_t& banmap) 174 : : { 175 : 5821 : LOCK(m_cs_banned); 176 : : // Sweep the banlist so expired bans are not returned 177 [ + - ]: 5821 : SweepBanned(); 178 [ + - ]: 5821 : banmap = m_banned; //create a thread safe copy 179 : 5821 : } 180 : : 181 : 23583 : void BanMan::SweepBanned() 182 : : { 183 : 23583 : AssertLockHeld(m_cs_banned); 184 : : 185 : 23583 : int64_t now = GetTime(); 186 : 23583 : bool notify_ui = false; 187 : 23583 : banmap_t::iterator it = m_banned.begin(); 188 [ + + ]: 716725 : while (it != m_banned.end()) { 189 : 693142 : CSubNet sub_net = (*it).first; 190 : 693142 : CBanEntry ban_entry = (*it).second; 191 [ + - + + : 693142 : if (!sub_net.IsValid() || now > ban_entry.nBanUntil) { + + ] 192 [ + - ]: 2596 : m_banned.erase(it++); 193 : 2596 : m_is_dirty = true; 194 : 2596 : notify_ui = true; 195 [ + - + - : 2596 : LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString()); # # # # # # # # ] 196 : 2596 : } else { 197 : 690546 : ++it; 198 : : } 199 : 693142 : } 200 : : 201 : : // update UI 202 [ + + + - ]: 23583 : if (notify_ui && m_client_interface) { 203 : 0 : m_client_interface->BannedListChanged(); 204 : 0 : } 205 : 23583 : } 206 : : 207 : 17000 : bool BanMan::BannedSetIsDirty() 208 : : { 209 : 17000 : LOCK(m_cs_banned); 210 : 17000 : return m_is_dirty; 211 : 17000 : } 212 : : 213 : 15398 : void BanMan::SetBannedSetDirty(bool dirty) 214 : : { 215 : 15398 : LOCK(m_cs_banned); //reuse m_banned lock for the m_is_dirty flag 216 : 15398 : m_is_dirty = dirty; 217 : 15398 : }