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