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