Coverage Report

Created: 2026-04-29 19:21

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