Skip to content
Permalink
Browse files

tmp

  • Loading branch information...
moneromooo-monero committed Jul 11, 2019
1 parent a6670bd commit 53c0e75fb7b65524abb3a2070fc775092ae9d084
Showing with 192 additions and 42 deletions.
  1. +17 −9 src/net/parse.cpp
  2. +13 −0 src/net/parse.h
  3. +5 −1 src/p2p/net_node.h
  4. +89 −19 src/p2p/net_node.inl
  5. +5 −0 src/p2p/net_node_common.h
  6. +28 −1 src/rpc/core_rpc_server.cpp
  7. +17 −12 tests/unit_tests/ban.cpp
  8. +18 −0 tests/unit_tests/net.cpp
@@ -53,24 +53,32 @@ namespace net
return make_error_code(net::error::invalid_port);
}

std::uint32_t ip = 0;
if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
return {epee::net_utils::ipv4_network_address{ip, port}};
return make_error_code(net::error::unsupported_address);
}

expect<epee::net_utils::ipv4_network_subnet>
get_ipv4_subnet_address(const boost::string_ref address, bool allow_implicit_32)
{
uint32_t mask = 32;
const boost::string_ref::size_type slash = address.find_first_of('/');
if (slash != boost::string_ref::npos)
{
std::uint32_t ip = 0;
if (epee::string_tools::get_ip_int32_from_string(ip, std::string{address.data(), slash}))
{
uint32_t mask;
if (!epee::string_tools::get_xtype_from_string(mask, std::string{address.substr(slash + 1)}))
return make_error_code(net::error::invalid_mask);
if (mask > 32)
return make_error_code(net::error::invalid_mask);
return {epee::net_utils::ipv4_network_subnet{ip, (uint8_t)mask}};
}
}
else if (!allow_implicit_32)
return make_error_code(net::error::invalid_mask);

std::uint32_t ip = 0;
if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
return {epee::net_utils::ipv4_network_address{ip, port}};
return make_error_code(net::error::unsupported_address);
boost::string_ref S(address.data(), slash != boost::string_ref::npos ? slash : address.size());
if (!epee::string_tools::get_ip_int32_from_string(ip, std::string(S)))
return make_error_code(net::error::invalid_host);

return {epee::net_utils::ipv4_network_subnet{ip, (uint8_t)mask}};
}
}
@@ -50,5 +50,18 @@ namespace net
*/
expect<epee::net_utils::network_address>
get_network_address(boost::string_ref address, std::uint16_t default_port);

/*!
Identifies an IPv4 subnet in CIDR notatioa and returns it as a generic
`network_address`. If the type is unsupported, it might be a hostname,
and `error() == net::error::kUnsupportedAddress` is returned.
\param address An ipv4 address.
\param allow_implicit_32 whether to accept "raw" IPv4 addresses, with CIDR notation
\return A tor or IPv4 address, else error.
*/
expect<epee::net_utils::ipv4_network_subnet>
get_ipv4_subnet_address(boost::string_ref address, bool allow_implicit_32 = false);
}

@@ -248,8 +248,11 @@ namespace nodetool
void change_max_in_public_peers(size_t count);
virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_host(const epee::net_utils::network_address &address);
virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet);
virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); }
virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; }

virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
virtual void remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
@@ -462,8 +465,9 @@ namespace nodetool
std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache;
epee::critical_section m_conn_fails_cache_lock;

epee::critical_section m_blocked_hosts_lock;
epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets
std::map<epee::net_utils::network_address, time_t> m_blocked_hosts;
std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets;

epee::critical_section m_host_fails_score_lock;
std::map<std::string, uint64_t> m_host_fails_score;
@@ -159,33 +159,51 @@ namespace nodetool
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);

// manually loop since we have to check for subnets
const time_t now = time(nullptr);
std::map<epee::net_utils::network_address, time_t>::iterator it;
for (it = m_blocked_hosts.begin(); it != m_blocked_hosts.end(); )

// look in the hosts list
auto it = m_blocked_hosts.find(address);
if (it != m_blocked_hosts.end())
{
if(now >= it->second)
if (now >= it->second)
{
it = m_blocked_hosts.erase(it);
m_blocked_hosts.erase(it);
MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
continue;
it = m_blocked_hosts.end();
}
if (it->first == address)
break;
bool is_subnet = false;
try { it->first.template as<epee::net_utils::ipv4_network_subnet>(); is_subnet = true; } catch(...){}
if (is_subnet && address.get_type_id() == epee::net_utils::address_type::ipv4)
}
if (it != m_blocked_hosts.end())
{
if (t)
*t = it->second - now;
return false;
}

// manually loop in subnets
if (address.get_type_id() == epee::net_utils::address_type::ipv4)
{
auto ipv4_address = address.template as<epee::net_utils::ipv4_network_address>();
std::map<epee::net_utils::ipv4_network_subnet, time_t>::iterator it;
for (it = m_blocked_subnets.begin(); it != m_blocked_subnets.end(); )
{
if (it->first.template as<epee::net_utils::ipv4_network_subnet>().matches(address.template as<epee::net_utils::ipv4_network_address>()))
break;
if (now >= it->second)
{
it = m_blocked_subnets.erase(it);
MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked.");
continue;
}
if (it->first.matches(ipv4_address))
{
if (t)
*t = it->second - now;
return false;
}
++it;
}
++it;
}
if (it == m_blocked_hosts.end())
return true;
if (t)
*t = it->second - now;
return false;

// not found in hosts or subnets, allowed
return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -241,6 +259,58 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds)
{
const time_t now = time(nullptr);

CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
time_t limit;
if (now > std::numeric_limits<time_t>::max() - seconds)
limit = std::numeric_limits<time_t>::max();
else
limit = now + seconds;
m_blocked_subnets[subnet] = limit;

// drop any connection to that subnet. This should only have to look into
// the zone related to the connection, but really make sure everything is
// swept ...
std::vector<boost::uuids::uuid> conns;
for(auto& zone : m_network_zones)
{
zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
{
if (cntxt.m_remote_address.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id())
return true;
auto ipv4_address = cntxt.m_remote_address.template as<epee::net_utils::ipv4_network_address>();
if (subnet.matches(ipv4_address))
{
conns.push_back(cntxt.m_connection_id);
}
return true;
});
for (const auto &c: conns)
zone.second.m_net_server.get_config_object().close(c);

conns.clear();
}

MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " blocked.");
return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet)
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
auto i = m_blocked_subnets.find(subnet);
if (i == m_blocked_subnets.end())
return false;
m_blocked_subnets.erase(i);
MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " unblocked.");
return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address)
{
if(!address.is_blockable())
@@ -57,6 +57,7 @@ namespace nodetool
virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0;
virtual bool unblock_host(const epee::net_utils::network_address &address)=0;
virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()=0;
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0;
virtual bool add_host_fail(const epee::net_utils::network_address &address)=0;
virtual void add_used_stripe_peer(const t_connection_context &context)=0;
virtual void remove_used_stripe_peer(const t_connection_context &context)=0;
@@ -116,6 +117,10 @@ namespace nodetool
{
return std::map<epee::net_utils::network_address, time_t>();
}
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()
{
return std::map<epee::net_utils::ipv4_network_subnet, time_t>();
}
virtual bool add_host_fail(const epee::net_utils::network_address &address)
{
return true;
@@ -1786,6 +1786,17 @@ namespace cryptonote
res.bans.push_back(b);
}
}
std::map<epee::net_utils::ipv4_network_subnet, time_t> blocked_subnets = m_p2p.get_blocked_subnets();
for (std::map<epee::net_utils::ipv4_network_subnet, time_t>::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i)
{
if (i->second > now) {
COMMAND_RPC_GETBANS::ban b;
b.host = i->first.host_str();
b.ip = 0;
b.seconds = i->second - now;
res.bans.push_back(b);
}
}

res.status = CORE_RPC_STATUS_OK;
return true;
@@ -1827,13 +1838,29 @@ namespace cryptonote
for (auto i = req.bans.begin(); i != req.bans.end(); ++i)
{
epee::net_utils::network_address na;

// try subnet first
if (!i->host.empty())
{
auto ns_parsed = net::get_ipv4_subnet_address(i->host);
if (ns_parsed)
{
if (i->ban)
m_p2p.block_subnet(*ns_parsed, i->seconds);
else
m_p2p.unblock_subnet(*ns_parsed);
continue;
}
}

// then host
if (!i->host.empty())
{
auto na_parsed = net::get_network_address(i->host, 0);
if (!na_parsed)
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
error_resp.message = "Unsupported host type";
error_resp.message = "Unsupported host/subnet type";
return false;
}
na = std::move(*na_parsed);
@@ -211,28 +211,33 @@ TEST(ban, limit)
TEST(ban, subnet)
{
time_t seconds;
ASSERT_TRUE(server.block_host(MAKE_IPV4_SUBNET(1,2,3,4,24), 10));
ASSERT_TRUE(server.get_blocked_hosts().size() == 1);
test_core pr_core;
cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL);
Server server(cprotocol);
cprotocol.set_p2p_endpoint(&server);

ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,24), 10));
ASSERT_TRUE(server.get_blocked_subnets().size() == 1);
ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,4), &seconds));
ASSERT_TRUE(seconds >= 9);
ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,255), &seconds));
ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,0), &seconds));
ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,4,0), &seconds));
ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,2,0), &seconds));
ASSERT_TRUE(server.unblock_host(MAKE_IPV4_SUBNET(1,2,3,8,24)));
ASSERT_TRUE(server.get_blocked_hosts().size() == 0);
ASSERT_TRUE(server.unblock_subnet(MAKE_IPV4_SUBNET(1,2,3,8,24)));
ASSERT_TRUE(server.get_blocked_subnets().size() == 0);
ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,255), &seconds));
ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,0), &seconds));
ASSERT_TRUE(server.block_host(MAKE_IPV4_SUBNET(1,2,3,4,8), 10));
ASSERT_TRUE(server.get_blocked_hosts().size() == 1);
ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,8), 10));
ASSERT_TRUE(server.get_blocked_subnets().size() == 1);
ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,255,3,255), &seconds));
ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,0,3,255), &seconds));
ASSERT_FALSE(server.unblock_host(MAKE_IPV4_SUBNET(1,2,3,8,24)));
ASSERT_TRUE(server.get_blocked_hosts().size() == 1);
ASSERT_TRUE(server.block_host(MAKE_IPV4_SUBNET(1,2,3,4,8), 10));
ASSERT_TRUE(server.get_blocked_hosts().size() == 1);
ASSERT_TRUE(server.unblock_host(MAKE_IPV4_SUBNET(1,255,0,0,8)));
ASSERT_TRUE(server.get_blocked_hosts().size() == 0);
ASSERT_FALSE(server.unblock_subnet(MAKE_IPV4_SUBNET(1,2,3,8,24)));
ASSERT_TRUE(server.get_blocked_subnets().size() == 1);
ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,8), 10));
ASSERT_TRUE(server.get_blocked_subnets().size() == 1);
ASSERT_TRUE(server.unblock_subnet(MAKE_IPV4_SUBNET(1,255,0,0,8)));
ASSERT_TRUE(server.get_blocked_subnets().size() == 0);
}

namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; }
@@ -524,6 +524,24 @@ TEST(get_network_address, ipv4)
EXPECT_STREQ("23.0.0.254:2000", address->str().c_str());
}

TEST(get_network_address, ipv4subnet)
{
expect<epee::net_utils::ipv4_network_subnet> address = net::get_ipv4_subnet_address("0.0.0.0", true);
EXPECT_STREQ("0.0.0.0/32", address->str().c_str());

address = net::get_ipv4_subnet_address("0.0.0.0");
EXPECT_TRUE(!address);

address = net::get_ipv4_subnet_address("0.0.0.0/32");
EXPECT_STREQ("0.0.0.0/32", address->str().c_str());

address = net::get_ipv4_subnet_address("0.0.0.0/0");
EXPECT_STREQ("0.0.0.0/0", address->str().c_str());

address = net::get_ipv4_subnet_address("12.34.56.78/16");
EXPECT_STREQ("12.34.0.0/16", address->str().c_str());
}

namespace
{
using stream_type = boost::asio::ip::tcp;

0 comments on commit 53c0e75

Please sign in to comment.
You can’t perform that action at this time.