diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 403f5a3..bc3c577 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -633,9 +633,7 @@ POP_WARNINGS boost::bind(&boosted_tcp_server::handle_accept, this, boost::asio::placeholders::error)); - bool r = conn->start(true, 1 < m_threads_count); - if (!r) - LOG_ERROR("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); + conn->start(true, 1 < m_threads_count); }else { LOG_ERROR("Some problems at accept: " << e.message() << ", connections_count = " << m_sockets_count); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index ec176ef..e7b1f7b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -106,6 +106,11 @@ #define P2P_STAT_TRUSTED_PUB_KEY "" #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +#define P2P_FAILED_ADDR_FORGET_SECONDS (60*60) //1 hour +#define P2P_IP_BLOCKTIME (60*60*24) //24 hour +#define P2P_IP_FAILS_BEFOR_BLOCK 10 +#define P2P_IDLE_CONNECTION_KILL_INTERVAL (5*60) //5 minutes + #define ALLOW_DEBUG_COMMANDS #define CRYPTONOTE_NAME "aeon" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2bd22db..80c227c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -332,6 +332,7 @@ namespace cryptonote LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } //to avoid concurrency in core between connections, suspend connections which delivered block later then first one @@ -407,12 +408,14 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } if(bvc.m_marked_as_orphaned) { LOG_PRINT_CCONTEXT_L0("Block received at sync phase was marked as orphaned, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } @@ -534,6 +537,7 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } @@ -542,6 +546,7 @@ namespace cryptonote LOG_ERROR_CCONTEXT("sent m_block_ids starting from unknown id: " << epee::string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); return 1; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index b882a9b..43fb2ca 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -41,6 +41,7 @@ #include #include #include +#include #include "warnings.h" #include "net/levin_server_cp2.h" @@ -55,6 +56,7 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) + namespace nodetool { template @@ -65,7 +67,8 @@ namespace nodetool template class node_server: public epee::levin::levin_commands_handler >, - public i_p2p_endpoint + public i_p2p_endpoint, + public epee::net_utils::i_connection_filter { struct by_conn_id{}; struct by_peer_id{}; @@ -94,8 +97,23 @@ namespace nodetool template void serialize(Archive &a, const t_version_type ver) { +#if 0 + { + time_t current_time = time(nullptr); + time_t archive_time = current_time; + a & archive_time; + if(archive_time > current_time) + { + LOG_PRINT_L0("p2p network state file has future time, skipped"); + return; + } + } +#endif a & m_peerlist; a & m_config.m_peer_id; +#if 0 + a & m_blocked_ips; +#endif } // debug functions bool log_peerlist(); @@ -147,6 +165,10 @@ namespace nodetool virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); virtual void for_each_connection(std::function f); + virtual bool block_ip(uint32_t adress); + virtual bool add_ip_fail(uint32_t address); + //----------------- i_connection_filter -------------------------------------------------------- + virtual bool is_remote_ip_allowed(uint32_t adress); //----------------------------------------------------------------------------------------------- bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line(const boost::program_options::variables_map& vm); @@ -171,6 +193,8 @@ namespace nodetool template bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool make_expected_connections_count(bool white_list, size_t expected_connections); + void cache_connect_fail_info(const net_address& addr); + bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); template @@ -229,6 +253,15 @@ namespace nodetool uint64_t m_peer_livetime; //keep connections to initiate some interactions net_server m_net_server; + + std::map m_conn_fails_cache; + epee::critical_section m_conn_fails_cache_lock; + + epee::critical_section m_blocked_ips_lock; + std::map m_blocked_ips; + + epee::critical_section m_ip_fails_score_lock; + std::map m_ip_fails_score; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 84585f1..5c76f6e 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -28,6 +28,8 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +// IP blocking adapted from Boolberry + #pragma once #include @@ -118,6 +120,22 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::is_remote_ip_allowed(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + auto it = m_blocked_ips.find(addr); + if(it == m_blocked_ips.end()) + return true; + if(time(nullptr) - it->second > P2P_IP_BLOCKTIME ) + { + m_blocked_ips.erase(it); + LOG_PRINT_CYAN("Ip " << epee::string_tools::get_ip_string_from_int32(addr) << "is unblocked.", LOG_LEVEL_0); + return true; + } + return false; + } + //----------------------------------------------------------------------------------- + template bool node_server::make_default_config() { m_config.m_peer_id = crypto::rand(); @@ -125,6 +143,31 @@ namespace nodetool } //----------------------------------------------------------------------------------- template + bool node_server::block_ip(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + m_blocked_ips[addr] = time(nullptr); + LOG_PRINT_CYAN("Ip " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked.", LOG_LEVEL_0); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::add_ip_fail(uint32_t address) + { + CRITICAL_REGION_LOCAL(m_ip_fails_score_lock); + uint64_t fails = ++m_ip_fails_score[address]; + LOG_PRINT_CYAN("Ip " << epee::string_tools::get_ip_string_from_int32(address) << " fail score=" << fails, LOG_LEVEL_1); + if(fails > P2P_IP_FAILS_BEFOR_BLOCK) + { + auto it = m_ip_fails_score.find(address); + CHECK_AND_ASSERT_MES(it != m_ip_fails_score.end(), false, "internal error"); + it->second = P2P_IP_FAILS_BEFOR_BLOCK/2; + block_ip(address); + } + return true; + } + //----------------------------------------------------------------------------------- + template bool node_server::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) { return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); @@ -245,6 +288,7 @@ namespace nodetool m_net_server.set_threads_prefix("P2P"); m_net_server.get_config_object().m_pcommands_handler = this; m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + m_net_server.set_connection_filter(this); //try to bind LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); @@ -403,6 +447,7 @@ namespace nodetool if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); + add_ip_fail(context.m_remote_ip); return; } hsh_result = true; @@ -464,6 +509,7 @@ namespace nodetool { LOG_ERROR_CCONTEXT("COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); m_net_server.get_config_object().close(context.m_connection_id ); + add_ip_fail(context.m_remote_ip); } if(!context.m_is_income) m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); @@ -598,6 +644,20 @@ namespace nodetool #undef LOG_PRINT_CC_PRIORITY_NODE + //----------------------------------------------------------------------------------- + template + bool node_server::is_addr_recently_failed(const net_address& addr) + { + CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); + auto it = m_conn_fails_cache.find(addr); + if(it == m_conn_fails_cache.end()) + return false; + + if(time(NULL) - it->second > P2P_FAILED_ADDR_FORGET_SECONDS) + return false; + else + return true; + } //----------------------------------------------------------------------------------- template bool node_server::make_new_connection_from_peerlist(bool use_white_list) @@ -631,6 +691,12 @@ namespace nodetool if(is_peer_used(pe)) continue; + if(!is_remote_ip_allowed(pe.adr.ip)) + continue; + + if(is_addr_recently_failed(pe.adr)) + continue; + LOG_PRINT_L2("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << "[white=" << use_white_list @@ -1029,6 +1095,7 @@ namespace nodetool LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); drop_connection(context); + add_ip_fail(context.m_remote_ip); return 1; } @@ -1036,6 +1103,7 @@ namespace nodetool { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); + add_ip_fail(context.m_remote_ip); return 1; } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 2ec5ff8..3f6bc34 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -50,6 +50,8 @@ namespace nodetool virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_connections_count()=0; virtual void for_each_connection(std::function f)=0; + virtual bool block_ip(uint32_t adress)=0; + virtual bool add_ip_fail(uint32_t adress)=0; }; template @@ -84,5 +86,13 @@ namespace nodetool { return false; } + virtual bool block_ip(uint32_t adress) + { + return true; + } + virtual bool add_ip_fail(uint32_t adress) + { + return true; + } }; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 3964b56..dfb7455 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -52,7 +52,7 @@ #include "cryptonote_config.h" #include "net_peerlist_boost_serialization.h" - +#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 4 namespace nodetool { @@ -394,4 +394,4 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- } -BOOST_CLASS_VERSION(nodetool::peerlist_manager, 4) +BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER)