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 : 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 : 0 : 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 : 1 : bool BanMan::IsDiscouraged(const CNetAddr& net_addr) 83 : : { 84 : 1 : LOCK(m_cs_banned); 85 [ + - ][ + - ]: 1 : return m_discouraged.contains(net_addr.GetAddrBytes()); [ + - ] 86 : 1 : } 87 : : 88 : 1 : bool BanMan::IsBanned(const CNetAddr& net_addr) 89 : : { 90 : 1 : auto current_time = GetTime(); 91 : 1 : LOCK(m_cs_banned); 92 [ - + ]: 2 : 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 : 1 : return false; 101 : 1 : } 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 : }