Skip to content

Commit e650bba

Browse files
committed
[rpc] Add listbanned ip command
1 parent c49b351 commit e650bba

File tree

6 files changed

+65
-14
lines changed

6 files changed

+65
-14
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
### RPC and other APIs
22
- #19825 `setban` behavior changed: Banning a subnet that is a subset of a previous ban is a no-op instead of an rpc error.
33
- #19825 `setban` behavior changed: Unbanning a non-banned subnet is a no-op instead of an rpc error.
4-
- #19825 `setban` behavior changed: Added `setban removeall ip` to remove all bans that affect an ip address.
4+
- #19825 `setban` behavior changed: Added `setban removeall ip` to remove all bans that affect an ip address.
5+
- #19825 `listbanned ip` added: Lists ban entries that include ip.

src/banman.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
#include <banman.h>
77

8-
#include <netaddress.h>
98
#include <node/ui_interface.h>
109
#include <util/system.h>
1110
#include <util/time.h>
@@ -154,12 +153,23 @@ void BanMan::UnbanAll(const CNetAddr& net_addr)
154153
DumpBanlist(); //store banlist to disk immediately
155154
}
156155

157-
void BanMan::GetBanned(banmap_t& banmap)
156+
void BanMan::GetBanned(banmap_t& banmap, std::optional<const CNetAddr> filterForIp)
158157
{
159158
LOCK(m_cs_banned);
160159
// Sweep the banlist so expired bans are not returned
161160
SweepBanned();
162161
banmap = m_banned; //create a thread safe copy
162+
163+
if (filterForIp) {
164+
for (auto it = banmap.begin(); it != banmap.end();) {
165+
CSubNet sub_net = it->first;
166+
if (!sub_net.Match(filterForIp.value())) {
167+
it = banmap.erase(it);
168+
} else {
169+
it++;
170+
}
171+
}
172+
}
163173
}
164174

165175
void BanMan::SetBanned(const banmap_t& banmap)

src/banman.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@
88
#include <addrdb.h>
99
#include <bloom.h>
1010
#include <fs.h>
11+
#include <netaddress.h> // For CNetAddr and CSubNet
1112
#include <net_types.h> // For banmap_t
1213
#include <sync.h>
1314

1415
#include <chrono>
1516
#include <cstdint>
1617
#include <memory>
18+
#include <optional>
1719

1820
// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
1921
static constexpr unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
2022
// How often to dump addresses to banlist.dat
2123
static constexpr std::chrono::minutes DUMP_BANS_INTERVAL{15};
2224

2325
class CClientUIInterface;
24-
class CNetAddr;
25-
class CSubNet;
2626

2727
// Banman manages two related but distinct concepts:
2828
//
@@ -77,7 +77,7 @@ class BanMan
7777

7878
// Removes all ban entries that include net_addr
7979
void UnbanAll(const CNetAddr& net_addr);
80-
void GetBanned(banmap_t& banmap);
80+
void GetBanned(banmap_t& banmap, std::optional<const CNetAddr> filterForIp = {});
8181
void DumpBanlist();
8282

8383
private:

src/rpc/net.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,9 @@ static RPCHelpMan listbanned()
673673
{
674674
return RPCHelpMan{"listbanned",
675675
"\nList all manually banned IPs/Subnets.\n",
676-
{},
676+
{
677+
{"ip", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If IP is provided, only bans which include the IP will be returned."},
678+
},
677679
RPCResult{RPCResult::Type::ARR, "", "",
678680
{
679681
{RPCResult::Type::OBJ, "", "",
@@ -695,8 +697,16 @@ static RPCHelpMan listbanned()
695697
}
696698

697699
banmap_t banMap;
698-
node.banman->GetBanned(banMap);
699-
700+
if (request.params[0].isNull()) {
701+
node.banman->GetBanned(banMap);
702+
} else {
703+
CNetAddr netAddr;
704+
LookupHost(request.params[0].get_str(), netAddr, false);
705+
if (!netAddr.IsValid()) {
706+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Invalid ip address");
707+
}
708+
node.banman->GetBanned(banMap, netAddr);
709+
}
700710
UniValue bannedAddresses(UniValue::VARR);
701711
for (const auto& entry : banMap)
702712
{
@@ -880,7 +890,7 @@ static const CRPCCommand commands[] =
880890
{ "network", "getnettotals", &getnettotals, {} },
881891
{ "network", "getnetworkinfo", &getnetworkinfo, {} },
882892
{ "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} },
883-
{ "network", "listbanned", &listbanned, {} },
893+
{ "network", "listbanned", &listbanned, {"ip"} },
884894
{ "network", "clearbanned", &clearbanned, {} },
885895
{ "network", "setnetworkactive", &setnetworkactive, {"state"} },
886896
{ "network", "getnodeaddresses", &getnodeaddresses, {"count"} },

src/test/rpc_tests.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,14 +365,26 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
365365
adr = find_value(o1, "address");
366366
BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128");
367367

368-
//removeall ipv4 test
368+
//listbanned + removeall ipv4 test
369369
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
370370
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/16 add")));
371371
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add")));
372372
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/32 add")));
373373
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
374374
ar = r.get_array();
375375
BOOST_CHECK_EQUAL(ar.size(), 3U);
376+
BOOST_CHECK_THROW(CallRPC(std::string("listbanned 127.0.0.3/32")), std::runtime_error); // requires single ip, not subnet representation
377+
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned 127.0.0.3")));
378+
ar = r.get_array();
379+
BOOST_CHECK_EQUAL(ar.size(), 2U);
380+
for (size_t i = 0; i < ar.size(); i++) {
381+
o1 = ar[i].get_obj();
382+
auto address = find_value(o1, "address").get_str();
383+
if (address != "127.0.0.0/16" &&
384+
address != "127.0.0.0/24") {
385+
BOOST_FAIL("Unexpected address in banlist");
386+
}
387+
}
376388
BOOST_CHECK_THROW(CallRPC(std::string("setban 127.0.0.3/32 removeall")), std::runtime_error); // cannot removeall subnets
377389
BOOST_CHECK_NO_THROW(CallRPC(std::string("setban 127.0.0.3 removeall"))); // remove all subnets with 127.0.0.3
378390
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
@@ -382,14 +394,26 @@ BOOST_AUTO_TEST_CASE(rpc_ban)
382394
adr = find_value(o1, "address");
383395
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/32");
384396

385-
//removeall ipv6 test
397+
//listbanned + removeall ipv6 test
386398
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
387399
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/96 add")));
388400
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/112 add")));
389401
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128 add")));
390402
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));
391403
ar = r.get_array();
392404
BOOST_CHECK_EQUAL(ar.size(), 3U);
405+
BOOST_CHECK_THROW(CallRPC(std::string("listbanned 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64/128")), std::runtime_error); // requires single ip, not subnet representation
406+
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64")));
407+
ar = r.get_array();
408+
BOOST_CHECK_EQUAL(ar.size(), 2U);
409+
for (size_t i = 0; i < ar.size(); i++) {
410+
o1 = ar[i].get_obj();
411+
auto address = find_value(o1, "address").get_str();
412+
if (address != "2001:4d48:ac57:400:cacf:e9ff::/96" &&
413+
address != "2001:4d48:ac57:400:cacf:e9ff:fe1d:0/112") {
414+
BOOST_FAIL("Unexpected address in banlist");
415+
}
416+
}
393417
BOOST_CHECK_THROW(CallRPC(std::string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64/128 removeall")), std::runtime_error); // cannot removeall subnets
394418
BOOST_CHECK_NO_THROW(CallRPC(std::string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64 removeall"))); // remove all subnets with 127.0.0.3
395419
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned")));

test/functional/p2p_disconnect_ban.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@ def run_test(self):
6666
self.nodes[1].clearbanned()
6767
assert_equal(len(self.nodes[1].listbanned()), 0)
6868

69-
self.log.info("setban removeall: ipv4 test")
69+
self.log.info("setban listbanned and removeall: ipv4 test")
7070
self.nodes[1].setban("127.0.0.1/16", "add")
7171
self.nodes[1].setban("127.0.0.1/24", "add")
7272
self.nodes[1].setban("127.0.0.1/32", "add")
7373
assert_equal(len(self.nodes[1].listbanned()), 3)
74+
assert_raises_rpc_error(-8, "Error: Invalid ip address", self.nodes[1].listbanned, "127.0.0.3/32")
75+
relevant_bans = self.nodes[1].listbanned("127.0.0.3")
76+
assert_equal({x["address"] for x in relevant_bans}, {"127.0.0.0/16", "127.0.0.0/24"})
7477
assert_raises_rpc_error(-8, "Error: setban removeall requires single ip address",
7578
self.nodes[1].setban, "127.0.0.3/32", "removeall")
7679
self.nodes[1].setban("127.0.0.3", "removeall")
@@ -79,11 +82,14 @@ def run_test(self):
7982
assert_equal(listbanned_response[0]["address"], "127.0.0.1/32")
8083
self.nodes[1].clearbanned()
8184

82-
self.log.info("setban removeall: ipv6 test")
85+
self.log.info("setban listbanned and removeall: ipv6 test")
8386
self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/96", "add")
8487
self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/112", "add")
8588
self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128", "add")
8689
assert_equal(len(self.nodes[1].listbanned()), 3)
90+
assert_raises_rpc_error(-8, "Error: Invalid ip address",
91+
self.nodes[1].listbanned, "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64/128")
92+
relevant_bans = self.nodes[1].listbanned("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64")
8793
assert_raises_rpc_error(-8, "Error: setban removeall requires single ip address",
8894
self.nodes[1].setban, "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64/128", "removeall")
8995
self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c64", "removeall")

0 commit comments

Comments
 (0)