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