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