allow blocking whole subnets
This commit is contained in:
parent
515ac2951d
commit
65c4004963
@ -70,7 +70,7 @@ namespace net_utils
|
||||
|
||||
struct i_connection_filter
|
||||
{
|
||||
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address)=0;
|
||||
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL)=0;
|
||||
protected:
|
||||
virtual ~i_connection_filter(){}
|
||||
};
|
||||
|
@ -41,7 +41,7 @@
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "net"
|
||||
|
||||
#ifndef MAKE_IP
|
||||
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
|
||||
#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24))
|
||||
#endif
|
||||
|
||||
#if BOOST_VERSION >= 107000
|
||||
@ -107,6 +107,53 @@ namespace net_utils
|
||||
inline bool operator>=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept
|
||||
{ return !lhs.less(rhs); }
|
||||
|
||||
class ipv4_network_subnet
|
||||
{
|
||||
uint32_t m_ip;
|
||||
uint8_t m_mask;
|
||||
|
||||
public:
|
||||
constexpr ipv4_network_subnet() noexcept
|
||||
: ipv4_network_subnet(0, 0)
|
||||
{}
|
||||
|
||||
constexpr ipv4_network_subnet(uint32_t ip, uint8_t mask) noexcept
|
||||
: m_ip(ip), m_mask(mask) {}
|
||||
|
||||
bool equal(const ipv4_network_subnet& other) const noexcept;
|
||||
bool less(const ipv4_network_subnet& other) const noexcept;
|
||||
constexpr bool is_same_host(const ipv4_network_subnet& other) const noexcept
|
||||
{ return subnet() == other.subnet(); }
|
||||
bool matches(const ipv4_network_address &address) const;
|
||||
|
||||
constexpr uint32_t subnet() const noexcept { return m_ip & ~(0xffffffffull << m_mask); }
|
||||
std::string str() const;
|
||||
std::string host_str() const;
|
||||
bool is_loopback() const;
|
||||
bool is_local() const;
|
||||
static constexpr address_type get_type_id() noexcept { return address_type::invalid; }
|
||||
static constexpr zone get_zone() noexcept { return zone::public_; }
|
||||
static constexpr bool is_blockable() noexcept { return true; }
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(m_ip)
|
||||
KV_SERIALIZE(m_mask)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
inline bool operator==(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
|
||||
{ return lhs.equal(rhs); }
|
||||
inline bool operator!=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
|
||||
{ return !lhs.equal(rhs); }
|
||||
inline bool operator<(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
|
||||
{ return lhs.less(rhs); }
|
||||
inline bool operator<=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
|
||||
{ return !rhs.less(lhs); }
|
||||
inline bool operator>(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
|
||||
{ return rhs.less(lhs); }
|
||||
inline bool operator>=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept
|
||||
{ return !lhs.less(rhs); }
|
||||
|
||||
class network_address
|
||||
{
|
||||
struct interface
|
||||
|
@ -22,6 +22,24 @@ namespace epee { namespace net_utils
|
||||
bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); }
|
||||
|
||||
|
||||
bool ipv4_network_subnet::equal(const ipv4_network_subnet& other) const noexcept
|
||||
{ return is_same_host(other) && m_mask == other.m_mask; }
|
||||
|
||||
bool ipv4_network_subnet::less(const ipv4_network_subnet& other) const noexcept
|
||||
{ return subnet() < other.subnet() ? true : (other.subnet() < subnet() ? false : (m_mask < other.m_mask)); }
|
||||
|
||||
std::string ipv4_network_subnet::str() const
|
||||
{ return string_tools::get_ip_string_from_int32(subnet()) + "/" + std::to_string(m_mask); }
|
||||
|
||||
std::string ipv4_network_subnet::host_str() const { return string_tools::get_ip_string_from_int32(subnet()) + "/" + std::to_string(m_mask); }
|
||||
bool ipv4_network_subnet::is_loopback() const { return net_utils::is_ip_loopback(subnet()); }
|
||||
bool ipv4_network_subnet::is_local() const { return net_utils::is_ip_local(subnet()); }
|
||||
bool ipv4_network_subnet::matches(const ipv4_network_address &address) const
|
||||
{
|
||||
return (address.ip() & ~(0xffffffffull << m_mask)) == subnet();
|
||||
}
|
||||
|
||||
|
||||
bool network_address::equal(const network_address& other) const
|
||||
{
|
||||
// clang typeid workaround
|
||||
|
@ -596,6 +596,13 @@ bool t_command_parser_executor::unban(const std::vector<std::string>& args)
|
||||
return m_executor.unban(ip);
|
||||
}
|
||||
|
||||
bool t_command_parser_executor::banned(const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.size() != 1) return false;
|
||||
std::string address = args[0];
|
||||
return m_executor.banned(address);
|
||||
}
|
||||
|
||||
bool t_command_parser_executor::flush_txpool(const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.size() > 1) return false;
|
||||
|
@ -127,6 +127,8 @@ public:
|
||||
|
||||
bool unban(const std::vector<std::string>& args);
|
||||
|
||||
bool banned(const std::vector<std::string>& args);
|
||||
|
||||
bool flush_txpool(const std::vector<std::string>& args);
|
||||
|
||||
bool output_histogram(const std::vector<std::string>& args);
|
||||
|
@ -243,9 +243,15 @@ t_command_server::t_command_server(
|
||||
m_command_lookup.set_handler(
|
||||
"unban"
|
||||
, std::bind(&t_command_parser_executor::unban, &m_parser, p::_1)
|
||||
, "unban <IP>"
|
||||
, "unban <address>"
|
||||
, "Unban a given <IP>."
|
||||
);
|
||||
m_command_lookup.set_handler(
|
||||
"banned"
|
||||
, std::bind(&t_command_parser_executor::banned, &m_parser, p::_1)
|
||||
, "banned <address>"
|
||||
, "Check whether an <address> is banned."
|
||||
);
|
||||
m_command_lookup.set_handler(
|
||||
"flush_txpool"
|
||||
, std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1)
|
||||
|
@ -1641,14 +1641,14 @@ bool t_rpc_command_executor::print_bans()
|
||||
|
||||
for (auto i = res.bans.begin(); i != res.bans.end(); ++i)
|
||||
{
|
||||
tools::msg_writer() << epee::string_tools::get_ip_string_from_int32(i->ip) << " banned for " << i->seconds << " seconds";
|
||||
tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
|
||||
bool t_rpc_command_executor::ban(const std::string &address, time_t seconds)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_SETBANS::request req;
|
||||
cryptonote::COMMAND_RPC_SETBANS::response res;
|
||||
@ -1656,11 +1656,8 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
|
||||
epee::json_rpc::error error_resp;
|
||||
|
||||
cryptonote::COMMAND_RPC_SETBANS::ban ban;
|
||||
if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip))
|
||||
{
|
||||
tools::fail_msg_writer() << "Invalid IP";
|
||||
return true;
|
||||
}
|
||||
ban.host = address;
|
||||
ban.ip = 0;
|
||||
ban.ban = true;
|
||||
ban.seconds = seconds;
|
||||
req.bans.push_back(ban);
|
||||
@ -1684,7 +1681,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::unban(const std::string &ip)
|
||||
bool t_rpc_command_executor::unban(const std::string &address)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_SETBANS::request req;
|
||||
cryptonote::COMMAND_RPC_SETBANS::response res;
|
||||
@ -1692,11 +1689,8 @@ bool t_rpc_command_executor::unban(const std::string &ip)
|
||||
epee::json_rpc::error error_resp;
|
||||
|
||||
cryptonote::COMMAND_RPC_SETBANS::ban ban;
|
||||
if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip))
|
||||
{
|
||||
tools::fail_msg_writer() << "Invalid IP";
|
||||
return true;
|
||||
}
|
||||
ban.host = address;
|
||||
ban.ip = 0;
|
||||
ban.ban = false;
|
||||
ban.seconds = 0;
|
||||
req.bans.push_back(ban);
|
||||
@ -1720,6 +1714,39 @@ bool t_rpc_command_executor::unban(const std::string &ip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::banned(const std::string &address)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_BANNED::request req;
|
||||
cryptonote::COMMAND_RPC_BANNED::response res;
|
||||
std::string fail_message = "Unsuccessful";
|
||||
epee::json_rpc::error error_resp;
|
||||
|
||||
req.address = address;
|
||||
|
||||
if (m_is_rpc)
|
||||
{
|
||||
if (!m_rpc_client->json_rpc_request(req, res, "banned", fail_message.c_str()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_rpc_server->on_banned(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
|
||||
{
|
||||
tools::fail_msg_writer() << make_error(fail_message, res.status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (res.banned)
|
||||
tools::msg_writer() << address << " is banned for " << res.seconds << " seconds";
|
||||
else
|
||||
tools::msg_writer() << address << " is not banned";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool t_rpc_command_executor::flush_txpool(const std::string &txid)
|
||||
{
|
||||
cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::request req;
|
||||
|
@ -137,9 +137,11 @@ public:
|
||||
|
||||
bool print_bans();
|
||||
|
||||
bool ban(const std::string &ip, time_t seconds);
|
||||
bool ban(const std::string &address, time_t seconds);
|
||||
|
||||
bool unban(const std::string &ip);
|
||||
bool unban(const std::string &address);
|
||||
|
||||
bool banned(const std::string &address);
|
||||
|
||||
bool flush_txpool(const std::string &txid);
|
||||
|
||||
|
@ -42,7 +42,8 @@ namespace net
|
||||
invalid_i2p_address,
|
||||
invalid_port, //!< Outside of 0-65535 range
|
||||
invalid_tor_address,//!< Invalid base32 or length
|
||||
unsupported_address //!< Type not supported by `get_network_address`
|
||||
unsupported_address,//!< Type not supported by `get_network_address`
|
||||
invalid_mask, //!< Outside of 0-32 range
|
||||
};
|
||||
|
||||
//! \return `std::error_category` for `net` namespace.
|
||||
|
@ -58,4 +58,27 @@ namespace net
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if (!allow_implicit_32)
|
||||
return make_error_code(net::error::invalid_mask);
|
||||
|
||||
std::uint32_t ip = 0;
|
||||
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,7 +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);
|
||||
@ -319,7 +323,7 @@ namespace nodetool
|
||||
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f);
|
||||
virtual bool add_host_fail(const epee::net_utils::network_address &address);
|
||||
//----------------- i_connection_filter --------------------------------------------------------
|
||||
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address);
|
||||
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL);
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
bool parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port = 0);
|
||||
bool handle_command_line(
|
||||
@ -461,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;
|
||||
|
@ -155,19 +155,55 @@ namespace nodetool
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_payload_net_handler>
|
||||
bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address)
|
||||
bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
|
||||
|
||||
const time_t now = time(nullptr);
|
||||
|
||||
// look in the hosts list
|
||||
auto it = m_blocked_hosts.find(address);
|
||||
if(it == m_blocked_hosts.end())
|
||||
return true;
|
||||
if(time(nullptr) >= it->second)
|
||||
if (it != m_blocked_hosts.end())
|
||||
{
|
||||
m_blocked_hosts.erase(it);
|
||||
MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
|
||||
return true;
|
||||
if (now >= it->second)
|
||||
{
|
||||
m_blocked_hosts.erase(it);
|
||||
MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
|
||||
it = m_blocked_hosts.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t)
|
||||
*t = it->second - now;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
// not found in hosts or subnets, allowed
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_payload_net_handler>
|
||||
@ -223,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,46 @@ 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
bool core_rpc_server::on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
|
||||
{
|
||||
PERF_TIMER(on_banned);
|
||||
|
||||
auto na_parsed = net::get_network_address(req.address, 0);
|
||||
if (!na_parsed)
|
||||
{
|
||||
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
|
||||
error_resp.message = "Unsupported host type";
|
||||
return false;
|
||||
}
|
||||
epee::net_utils::network_address na = std::move(*na_parsed);
|
||||
|
||||
time_t seconds;
|
||||
if (m_p2p.is_host_blocked(na, &seconds))
|
||||
{
|
||||
res.banned = true;
|
||||
res.seconds = seconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
res.banned = false;
|
||||
res.seconds = 0;
|
||||
}
|
||||
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
@ -1798,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);
|
||||
|
@ -154,6 +154,7 @@ namespace cryptonote
|
||||
MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO)
|
||||
MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted)
|
||||
MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted)
|
||||
MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted)
|
||||
MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted)
|
||||
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
|
||||
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
|
||||
@ -220,6 +221,7 @@ namespace cryptonote
|
||||
bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
|
||||
|
@ -84,7 +84,7 @@ namespace cryptonote
|
||||
// advance which version they will stop working with
|
||||
// Don't go over 32767 for any of these
|
||||
#define CORE_RPC_VERSION_MAJOR 2
|
||||
#define CORE_RPC_VERSION_MINOR 6
|
||||
#define CORE_RPC_VERSION_MINOR 7
|
||||
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
||||
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
|
||||
|
||||
@ -1876,6 +1876,33 @@ namespace cryptonote
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_BANNED
|
||||
{
|
||||
struct request_t
|
||||
{
|
||||
std::string address;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(address)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<request_t> request;
|
||||
|
||||
struct response_t
|
||||
{
|
||||
std::string status;
|
||||
bool banned;
|
||||
uint32_t seconds;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(status)
|
||||
KV_SERIALIZE(banned)
|
||||
KV_SERIALIZE(seconds)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
typedef epee::misc_utils::struct_init<response_t> response;
|
||||
};
|
||||
|
||||
struct COMMAND_RPC_FLUSH_TRANSACTION_POOL
|
||||
{
|
||||
struct request_t
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "cryptonote_protocol/cryptonote_protocol_handler.inl"
|
||||
|
||||
#define MAKE_IPV4_ADDRESS(a,b,c,d) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),0}
|
||||
#define MAKE_IPV4_SUBNET(a,b,c,d,e) epee::net_utils::ipv4_network_subnet{MAKE_IP(a,b,c,d),e}
|
||||
|
||||
namespace cryptonote {
|
||||
class blockchain_storage;
|
||||
@ -93,11 +94,10 @@ typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_cor
|
||||
|
||||
static bool is_blocked(Server &server, const epee::net_utils::network_address &address, time_t *t = NULL)
|
||||
{
|
||||
const std::string host = address.host_str();
|
||||
std::map<std::string, time_t> hosts = server.get_blocked_hosts();
|
||||
std::map<epee::net_utils::network_address, time_t> hosts = server.get_blocked_hosts();
|
||||
for (auto rec: hosts)
|
||||
{
|
||||
if (rec.first == host)
|
||||
if (rec.first == address)
|
||||
{
|
||||
if (t)
|
||||
*t = rec.second;
|
||||
@ -208,5 +208,37 @@ TEST(ban, limit)
|
||||
ASSERT_TRUE(is_blocked(server,MAKE_IPV4_ADDRESS(1,2,3,4)));
|
||||
}
|
||||
|
||||
TEST(ban, subnet)
|
||||
{
|
||||
time_t seconds;
|
||||
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_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_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_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>>; }
|
||||
namespace cryptonote { template class 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;
|
||||
|
Loading…
Reference in New Issue
Block a user