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