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 362 : 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 181 : { 19 : LoadBanlist(); 20 : DumpBanlist(); 21 181 : } 22 : 23 362 : BanMan::~BanMan() 24 181 : { 25 181 : DumpBanlist(); 26 362 : } 27 : 28 181 : void BanMan::LoadBanlist() 29 : { 30 181 : LOCK(m_banned_mutex); 31 : 32 181 : if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated); 33 : 34 181 : const auto start{SteadyClock::now()}; 35 181 : if (m_ban_db.Read(m_banned)) { 36 3 : SweepBanned(); // sweep out unused entries 37 : 38 3 : LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(), 39 : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); 40 3 : } else { 41 178 : LogPrintf("Recreating the banlist database\n"); 42 178 : m_banned = {}; 43 178 : m_is_dirty = true; 44 : } 45 181 : } 46 : 47 378 : void BanMan::DumpBanlist() 48 : { 49 378 : static Mutex dump_mutex; 50 378 : LOCK(dump_mutex); 51 : 52 378 : banmap_t banmap; 53 : { 54 378 : LOCK(m_banned_mutex); 55 378 : SweepBanned(); 56 378 : if (!m_is_dirty) return; 57 195 : banmap = m_banned; 58 195 : m_is_dirty = false; 59 378 : } 60 : 61 195 : const auto start{SteadyClock::now()}; 62 195 : if (!m_ban_db.Write(banmap)) { 63 0 : LOCK(m_banned_mutex); 64 0 : m_is_dirty = true; 65 0 : } 66 : 67 195 : LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(), 68 : Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); 69 378 : } 70 : 71 7 : void BanMan::ClearBanned() 72 : { 73 : { 74 7 : LOCK(m_banned_mutex); 75 7 : m_banned.clear(); 76 7 : m_is_dirty = true; 77 7 : } 78 7 : DumpBanlist(); //store banlist to disk 79 7 : if (m_client_interface) m_client_interface->BannedListChanged(); 80 7 : } 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 11 : bool BanMan::IsDiscouraged(const CNetAddr& net_addr) 93 : { 94 11 : LOCK(m_banned_mutex); 95 11 : return m_discouraged.contains(net_addr.GetAddrBytes()); 96 11 : } 97 : 98 4 : bool BanMan::IsBanned(const CNetAddr& net_addr) 99 : { 100 4 : auto current_time = GetTime(); 101 4 : LOCK(m_banned_mutex); 102 4 : for (const auto& it : m_banned) { 103 2 : CSubNet sub_net = it.first; 104 2 : CBanEntry ban_entry = it.second; 105 : 106 2 : if (current_time < ban_entry.nBanUntil && sub_net.Match(net_addr)) { 107 2 : return true; 108 : } 109 2 : } 110 2 : return false; 111 4 : } 112 : 113 5 : bool BanMan::IsBanned(const CSubNet& sub_net) 114 : { 115 5 : auto current_time = GetTime(); 116 5 : LOCK(m_banned_mutex); 117 5 : banmap_t::iterator i = m_banned.find(sub_net); 118 5 : 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 5 : return false; 125 5 : } 126 : 127 2 : void BanMan::Ban(const CNetAddr& net_addr, int64_t ban_time_offset, bool since_unix_epoch) 128 : { 129 2 : CSubNet sub_net(net_addr); 130 2 : Ban(sub_net, ban_time_offset, since_unix_epoch); 131 2 : } 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 7 : void BanMan::Ban(const CSubNet& sub_net, int64_t ban_time_offset, bool since_unix_epoch) 140 : { 141 7 : CBanEntry ban_entry(GetTime()); 142 : 143 7 : int64_t normalized_ban_time_offset = ban_time_offset; 144 7 : bool normalized_since_unix_epoch = since_unix_epoch; 145 7 : if (ban_time_offset <= 0) { 146 5 : normalized_ban_time_offset = m_default_ban_time; 147 5 : normalized_since_unix_epoch = false; 148 5 : } 149 7 : ban_entry.nBanUntil = (normalized_since_unix_epoch ? 0 : GetTime()) + normalized_ban_time_offset; 150 : 151 : { 152 7 : LOCK(m_banned_mutex); 153 7 : if (m_banned[sub_net].nBanUntil < ban_entry.nBanUntil) { 154 7 : m_banned[sub_net] = ban_entry; 155 7 : m_is_dirty = true; 156 7 : } else 157 0 : return; 158 7 : } 159 7 : if (m_client_interface) m_client_interface->BannedListChanged(); 160 : 161 : //store banlist to disk immediately 162 7 : DumpBanlist(); 163 7 : } 164 : 165 1 : bool BanMan::Unban(const CNetAddr& net_addr) 166 : { 167 1 : CSubNet sub_net(net_addr); 168 1 : return Unban(sub_net); 169 1 : } 170 : 171 2 : bool BanMan::Unban(const CSubNet& sub_net) 172 : { 173 : { 174 2 : LOCK(m_banned_mutex); 175 2 : if (m_banned.erase(sub_net) == 0) return false; 176 2 : m_is_dirty = true; 177 2 : } 178 2 : if (m_client_interface) m_client_interface->BannedListChanged(); 179 2 : DumpBanlist(); //store banlist to disk immediately 180 2 : return true; 181 2 : } 182 : 183 10 : void BanMan::GetBanned(banmap_t& banmap) 184 : { 185 10 : LOCK(m_banned_mutex); 186 : // Sweep the banlist so expired bans are not returned 187 10 : SweepBanned(); 188 10 : banmap = m_banned; //create a thread safe copy 189 10 : } 190 : 191 391 : void BanMan::SweepBanned() 192 : { 193 391 : AssertLockHeld(m_banned_mutex); 194 : 195 391 : int64_t now = GetTime(); 196 391 : bool notify_ui = false; 197 391 : banmap_t::iterator it = m_banned.begin(); 198 409 : while (it != m_banned.end()) { 199 18 : CSubNet sub_net = (*it).first; 200 18 : CBanEntry ban_entry = (*it).second; 201 18 : if (!sub_net.IsValid() || now > ban_entry.nBanUntil) { 202 0 : m_banned.erase(it++); 203 0 : m_is_dirty = true; 204 0 : notify_ui = true; 205 0 : LogPrint(BCLog::NET, "Removed banned node address/subnet: %s\n", sub_net.ToString()); 206 0 : } else { 207 18 : ++it; 208 : } 209 18 : } 210 : 211 : // update UI 212 391 : if (notify_ui && m_client_interface) { 213 0 : m_client_interface->BannedListChanged(); 214 0 : } 215 391 : }