aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schnelli <jonas.schnelli@include7.ch>2015-05-25 20:03:51 +0200
committerJonas Schnelli <jonas.schnelli@include7.ch>2015-06-17 21:40:55 +0200
commit433fb1a95d7a96a033d7454e198d274e92108865 (patch)
tree51e371e3e270d5b7485414549b4e27e19cde1a87
parente8b93473f12ec901f965cd244a7437646ee66c43 (diff)
[RPC] extend setban to allow subnets
-rwxr-xr-xqa/rpc-tests/httpbasics.py25
-rw-r--r--src/net.cpp50
-rw-r--r--src/net.h8
-rw-r--r--src/netbase.cpp5
-rw-r--r--src/netbase.h1
-rw-r--r--src/rpcnet.cpp44
-rw-r--r--src/test/rpc_tests.cpp40
7 files changed, 139 insertions, 34 deletions
diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py
index f35fe27dde..6d6ef9df76 100755
--- a/qa/rpc-tests/httpbasics.py
+++ b/qa/rpc-tests/httpbasics.py
@@ -101,13 +101,28 @@ class HTTPBasicsTest (BitcoinTestFramework):
###########################
# 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")
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].listbanned()), 1);
+ assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point
+ assert_equal(len(self.nodes[2].listbanned()), 1)
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__':
HTTPBasicsTest ().main ()
diff --git a/src/net.cpp b/src/net.cpp
index a065bb29bb..3ba2379ea0 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -332,6 +332,15 @@ CNode* FindNode(const CNetAddr& ip)
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)
{
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;
void CNode::ClearBanned()
@@ -447,7 +456,24 @@ bool CNode::IsBanned(CNetAddr ip)
bool fResult = false;
{
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())
{
int64_t t = (*i).second;
@@ -458,24 +484,34 @@ bool CNode::IsBanned(CNetAddr ip)
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
if (bantimeoffset > 0)
banTime = GetTime()+bantimeoffset;
LOCK(cs_setBanned);
- if (setBanned[addr] < banTime)
- setBanned[addr] = banTime;
+ if (setBanned[subNet] < banTime)
+ setBanned[subNet] = banTime;
}
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);
- if (setBanned.erase(addr))
+ if (setBanned.erase(subNet))
return true;
return false;
}
-void CNode::GetBanned(std::map<CNetAddr, int64_t> &banMap)
+void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
{
LOCK(cs_setBanned);
banMap = setBanned; //create a thread safe copy
diff --git a/src/net.h b/src/net.h
index ee3da16aa6..d800aa22c6 100644
--- a/src/net.h
+++ b/src/net.h
@@ -66,6 +66,7 @@ unsigned int SendBufferSize();
void AddOneShot(const std::string& strDest);
void AddressCurrentlyConnected(const CService& addr);
CNode* FindNode(const CNetAddr& ip);
+CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip);
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
@@ -284,7 +285,7 @@ protected:
// Denial-of-service detection/prevention
// 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;
// Whitelisted ranges. Any node connecting from these is automatically
@@ -606,9 +607,12 @@ public:
// new code.
static void ClearBanned(); // needed for unit testing
static bool IsBanned(CNetAddr ip);
+ static bool IsBanned(CSubNet subnet);
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 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);
diff --git a/src/netbase.cpp b/src/netbase.cpp
index e3cb4e706f..b7c77fda6b 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -1330,6 +1330,11 @@ bool operator!=(const CSubNet& a, const CSubNet& 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
std::string NetworkErrorString(int err)
{
diff --git a/src/netbase.h b/src/netbase.h
index 1f2957116e..27f0eac2a2 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -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);
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index 6157a2d0a0..e6c33e1d06 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -474,39 +474,51 @@ Value setban(const Array& params, bool fHelp)
if (fHelp || params.size() < 2 ||
(strCommand != "add" && strCommand != "remove"))
throw runtime_error(
- "setban \"node\" \"add|remove\" (bantime)\n"
- "\nAttempts add or remove a IP from the banned list.\n"
+ "setban \"ip(/netmask)\" \"add|remove\" (bantime)\n"
+ "\nAttempts add or remove a IP/Subnet from the banned list.\n"
"\nArguments:\n"
- "1. \"ip\" (string, required) The IP (see getpeerinfo for nodes ip)\n"
- "2. \"command\" (string, required) 'add' to add a IP to the list, 'remove' to remove a IP 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. \"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/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"
"\nExamples:\n"
+ HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
+ + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")
);
- CNetAddr netAddr(params[0].get_str());
- if (!netAddr.IsValid())
- throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP Address");
+ CSubNet subNet;
+ CNetAddr netAddr;
+ 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 (CNode::IsBanned(netAddr))
- throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP already banned");
+ if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr))
+ throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
int64_t banTime = 0; //use standard bantime if not specified
if (params.size() == 3 && !params[2].is_null())
banTime = params[2].get_int64();
- CNode::Ban(netAddr, banTime);
+ isSubnet ? CNode::Ban(subNet, banTime) : CNode::Ban(netAddr, banTime);
//disconnect possible nodes
- while(CNode *bannedNode = FindNode(netAddr))
+ while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr)))
bannedNode->CloseSocketDisconnect();
}
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");
}
@@ -518,17 +530,17 @@ Value listbanned(const Array& params, bool fHelp)
if (fHelp || params.size() != 0)
throw runtime_error(
"listbanned\n"
- "\nList all banned IPs.\n"
+ "\nList all banned IPs/Subnets.\n"
"\nExamples:\n"
+ HelpExampleCli("listbanned", "")
+ HelpExampleRpc("listbanned", "")
);
- std::map<CNetAddr, int64_t> banMap;
+ std::map<CSubNet, int64_t> banMap;
CNode::GetBanned(banMap);
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;
rec.push_back(Pair("address", (*it).first.ToString()));
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 3cec4b76dd..26588a43ed 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -179,11 +179,43 @@ BOOST_AUTO_TEST_CASE(rpc_boostasiotocnetaddr)
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")));
+
+ 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()