Coverage Report

Created: 2024-10-21 15:10

/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
}