[RPC] extend setban to allow subnets
This commit is contained in:
parent
e8b93473f1
commit
433fb1a95d
7 changed files with 139 additions and 34 deletions
|
@ -101,13 +101,28 @@ class HTTPBasicsTest (BitcoinTestFramework):
|
||||||
###########################
|
###########################
|
||||||
# setban/listbanned tests #
|
# setban/listbanned tests #
|
||||||
###########################
|
###########################
|
||||||
assert_equal(len(self.nodes[2].getpeerinfo()), 4); #we should have 4 nodes at this point
|
assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point
|
||||||
self.nodes[2].setban("127.0.0.1", "add")
|
self.nodes[2].setban("127.0.0.1", "add")
|
||||||
time.sleep(3) #wait till the nodes are disconected
|
time.sleep(3) #wait till the nodes are disconected
|
||||||
assert_equal(len(self.nodes[2].getpeerinfo()), 0); #all nodes must be disconnected at this point
|
assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point
|
||||||
assert_equal(len(self.nodes[2].listbanned()), 1);
|
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||||
self.nodes[2].clearbanned()
|
self.nodes[2].clearbanned()
|
||||||
assert_equal(len(self.nodes[2].listbanned()), 0);
|
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||||
|
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||||
|
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||||
|
try:
|
||||||
|
self.nodes[2].setban("127.0.0.1", "add") #throws exception because 127.0.0.1 is within range 127.0.0.0/24
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
|
||||||
|
try:
|
||||||
|
self.nodes[2].setban("127.0.0.1", "remove")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||||
|
self.nodes[2].setban("127.0.0.0/24", "remove")
|
||||||
|
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||||
|
self.nodes[2].clearbanned()
|
||||||
|
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
HTTPBasicsTest ().main ()
|
HTTPBasicsTest ().main ()
|
||||||
|
|
50
src/net.cpp
50
src/net.cpp
|
@ -332,6 +332,15 @@ CNode* FindNode(const CNetAddr& ip)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CNode* FindNode(const CSubNet& subNet)
|
||||||
|
{
|
||||||
|
LOCK(cs_vNodes);
|
||||||
|
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||||
|
if (subNet.Match((CNetAddr)pnode->addr))
|
||||||
|
return (pnode);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
CNode* FindNode(const std::string& addrName)
|
CNode* FindNode(const std::string& addrName)
|
||||||
{
|
{
|
||||||
LOCK(cs_vNodes);
|
LOCK(cs_vNodes);
|
||||||
|
@ -434,7 +443,7 @@ void CNode::PushVersion()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::map<CNetAddr, int64_t> CNode::setBanned;
|
std::map<CSubNet, int64_t> CNode::setBanned;
|
||||||
CCriticalSection CNode::cs_setBanned;
|
CCriticalSection CNode::cs_setBanned;
|
||||||
|
|
||||||
void CNode::ClearBanned()
|
void CNode::ClearBanned()
|
||||||
|
@ -447,7 +456,24 @@ bool CNode::IsBanned(CNetAddr ip)
|
||||||
bool fResult = false;
|
bool fResult = false;
|
||||||
{
|
{
|
||||||
LOCK(cs_setBanned);
|
LOCK(cs_setBanned);
|
||||||
std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip);
|
for (std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); it != setBanned.end(); it++)
|
||||||
|
{
|
||||||
|
CSubNet subNet = (*it).first;
|
||||||
|
int64_t t = (*it).second;
|
||||||
|
|
||||||
|
if(subNet.Match(ip) && GetTime() < t)
|
||||||
|
fResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNode::IsBanned(CSubNet subnet)
|
||||||
|
{
|
||||||
|
bool fResult = false;
|
||||||
|
{
|
||||||
|
LOCK(cs_setBanned);
|
||||||
|
std::map<CSubNet, int64_t>::iterator i = setBanned.find(subnet);
|
||||||
if (i != setBanned.end())
|
if (i != setBanned.end())
|
||||||
{
|
{
|
||||||
int64_t t = (*i).second;
|
int64_t t = (*i).second;
|
||||||
|
@ -458,24 +484,34 @@ bool CNode::IsBanned(CNetAddr ip)
|
||||||
return fResult;
|
return fResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNode::Ban(const CNetAddr &addr, int64_t bantimeoffset) {
|
void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset) {
|
||||||
|
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
|
||||||
|
Ban(subNet, bantimeoffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset) {
|
||||||
int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
|
int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
|
||||||
if (bantimeoffset > 0)
|
if (bantimeoffset > 0)
|
||||||
banTime = GetTime()+bantimeoffset;
|
banTime = GetTime()+bantimeoffset;
|
||||||
|
|
||||||
LOCK(cs_setBanned);
|
LOCK(cs_setBanned);
|
||||||
if (setBanned[addr] < banTime)
|
if (setBanned[subNet] < banTime)
|
||||||
setBanned[addr] = banTime;
|
setBanned[subNet] = banTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNode::Unban(const CNetAddr &addr) {
|
bool CNode::Unban(const CNetAddr &addr) {
|
||||||
|
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
|
||||||
|
return Unban(subNet);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNode::Unban(const CSubNet &subNet) {
|
||||||
LOCK(cs_setBanned);
|
LOCK(cs_setBanned);
|
||||||
if (setBanned.erase(addr))
|
if (setBanned.erase(subNet))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNode::GetBanned(std::map<CNetAddr, int64_t> &banMap)
|
void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
|
||||||
{
|
{
|
||||||
LOCK(cs_setBanned);
|
LOCK(cs_setBanned);
|
||||||
banMap = setBanned; //create a thread safe copy
|
banMap = setBanned; //create a thread safe copy
|
||||||
|
|
|
@ -66,6 +66,7 @@ unsigned int SendBufferSize();
|
||||||
void AddOneShot(const std::string& strDest);
|
void AddOneShot(const std::string& strDest);
|
||||||
void AddressCurrentlyConnected(const CService& addr);
|
void AddressCurrentlyConnected(const CService& addr);
|
||||||
CNode* FindNode(const CNetAddr& ip);
|
CNode* FindNode(const CNetAddr& ip);
|
||||||
|
CNode* FindNode(const CSubNet& subNet);
|
||||||
CNode* FindNode(const std::string& addrName);
|
CNode* FindNode(const std::string& addrName);
|
||||||
CNode* FindNode(const CService& ip);
|
CNode* FindNode(const CService& ip);
|
||||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
|
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
|
||||||
|
@ -284,7 +285,7 @@ protected:
|
||||||
|
|
||||||
// Denial-of-service detection/prevention
|
// Denial-of-service detection/prevention
|
||||||
// Key is IP address, value is banned-until-time
|
// Key is IP address, value is banned-until-time
|
||||||
static std::map<CNetAddr, int64_t> setBanned;
|
static std::map<CSubNet, int64_t> setBanned;
|
||||||
static CCriticalSection cs_setBanned;
|
static CCriticalSection cs_setBanned;
|
||||||
|
|
||||||
// Whitelisted ranges. Any node connecting from these is automatically
|
// Whitelisted ranges. Any node connecting from these is automatically
|
||||||
|
@ -606,9 +607,12 @@ public:
|
||||||
// new code.
|
// new code.
|
||||||
static void ClearBanned(); // needed for unit testing
|
static void ClearBanned(); // needed for unit testing
|
||||||
static bool IsBanned(CNetAddr ip);
|
static bool IsBanned(CNetAddr ip);
|
||||||
|
static bool IsBanned(CSubNet subnet);
|
||||||
static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0);
|
static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0);
|
||||||
|
static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0);
|
||||||
static bool Unban(const CNetAddr &ip);
|
static bool Unban(const CNetAddr &ip);
|
||||||
static void GetBanned(std::map<CNetAddr, int64_t> &banmap);
|
static bool Unban(const CSubNet &ip);
|
||||||
|
static void GetBanned(std::map<CSubNet, int64_t> &banmap);
|
||||||
|
|
||||||
void copyStats(CNodeStats &stats);
|
void copyStats(CNodeStats &stats);
|
||||||
|
|
||||||
|
|
|
@ -1330,6 +1330,11 @@ bool operator!=(const CSubNet& a, const CSubNet& b)
|
||||||
return !(a==b);
|
return !(a==b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator<(const CSubNet& a, const CSubNet& b)
|
||||||
|
{
|
||||||
|
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16)));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
std::string NetworkErrorString(int err)
|
std::string NetworkErrorString(int err)
|
||||||
{
|
{
|
||||||
|
|
|
@ -125,6 +125,7 @@ class CSubNet
|
||||||
|
|
||||||
friend bool operator==(const CSubNet& a, const CSubNet& b);
|
friend bool operator==(const CSubNet& a, const CSubNet& b);
|
||||||
friend bool operator!=(const CSubNet& a, const CSubNet& b);
|
friend bool operator!=(const CSubNet& a, const CSubNet& b);
|
||||||
|
friend bool operator<(const CSubNet& a, const CSubNet& b);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A combination of a network address (CNetAddr) and a (TCP) port */
|
/** A combination of a network address (CNetAddr) and a (TCP) port */
|
||||||
|
|
|
@ -474,39 +474,51 @@ Value setban(const Array& params, bool fHelp)
|
||||||
if (fHelp || params.size() < 2 ||
|
if (fHelp || params.size() < 2 ||
|
||||||
(strCommand != "add" && strCommand != "remove"))
|
(strCommand != "add" && strCommand != "remove"))
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"setban \"node\" \"add|remove\" (bantime)\n"
|
"setban \"ip(/netmask)\" \"add|remove\" (bantime)\n"
|
||||||
"\nAttempts add or remove a IP from the banned list.\n"
|
"\nAttempts add or remove a IP/Subnet from the banned list.\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"ip\" (string, required) The IP (see getpeerinfo for nodes ip)\n"
|
"1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n"
|
||||||
"2. \"command\" (string, required) 'add' to add a IP to the list, 'remove' to remove a IP from the list\n"
|
"2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n"
|
||||||
"1. \"bantime\" (numeric, optional) time in seconds how long the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n"
|
"1. \"bantime\" (numeric, optional) time in seconds how long the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
|
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
|
||||||
|
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
|
||||||
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")
|
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")
|
||||||
);
|
);
|
||||||
|
|
||||||
CNetAddr netAddr(params[0].get_str());
|
CSubNet subNet;
|
||||||
if (!netAddr.IsValid())
|
CNetAddr netAddr;
|
||||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP Address");
|
bool isSubnet = false;
|
||||||
|
|
||||||
|
if (params[0].get_str().find("/") != string::npos)
|
||||||
|
isSubnet = true;
|
||||||
|
|
||||||
|
if (!isSubnet)
|
||||||
|
netAddr = CNetAddr(params[0].get_str());
|
||||||
|
else
|
||||||
|
subNet = CSubNet(params[0].get_str());
|
||||||
|
|
||||||
|
if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
|
||||||
|
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet");
|
||||||
|
|
||||||
if (strCommand == "add")
|
if (strCommand == "add")
|
||||||
{
|
{
|
||||||
if (CNode::IsBanned(netAddr))
|
if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr))
|
||||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP already banned");
|
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
|
||||||
|
|
||||||
int64_t banTime = 0; //use standard bantime if not specified
|
int64_t banTime = 0; //use standard bantime if not specified
|
||||||
if (params.size() == 3 && !params[2].is_null())
|
if (params.size() == 3 && !params[2].is_null())
|
||||||
banTime = params[2].get_int64();
|
banTime = params[2].get_int64();
|
||||||
|
|
||||||
CNode::Ban(netAddr, banTime);
|
isSubnet ? CNode::Ban(subNet, banTime) : CNode::Ban(netAddr, banTime);
|
||||||
|
|
||||||
//disconnect possible nodes
|
//disconnect possible nodes
|
||||||
while(CNode *bannedNode = FindNode(netAddr))
|
while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
|
||||||
bannedNode->CloseSocketDisconnect();
|
bannedNode->CloseSocketDisconnect();
|
||||||
}
|
}
|
||||||
else if(strCommand == "remove")
|
else if(strCommand == "remove")
|
||||||
{
|
{
|
||||||
if (!CNode::Unban(netAddr))
|
if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) ))
|
||||||
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Unban failed");
|
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Unban failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,17 +530,17 @@ Value listbanned(const Array& params, bool fHelp)
|
||||||
if (fHelp || params.size() != 0)
|
if (fHelp || params.size() != 0)
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"listbanned\n"
|
"listbanned\n"
|
||||||
"\nList all banned IPs.\n"
|
"\nList all banned IPs/Subnets.\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("listbanned", "")
|
+ HelpExampleCli("listbanned", "")
|
||||||
+ HelpExampleRpc("listbanned", "")
|
+ HelpExampleRpc("listbanned", "")
|
||||||
);
|
);
|
||||||
|
|
||||||
std::map<CNetAddr, int64_t> banMap;
|
std::map<CSubNet, int64_t> banMap;
|
||||||
CNode::GetBanned(banMap);
|
CNode::GetBanned(banMap);
|
||||||
|
|
||||||
Array bannedAddresses;
|
Array bannedAddresses;
|
||||||
for (std::map<CNetAddr, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++)
|
for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++)
|
||||||
{
|
{
|
||||||
Object rec;
|
Object rec;
|
||||||
rec.push_back(Pair("address", (*it).first.ToString()));
|
rec.push_back(Pair("address", (*it).first.ToString()));
|
||||||
|
|
|
@ -179,11 +179,43 @@ BOOST_AUTO_TEST_CASE(rpc_boostasiotocnetaddr)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(rpc_ban)
|
BOOST_AUTO_TEST_CASE(rpc_ban)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.1 add")));
|
|
||||||
BOOST_CHECK_THROW(CallRPC(string("setban 127.0.0.1:8334")), runtime_error); //portnumber for setban not allowed
|
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("listbanned")));
|
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.1 remove")));
|
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||||
|
|
||||||
|
Value r;
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0 add")));
|
||||||
|
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.0:8334")), runtime_error); //portnumber for setban not allowed
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
|
Array ar = r.get_array();
|
||||||
|
Object o1 = ar[0].get_obj();
|
||||||
|
Value adr = find_value(o1, "address");
|
||||||
|
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.255");
|
||||||
|
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));;
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
|
ar = r.get_array();
|
||||||
|
BOOST_CHECK_EQUAL(ar.size(), 0);
|
||||||
|
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/24 add")));
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
|
ar = r.get_array();
|
||||||
|
o1 = ar[0].get_obj();
|
||||||
|
adr = find_value(o1, "address");
|
||||||
|
BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0");
|
||||||
|
|
||||||
|
// must throw an exception because 127.0.0.1 is in already banned suubnet range
|
||||||
|
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.1 add")), runtime_error);
|
||||||
|
|
||||||
|
BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0/24 remove")));;
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
|
ar = r.get_array();
|
||||||
|
BOOST_CHECK_EQUAL(ar.size(), 0);
|
||||||
|
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/255.255.0.0 add")));
|
||||||
|
BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.1.1 add")), runtime_error);
|
||||||
|
|
||||||
|
BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned")));
|
||||||
|
BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned")));
|
||||||
|
ar = r.get_array();
|
||||||
|
BOOST_CHECK_EQUAL(ar.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Reference in a new issue