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