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