diff --git a/contrib/epee/include/net/abstract_tcp_server.h b/contrib/epee/include/net/abstract_tcp_server.h index 1efd5091c..000305cfa 100644 --- a/contrib/epee/include/net/abstract_tcp_server.h +++ b/contrib/epee/include/net/abstract_tcp_server.h @@ -296,7 +296,7 @@ namespace net_utils } //---------------------------------------------------------------------------------------- template - bool abstract_tcp_server::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from) + bool abstract_tcp_server::invoke_connection(SOCKET hnew_sock, const network_address &remote_address) { m_connections_lock.lock(); m_connections.push_back(thread_context()); @@ -304,8 +304,7 @@ namespace net_utils m_connections.back().m_socket = hnew_sock; m_connections.back().powner = this; m_connections.back().m_self_it = --m_connections.end(); - m_connections.back().m_context.m_remote_ip = ip_from; - m_connections.back().m_context.m_remote_port = post_from; + m_connections.back().m_context.m_remote_address = remote_address; m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); return true; diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 506949dbd..ca58d5467 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -69,7 +69,7 @@ namespace net_utils struct i_connection_filter { - virtual bool is_remote_ip_allowed(uint32_t adress)=0; + virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address)=0; protected: virtual ~i_connection_filter(){} }; diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 75a9c5be9..0fbd9ed28 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -133,6 +133,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::system::error_code ec; auto remote_ep = socket_.remote_endpoint(ec); CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value()); + CHECK_AND_NO_ASSERT_MES(remote_ep.address().is_v4(), false, "IPv6 not supported here"); auto local_ep = socket_.local_endpoint(ec); CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value()); @@ -145,14 +146,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) // that stuff turns out to be included, even though it's from src... Taking advantage random_uuid = crypto::rand(); - context.set_details(random_uuid, ip_, remote_ep.port(), is_income); + context.set_details(random_uuid, new epee::net_utils::ipv4_network_address(ip_, remote_ep.port()), is_income); _dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << " to " << local_ep.address().to_string() << ':' << local_ep.port() << ", total sockets objects " << m_ref_sock_count); - if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip)) + if(m_pfilter && !m_pfilter->is_remote_host_allowed(context.m_remote_address)) { - _dbg2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection"); + _dbg2("[sock " << socket_.native_handle() << "] host denied " << context.m_remote_address.host_str() << ", shutdowning connection"); close(); return false; } diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.inl b/contrib/epee/include/net/abstract_tcp_server_cp.inl index ba201e3bf..e0ef6470e 100644 --- a/contrib/epee/include/net/abstract_tcp_server_cp.inl +++ b/contrib/epee/include/net/abstract_tcp_server_cp.inl @@ -471,7 +471,7 @@ bool cp_server_impl::run_server(int threads_count = 0) } //------------------------------------------------------------- template -bool cp_server_impl::add_new_connection(SOCKET new_sock, long ip_from, int port_from) +bool cp_server_impl::add_new_connection(SOCKET new_sock, const network_address &address_from) { PROFILE_FUNC("[add_new_connection]"); @@ -487,8 +487,7 @@ bool cp_server_impl::add_new_connection(SOCKET new_sock, long ip_from m_connections_lock.unlock(); conn.init_buffers(); conn.m_sock = new_sock; - conn.context.m_remote_ip = ip_from; - conn.context.m_remote_port = port_from; + conn.context.m_remote_address = address_from; conn.m_completion_port = m_completion_port; { PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort"); diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 8e39b4104..429e3e1af 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -39,7 +39,7 @@ epee::net_utils::http::http_response_info& response, \ context_type& m_conn_context) \ {\ - LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \ + LOG_PRINT_L2("HTTP [" << m_conn_context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \ response.m_response_code = 200; \ response.m_response_comment = "Ok"; \ if(!handle_http_request_map(query_info, response, m_conn_context)) \ diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h index 2cbdb4587..4b48070d6 100644 --- a/contrib/epee/include/net/levin_client_async.h +++ b/contrib/epee/include/net/levin_client_async.h @@ -492,8 +492,7 @@ namespace levin { net_utils::connection_context_base conn_context; - conn_context.m_remote_ip = m_ip; - conn_context.m_remote_port = m_port; + conn_context.m_remote_address = m_address; if(head.m_have_to_return_data) { std::string return_buff; diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 4334029f7..ba59daf4f 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -29,8 +29,11 @@ #ifndef _NET_UTILS_BASE_H_ #define _NET_UTILS_BASE_H_ +#include #include #include +#include "serialization/keyvalue_serialization.h" +#include "net/local_ip.h" #include "string_tools.h" #include "misc_log_ex.h" @@ -46,14 +49,111 @@ namespace epee { namespace net_utils { + struct network_address_base + { + public: + bool operator==(const network_address_base &other) const { return m_full_id == other.m_full_id; } + bool operator!=(const network_address_base &other) const { return !operator==(other); } + bool operator<(const network_address_base &other) const { return m_full_id < other.m_full_id; } + bool is_same_host(const network_address_base &other) const { return m_host_id == other.m_host_id; } + virtual std::string str() const = 0; + virtual std::string host_str() const = 0; + virtual bool is_loopback() const = 0; + virtual bool is_local() const = 0; + virtual uint8_t get_type_id() const = 0; + protected: + // A very simple non cryptographic hash function by Fowler, Noll, Vo + uint64_t fnv1a(const uint8_t *data, size_t len) const { + uint64_t h = 0xcbf29ce484222325; + while (len--) + h = (h ^ *data++) * 0x100000001b3; + return h; + } + uint64_t m_host_id; + uint64_t m_full_id; + }; + struct ipv4_network_address: public network_address_base + { + void init_ids() + { + m_host_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip)); + m_full_id = fnv1a((const uint8_t*)&m_ip, sizeof(m_ip) + sizeof(m_port)); + } + public: + ipv4_network_address(uint32_t ip, uint16_t port): network_address_base(), m_ip(ip), m_port(port) { init_ids(); } + uint32_t ip() const { return m_ip; } + uint16_t port() const { return m_port; } + virtual std::string str() const { return epee::string_tools::get_ip_string_from_int32(m_ip) + ":" + std::to_string(m_port); } + virtual std::string host_str() const { return epee::string_tools::get_ip_string_from_int32(m_ip); } + virtual bool is_loopback() const { return epee::net_utils::is_ip_loopback(m_ip); } + virtual bool is_local() const { return epee::net_utils::is_ip_local(m_ip); } + virtual uint8_t get_type_id() const { return ID; } + public: // serialization + static const uint8_t ID = 1; +#pragma pack(push) +#pragma pack(1) + uint32_t m_ip; + uint16_t m_port; +#pragma pack(pop) + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_ip) + KV_SERIALIZE(m_port) + if (!is_store) + const_cast(this_ref).init_ids(); + END_KV_SERIALIZE_MAP() + }; + class network_address: public boost::shared_ptr + { + public: + network_address() {} + network_address(ipv4_network_address *address): boost::shared_ptr(address) {} + bool operator==(const network_address &other) const { return (*this)->operator==(*other); } + bool operator!=(const network_address &other) const { return (*this)->operator!=(*other); } + bool operator<(const network_address &other) const { return (*this)->operator<(*other); } + bool is_same_host(const network_address &other) const { return (*this)->is_same_host(*other); } + std::string str() const { return (*this) ? (*this)->str() : ""; } + std::string host_str() const { return (*this) ? (*this)->host_str() : ""; } + bool is_loopback() const { return (*this)->is_loopback(); } + bool is_local() const { return (*this)->is_local(); } + const std::type_info &type() const { return typeid(**this); } + uint8_t get_type_id() const { return (*this)->get_type_id(); } + template Type &as() { if (type() != typeid(Type)) throw std::runtime_error("Bad type"); return *(Type*)get(); } + template const Type &as() const { if (type() != typeid(Type)) throw std::runtime_error("Bad type"); return *(const Type*)get(); } + + BEGIN_KV_SERIALIZE_MAP() + uint8_t type = is_store ? this_ref.get_type_id() : 0; + epee::serialization::selector::serialize(type, stg, hparent_section, "type"); + switch (type) + { + case ipv4_network_address::ID: + if (!is_store) + const_cast(this_ref).reset(new ipv4_network_address(0, 0)); + KV_SERIALIZE(as()); + break; + default: MERROR("Unsupported network address type: " << type); return false; + } + END_KV_SERIALIZE_MAP() + }; + inline bool create_network_address(network_address &address, const std::string &string, uint16_t default_port = 0) + { + uint32_t ip; + uint16_t port; + if (epee::string_tools::parse_peer_from_string(ip, port, string)) + { + if (default_port && !port) + port = default_port; + address.reset(new ipv4_network_address(ip, port)); + return true; + } + return false; + } /************************************************************************/ /* */ /************************************************************************/ struct connection_context_base { const boost::uuids::uuid m_connection_id; - const uint32_t m_remote_ip; - const uint32_t m_remote_port; + const network_address m_remote_address; const bool m_is_income; const time_t m_started; time_t m_last_recv; @@ -64,12 +164,11 @@ namespace net_utils double m_current_speed_up; connection_context_base(boost::uuids::uuid connection_id, - long remote_ip, int remote_port, bool is_income, + const network_address &remote_address, bool is_income, time_t last_recv = 0, time_t last_send = 0, uint64_t recv_cnt = 0, uint64_t send_cnt = 0): m_connection_id(connection_id), - m_remote_ip(remote_ip), - m_remote_port(remote_port), + m_remote_address(remote_address), m_is_income(is_income), m_started(time(NULL)), m_last_recv(last_recv), @@ -81,8 +180,7 @@ namespace net_utils {} connection_context_base(): m_connection_id(), - m_remote_ip(0), - m_remote_port(0), + m_remote_address(new ipv4_network_address(0,0)), m_is_income(false), m_started(time(NULL)), m_last_recv(0), @@ -95,17 +193,17 @@ namespace net_utils connection_context_base& operator=(const connection_context_base& a) { - set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income); + set_details(a.m_connection_id, a.m_remote_address, a.m_is_income); return *this; } private: template friend class connection; - void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income) + void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income) { this->~connection_context_base(); - new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income); + new(this) connection_context_base(connection_id, remote_address, is_income); } }; @@ -135,7 +233,7 @@ namespace net_utils std::string print_connection_context(const connection_context_base& ctx) { std::stringstream ss; - ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); + ss << ctx.m_remote_address->str() << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); return ss.str(); } @@ -143,7 +241,7 @@ namespace net_utils std::string print_connection_context_short(const connection_context_base& ctx) { std::stringstream ss; - ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT"); + ss << ctx.m_remote_address->str() << (ctx.m_is_income ? " INC":" OUT"); return ss.str(); } diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 16e60e99a..258caa49e 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -193,7 +193,7 @@ POP_WARNINGS //---------------------------------------------------------------------------- bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str); //---------------------------------------------------------------------------- - inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) + inline bool parse_peer_from_string(uint32_t& ip, uint16_t& port, const std::string& addres) { //parse ip and address std::string::size_type p = addres.find(':'); diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index fd5b980b8..6f6c1a803 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -49,6 +49,8 @@ namespace cryptonote bool localhost; bool local_ip; + std::string address; + std::string host; std::string ip; std::string port; @@ -76,6 +78,8 @@ namespace cryptonote KV_SERIALIZE(incoming) KV_SERIALIZE(localhost) KV_SERIALIZE(local_ip) + KV_SERIALIZE(address) + KV_SERIALIZE(host) KV_SERIALIZE(ip) KV_SERIALIZE(port) KV_SERIALIZE(peer_id) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 8b41ce514..465932e18 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -134,17 +134,12 @@ namespace cryptonote << std::setw(13) << "Up(now)" << ENDL; - uint32_t ip; m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { - bool local_ip = false; - ip = ntohl(cntxt.m_remote_ip); - // TODO: local ip in calss A, B - if (ip > 3232235520 && ip < 3232301055) // 192.168.x.x - local_ip = true; + bool local_ip = cntxt.m_remote_address.is_local(); auto connection_time = time(NULL) - cntxt.m_started; ss << std::setw(30) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + - epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) + cntxt.m_remote_address.str() << std::setw(20) << std::hex << peer_id << std::setw(20) << std::hex << support_flags << std::setw(30) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" @@ -155,7 +150,7 @@ namespace cryptonote << std::setw(10) << std::fixed << (connection_time == 0 ? 0.0 : cntxt.m_send_cnt / connection_time / 1024) << std::setw(13) << std::fixed << cntxt.m_current_speed_up / 1024 << (local_ip ? "[LAN]" : "") - << std::left << (ip == LOCALHOST_INT ? "[LOCALHOST]" : "") // 127.0.0.1 + << std::left << (cntxt.m_remote_address.is_loopback() ? "[LOCALHOST]" : "") // 127.0.0.1 << ENDL; if (connection_time > 1) @@ -193,8 +188,15 @@ namespace cryptonote cnx.incoming = cntxt.m_is_income ? true : false; - cnx.ip = epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip); - cnx.port = std::to_string(cntxt.m_remote_port); + cnx.address = cntxt.m_remote_address.str(); + cnx.host = cntxt.m_remote_address.host_str(); + cnx.ip = ""; + cnx.port = ""; + if (cntxt.m_remote_address.type() == typeid(epee::net_utils::ipv4_network_address)) + { + cnx.ip = cnx.host; + cnx.port = std::to_string(cntxt.m_remote_address.as().port()); + } std::stringstream peer_id_str; peer_id_str << std::hex << peer_id; @@ -212,25 +214,8 @@ namespace cryptonote cnx.live_time = timestamp - cntxt.m_started; - uint32_t ip; - ip = ntohl(cntxt.m_remote_ip); - if (ip == LOCALHOST_INT) - { - cnx.localhost = true; - } - else - { - cnx.localhost = false; - } - - if (ip > 3232235520 && ip < 3232301055) // 192.168.x.x - { - cnx.local_ip = true; - } - else - { - cnx.local_ip = false; - } + cnx.localhost = cntxt.m_remote_address.is_loopback(); + cnx.local_ip = cntxt.m_remote_address.is_local(); auto connection_time = time(NULL) - cntxt.m_started; if (connection_time == 0) @@ -990,7 +975,7 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); - m_p2p->add_ip_fail(context.m_remote_ip); + m_p2p->add_host_fail(context.m_remote_address); m_core.cleanup_handle_incoming_blocks(); return 1; } @@ -998,7 +983,7 @@ namespace cryptonote { LOG_PRINT_CCONTEXT_L1("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); + m_p2p->add_host_fail(context.m_remote_address); m_core.cleanup_handle_incoming_blocks(); return 1; } @@ -1150,7 +1135,7 @@ skip: { LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); m_p2p->drop_connection(context); - m_p2p->add_ip_fail(context.m_remote_ip); + m_p2p->add_host_fail(context.m_remote_address); return 1; } @@ -1159,7 +1144,7 @@ skip: 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); + m_p2p->add_host_fail(context.m_remote_address); return 1; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 13cd3f5b0..8798a52e0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -123,9 +123,9 @@ namespace nodetool size_t get_outgoing_connections_count(); peerlist_manager& get_peerlist_manager(){return m_peerlist;} void delete_connections(size_t count); - virtual bool block_ip(uint32_t adress, time_t seconds = P2P_IP_BLOCKTIME); - virtual bool unblock_ip(uint32_t address); - virtual std::map get_blocked_ips() { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); return m_blocked_ips; } + 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 std::map get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } private: const std::vector m_seed_nodes_list = { "seeds.moneroseeds.se" @@ -186,11 +186,11 @@ 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 add_ip_fail(uint32_t address); + virtual bool add_host_fail(const epee::net_utils::network_address &address); //----------------- i_connection_filter -------------------------------------------------------- - virtual bool is_remote_ip_allowed(uint32_t adress); + virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address); //----------------------------------------------------------------------------------------------- - bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); + 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( const boost::program_options::variables_map& vm ); @@ -209,18 +209,18 @@ namespace nodetool bool make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist); bool make_new_connection_from_peerlist(bool use_white_list); - bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0); + bool try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, PeerType peer_type = white, uint64_t first_seen_stamp = 0); size_t get_random_index_with_fixed_probability(size_t max_index); bool is_peer_used(const peerlist_entry& peer); bool is_peer_used(const anchor_peerlist_entry& peer); - bool is_addr_connected(const net_address& peer); + bool is_addr_connected(const epee::net_utils::network_address& peer); template bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); bool try_get_support_flags(const p2p_connection_context& context, std::function f); bool make_expected_connections_count(PeerType peer_type, 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); + void cache_connect_fail_info(const epee::net_utils::network_address& addr); + bool is_addr_recently_failed(const epee::net_utils::network_address& addr); + bool is_priority_node(const epee::net_utils::network_address& na); std::set get_seed_nodes(bool testnet) const; template @@ -236,9 +236,9 @@ namespace nodetool bool set_rate_down_limit(const boost::program_options::variables_map& vm, int64_t limit); bool set_rate_limit(const boost::program_options::variables_map& vm, int64_t limit); - bool has_too_many_connections(const uint32_t ip); + bool has_too_many_connections(const epee::net_utils::network_address &address); - bool check_connection_and_handshake_with_peer(const net_address& na, uint64_t last_seen_stamp); + bool check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp); bool gray_peerlist_housekeeping(); void kill() { ///< will be called e.g. from deinit() @@ -308,23 +308,23 @@ namespace nodetool #ifdef ALLOW_DEBUG_COMMANDS uint64_t m_last_stat_request_time; #endif - std::list m_priority_peers; - std::vector m_exclusive_peers; - std::vector m_seed_nodes; + std::list m_priority_peers; + std::vector m_exclusive_peers; + std::vector m_seed_nodes; std::list m_command_line_peers; uint64_t m_peer_livetime; //keep connections to initiate some interactions net_server m_net_server; boost::uuids::uuid m_network_id; - std::map m_conn_fails_cache; + 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_blocked_hosts_lock; + std::map m_blocked_hosts; - epee::critical_section m_ip_fails_score_lock; - std::map m_ip_fails_score; + epee::critical_section m_host_fails_score_lock; + std::map m_host_fails_score; bool m_testnet; }; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 5c903b1f5..47582cb96 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -200,16 +200,16 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::is_remote_ip_allowed(uint32_t addr) + bool node_server::is_remote_host_allowed(const epee::net_utils::network_address &address) { - CRITICAL_REGION_LOCAL(m_blocked_ips_lock); - auto it = m_blocked_ips.find(addr); - if(it == m_blocked_ips.end()) + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + auto it = m_blocked_hosts.find(address.host_str()); + if(it == m_blocked_hosts.end()) return true; if(time(nullptr) >= it->second) { - m_blocked_ips.erase(it); - MCLOG_CYAN(el::Level::Info, "global", "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked."); + m_blocked_hosts.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); return true; } return false; @@ -229,16 +229,16 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::block_ip(uint32_t addr, time_t seconds) + bool node_server::block_host(const epee::net_utils::network_address &addr, time_t seconds) { - CRITICAL_REGION_LOCAL(m_blocked_ips_lock); - m_blocked_ips[addr] = time(nullptr) + seconds; + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + m_blocked_hosts[addr.host_str()] = time(nullptr) + seconds; // drop any connection to that IP std::list conns; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if (cntxt.m_remote_ip == addr) + if (cntxt.m_remote_address.is_same_host(addr)) { conns.push_back(cntxt.m_connection_id); } @@ -247,42 +247,42 @@ namespace nodetool for (const auto &c: conns) m_net_server.get_config_object().close(c); - MCLOG_CYAN(el::Level::Info, "global", "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked."); + MCLOG_CYAN(el::Level::Info, "global", "Host " << addr.host_str() << " blocked."); return true; } //----------------------------------------------------------------------------------- template - bool node_server::unblock_ip(uint32_t addr) + bool node_server::unblock_host(const epee::net_utils::network_address &address) { - CRITICAL_REGION_LOCAL(m_blocked_ips_lock); - auto i = m_blocked_ips.find(addr); - if (i == m_blocked_ips.end()) + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + auto i = m_blocked_hosts.find(address.host_str()); + if (i == m_blocked_hosts.end()) return false; - m_blocked_ips.erase(i); - MCLOG_CYAN(el::Level::Info, "global", "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked."); + m_blocked_hosts.erase(i); + MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); return true; } //----------------------------------------------------------------------------------- template - bool node_server::add_ip_fail(uint32_t address) + bool node_server::add_host_fail(const epee::net_utils::network_address &address) { - CRITICAL_REGION_LOCAL(m_ip_fails_score_lock); - uint64_t fails = ++m_ip_fails_score[address]; - MDEBUG("IP " << epee::string_tools::get_ip_string_from_int32(address) << " fail score=" << fails); + CRITICAL_REGION_LOCAL(m_host_fails_score_lock); + uint64_t fails = ++m_host_fails_score[address.host_str()]; + MDEBUG("Host " << address.host_str() << " fail score=" << fails); if(fails > P2P_IP_FAILS_BEFORE_BLOCK) { - auto it = m_ip_fails_score.find(address); - CHECK_AND_ASSERT_MES(it != m_ip_fails_score.end(), false, "internal error"); + auto it = m_host_fails_score.find(address.host_str()); + CHECK_AND_ASSERT_MES(it != m_host_fails_score.end(), false, "internal error"); it->second = P2P_IP_FAILS_BEFORE_BLOCK/2; - block_ip(address); + block_host(address); } return true; } //----------------------------------------------------------------------------------- template - bool node_server::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) + bool node_server::parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port) { - return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); + return epee::net_utils::create_network_address(pe, node_addr, default_port); } //----------------------------------------------------------------------------------- template @@ -306,10 +306,9 @@ namespace nodetool { nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe); pe.id = crypto::rand(); - bool r = parse_peer_from_string(pe.adr, pr_str); + const uint16_t default_port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + bool r = parse_peer_from_string(pe.adr, pr_str, default_port); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - if (pe.adr.port == 0) - pe.adr.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; m_command_line_peers.push_back(pe); } } @@ -359,7 +358,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- inline void append_net_address( - std::vector & seed_nodes + std::vector & seed_nodes , std::string const & addr ) { @@ -383,15 +382,14 @@ namespace nodetool ip::tcp::endpoint endpoint = *i; if (endpoint.address().is_v4()) { - nodetool::net_address na; - na.ip = boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()); - na.port = endpoint.port(); + epee::net_utils::network_address na(new epee::net_utils::ipv4_network_address(boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port())); seed_nodes.push_back(na); - MINFO("Added seed node: " << endpoint.address().to_v4().to_string(ec) << ':' << na.port); + MINFO("Added seed node: " << na.str()); } else { - MDEBUG("IPv6 doesn't supported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); + MERROR("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); + throw std::runtime_error("IPv6 unsupported"); } } } @@ -752,10 +750,10 @@ namespace nodetool return; } - if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) + if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.node_data.local_time, context)) { LOG_ERROR_CC(context, "COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); - add_ip_fail(context.m_remote_ip); + add_host_fail(context.m_remote_address); return; } hsh_result = true; @@ -769,7 +767,7 @@ namespace nodetool } pi = context.peer_id = rsp.node_data.peer_id; - m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); + m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address); if(rsp.node_data.peer_id == m_config.m_peer_id) { @@ -820,14 +818,14 @@ namespace nodetool return; } - if(!handle_remote_peerlist(rsp.local_peerlist, rsp.local_time, context)) + if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.local_time, context)) { LOG_WARNING_CC(context, "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); + add_host_fail(context.m_remote_address); } if(!context.m_is_income) - m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); + m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address); m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); }); @@ -862,7 +860,7 @@ namespace nodetool bool used = false; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr == cntxt.m_remote_address)) { used = true; return false;//stop enumerating @@ -884,7 +882,7 @@ namespace nodetool m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr == cntxt.m_remote_address)) { used = true; @@ -898,12 +896,12 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::is_addr_connected(const net_address& peer) + bool node_server::is_addr_connected(const epee::net_utils::network_address& peer) { bool connected = false; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if(!cntxt.m_is_income && peer.ip == cntxt.m_remote_ip && peer.port == cntxt.m_remote_port) + if(!cntxt.m_is_income && peer == cntxt.m_remote_address) { connected = true; return false;//stop enumerating @@ -924,7 +922,7 @@ namespace nodetool } while(0) template - bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) + bool node_server::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) { if (m_current_number_of_out_peers == m_config.m_net_config.connections_count) // out peers limit { @@ -936,23 +934,24 @@ namespace nodetool m_current_number_of_out_peers --; // atomic variable, update time = 1s return false; } - MDEBUG("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" - << epee::string_tools::num_to_string_fast(na.port) << "(peer_type=" << peer_type << ", last_seen: " + MDEBUG("Connecting to " << na.str() << "(peer_type=" << peer_type << ", last_seen: " << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << ")..."); + CHECK_AND_ASSERT_MES(na.type() == typeid(epee::net_utils::ipv4_network_address), false, + "Only IPv4 addresses are supported here, got " << na.type().name()); + const epee::net_utils::ipv4_network_address &ipv4 = na.as(); + typename net_server::t_connection_context con = AUTO_VAL_INIT(con); - bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(na.ip), - epee::string_tools::num_to_string_fast(na.port), + bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), + epee::string_tools::num_to_string_fast(ipv4.port()), m_config.m_net_config.connection_timeout, con); if(!res) { bool is_priority = is_priority_node(na); - LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " - << epee::string_tools::get_ip_string_from_int32(na.ip) - << ":" << epee::string_tools::num_to_string_fast(na.port) + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " << na.str() /*<< ", try " << try_count*/); //m_peerlist.set_peer_unreachable(pe); return false; @@ -965,8 +964,7 @@ namespace nodetool { bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Failed to HANDSHAKE with peer " - << epee::string_tools::get_ip_string_from_int32(na.ip) - << ":" << epee::string_tools::num_to_string_fast(na.port) + << na.str() /*<< ", try " << try_count*/); return false; } @@ -999,25 +997,26 @@ namespace nodetool } template - bool node_server::check_connection_and_handshake_with_peer(const net_address& na, uint64_t last_seen_stamp) + bool node_server::check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp) { - LOG_PRINT_L1("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" - << epee::string_tools::num_to_string_fast(na.port) << "(last_seen: " + LOG_PRINT_L1("Connecting to " << na.str() << "(last_seen: " << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << ")..."); + CHECK_AND_ASSERT_MES(na.type() == typeid(epee::net_utils::ipv4_network_address), false, + "Only IPv4 addresses are supported here, got " << na.type().name()); + const epee::net_utils::ipv4_network_address &ipv4 = na.as(); + typename net_server::t_connection_context con = AUTO_VAL_INIT(con); - bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(na.ip), - epee::string_tools::num_to_string_fast(na.port), + bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), + epee::string_tools::num_to_string_fast(ipv4.port()), m_config.m_net_config.connection_timeout, con); if (!res) { bool is_priority = is_priority_node(na); - LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " - << epee::string_tools::get_ip_string_from_int32(na.ip) - << ":" << epee::string_tools::num_to_string_fast(na.port)); + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " << na.str()); return false; } @@ -1028,9 +1027,7 @@ namespace nodetool if (!res) { bool is_priority = is_priority_node(na); - LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Failed to HANDSHAKE with peer " - << epee::string_tools::get_ip_string_from_int32(na.ip) - << ":" << epee::string_tools::num_to_string_fast(na.port)); + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Failed to HANDSHAKE with peer " << na.str()); return false; } @@ -1046,7 +1043,7 @@ namespace nodetool //----------------------------------------------------------------------------------- template - bool node_server::is_addr_recently_failed(const net_address& addr) + bool node_server::is_addr_recently_failed(const epee::net_utils::network_address& addr) { CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); auto it = m_conn_fails_cache.find(addr); @@ -1063,14 +1060,14 @@ namespace nodetool bool node_server::make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist) { for (const auto& pe: anchor_peerlist) { - _note("Considering connecting (out) to peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port)); + _note("Considering connecting (out) to peer: " << pe.id << " " << pe.adr.str()); if(is_peer_used(pe)) { _note("Peer is used"); continue; } - if(!is_remote_ip_allowed(pe.adr.ip)) { + if(!is_remote_host_allowed(pe.adr)) { continue; } @@ -1078,8 +1075,7 @@ namespace nodetool continue; } - MDEBUG("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) - << ":" << boost::lexical_cast(pe.adr.port) + MDEBUG("Selected peer: " << pe.id << " " << pe.adr.str() << "[peer_type=" << anchor << "] first_seen: " << epee::misc_utils::get_time_interval_string(time(NULL) - pe.first_seen)); @@ -1130,21 +1126,20 @@ namespace nodetool ++try_count; - _note("Considering connecting (out) to peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port)); + _note("Considering connecting (out) to peer: " << pe.id << " " << pe.adr.str()); if(is_peer_used(pe)) { _note("Peer is used"); continue; } - if(!is_remote_ip_allowed(pe.adr.ip)) + if(!is_remote_host_allowed(pe.adr)) continue; if(is_addr_recently_failed(pe.adr)) continue; - MDEBUG("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) - << ":" << boost::lexical_cast(pe.adr.port) + MDEBUG("Selected peer: " << pe.id << " " << pe.adr.str() << "[peer_list=" << (use_white_list ? white : gray) << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); @@ -1325,7 +1320,7 @@ namespace nodetool { if(be.last_seen > local_time) { - MWARNING("FOUND FUTURE peerlist for entry " << epee::string_tools::get_ip_string_from_int32(be.adr.ip) << ":" << be.adr.port << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time); + MWARNING("FOUND FUTURE peerlist for entry " << be.adr.str() << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time); return false; } be.last_seen += delta; @@ -1421,8 +1416,7 @@ namespace nodetool m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { connection_entry ce; - ce.adr.ip = cntxt.m_remote_ip; - ce.adr.port = cntxt.m_remote_port; + ce.adr = cntxt.m_remote_address; ce.id = cntxt.peer_id; ce.is_income = cntxt.m_is_income; rsp.connections_list.push_back(ce); @@ -1512,19 +1506,24 @@ namespace nodetool if(!node_data.my_port) return false; - uint32_t actual_ip = context.m_remote_ip; - if(!m_peerlist.is_ip_allowed(actual_ip)) + CHECK_AND_ASSERT_MES(context.m_remote_address.type() == typeid(epee::net_utils::ipv4_network_address), false, + "Only IPv4 addresses are supported here, got " << context.m_remote_address.type().name()); + + const epee::net_utils::network_address na = context.m_remote_address; + uint32_t actual_ip = na.as().ip(); + if(!m_peerlist.is_host_allowed(context.m_remote_address)) return false; std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip); std::string port = epee::string_tools::num_to_string_fast(node_data.my_port); + epee::net_utils::network_address address(new epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)); peerid_type pr = node_data.peer_id; - bool r = m_net_server.connect_async(ip, port, m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ ip, port, pr, this]( + bool r = m_net_server.connect_async(ip, port, m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this]( const typename net_server::t_connection_context& ping_context, const boost::system::error_code& ec)->bool { if(ec) { - LOG_WARNING_CC(ping_context, "back ping connect failed to " << ip << ":" << port); + LOG_WARNING_CC(ping_context, "back ping connect failed to " << address.str()); return false; } COMMAND_PING::request req; @@ -1543,13 +1542,13 @@ namespace nodetool { if(code <= 0) { - LOG_ERROR_CC(ping_context, "Failed to invoke COMMAND_PING to " << ip << ":" << port << "(" << code << ", " << epee::levin::get_err_descr(code) << ")"); + LOG_ERROR_CC(ping_context, "Failed to invoke COMMAND_PING to " << address.str() << "(" << code << ", " << epee::levin::get_err_descr(code) << ")"); return; } if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) { - LOG_ERROR_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << ip << ":" << port << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << rsp.peer_id); + LOG_ERROR_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << rsp.peer_id); m_net_server.get_config_object().close(ping_context.m_connection_id); return; } @@ -1559,7 +1558,7 @@ namespace nodetool if(!inv_call_res) { - LOG_ERROR_CC(ping_context, "back ping invoke failed to " << ip << ":" << port); + LOG_ERROR_CC(ping_context, "back ping invoke failed to " << address.str()); m_net_server.get_config_object().close(ping_context.m_connection_id); return false; } @@ -1610,7 +1609,7 @@ namespace nodetool //fill response rsp.local_time = time(NULL); - m_peerlist.get_peerlist_head(rsp.local_peerlist); + m_peerlist.get_peerlist_head(rsp.local_peerlist_new); m_payload_handler.get_payload_sync_data(rsp.payload_data); LOG_DEBUG_CC(context, "COMMAND_TIMED_SYNC"); return 1; @@ -1624,7 +1623,7 @@ namespace nodetool LOG_INFO_CC(context, "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); + add_host_fail(context.m_remote_address); return 1; } @@ -1632,7 +1631,7 @@ namespace nodetool { LOG_ERROR_CC(context, "COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); - add_ip_fail(context.m_remote_ip); + add_host_fail(context.m_remote_address); return 1; } @@ -1650,9 +1649,9 @@ namespace nodetool return 1; } - if(has_too_many_connections(context.m_remote_ip)) + if(has_too_many_connections(context.m_remote_address)) { - LOG_PRINT_CCONTEXT_L1("CONNECTION FROM " << epee::string_tools::get_ip_string_from_int32(context.m_remote_ip) << " REFUSED, too many connections from the same address"); + LOG_PRINT_CCONTEXT_L1("CONNECTION FROM " << context.m_remote_address.host_str() << " REFUSED, too many connections from the same address"); drop_connection(context); return 1; } @@ -1667,16 +1666,18 @@ namespace nodetool //try ping to be sure that we can add this peer to peer_list try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() { + CHECK_AND_ASSERT_MES(context.m_remote_address.type() == typeid(epee::net_utils::ipv4_network_address), void(), + "Only IPv4 addresses are supported here, got " << context.m_remote_address.type().name()); //called only(!) if success pinged, update local peerlist peerlist_entry pe; - pe.adr.ip = context.m_remote_ip; - pe.adr.port = port_l; + const epee::net_utils::network_address na = context.m_remote_address; + pe.adr.reset(new epee::net_utils::ipv4_network_address(na.as().ip(), port_l)); time_t last_seen; time(&last_seen); pe.last_seen = static_cast(last_seen); pe.id = peer_id_l; this->m_peerlist.append_with_peer_white(pe); - LOG_DEBUG_CC(context, "PING SUCCESS " << epee::string_tools::get_ip_string_from_int32(context.m_remote_ip) << ":" << port_l); + LOG_DEBUG_CC(context, "PING SUCCESS " << context.m_remote_address.host_str() << ":" << port_l); }); } @@ -1686,7 +1687,7 @@ namespace nodetool }); //fill response - m_peerlist.get_peerlist_head(rsp.local_peerlist); + m_peerlist.get_peerlist_head(rsp.local_peerlist_new); get_local_node_data(rsp.node_data); m_payload_handler.get_payload_sync_data(rsp.payload_data); LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE"); @@ -1726,7 +1727,7 @@ namespace nodetool std::stringstream ss; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - ss << epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) << ":" << cntxt.m_remote_port + ss << cntxt.m_remote_address.str() << " \t\tpeer_id " << cntxt.peer_id << " \t\tconn_id " << epee::string_tools::get_str_from_guid_a(cntxt.m_connection_id) << (cntxt.m_is_income ? " INC":" OUT") << std::endl; @@ -1746,9 +1747,8 @@ namespace nodetool void node_server::on_connection_close(p2p_connection_context& context) { if (!m_net_server.is_stop_signal_sent() && !context.m_is_income) { - nodetool::net_address na = AUTO_VAL_INIT(na); - na.ip = context.m_remote_ip; - na.port = context.m_remote_port; + epee::net_utils::network_address na = AUTO_VAL_INIT(na); + na = context.m_remote_address; m_peerlist.remove_from_peer_anchor(na); } @@ -1757,7 +1757,7 @@ namespace nodetool } template - bool node_server::is_priority_node(const net_address& na) + bool node_server::is_priority_node(const epee::net_utils::network_address& na) { return (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); } @@ -1765,7 +1765,7 @@ namespace nodetool template template bool node_server::connect_to_peerlist(const Container& peers) { - for(const net_address& na: peers) + for(const epee::net_utils::network_address& na: peers) { if(m_net_server.is_stop_signal_sent()) return false; @@ -1786,11 +1786,10 @@ namespace nodetool for(const std::string& pr_str: perrs) { - nodetool::net_address na = AUTO_VAL_INIT(na); - bool r = parse_peer_from_string(na, pr_str); + epee::net_utils::network_address na = AUTO_VAL_INIT(na); + const uint16_t default_port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + bool r = parse_peer_from_string(na, pr_str, default_port); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - if (na.port == 0) - na.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; container.push_back(na); } @@ -1884,14 +1883,14 @@ namespace nodetool } template - bool node_server::has_too_many_connections(const uint32_t ip) + bool node_server::has_too_many_connections(const epee::net_utils::network_address &address) { const uint8_t max_connections = 1; uint8_t count = 0; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if (cntxt.m_is_income && cntxt.m_remote_ip == ip) { + if (cntxt.m_is_income && cntxt.m_remote_address.is_same_host(address)) { count++; if (count > max_connections) { @@ -1919,14 +1918,14 @@ namespace nodetool if (!success) { m_peerlist.remove_from_peer_gray(pe); - LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST IP address: " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << " Peer ID: " << std::hex << pe.id); + LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << std::hex << pe.id); return true; } m_peerlist.set_peer_just_seen(pe.id, pe.adr); - LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << " Peer ID: " << std::hex << pe.id); + LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << std::hex << pe.id); return true; } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 6aba7aa24..42de2655d 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -51,10 +51,10 @@ 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, time_t seconds = 0)=0; - virtual bool unblock_ip(uint32_t adress)=0; - virtual std::map get_blocked_ips()=0; - virtual bool add_ip_fail(uint32_t adress)=0; + 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 get_blocked_hosts()=0; + virtual bool add_host_fail(const epee::net_utils::network_address &address)=0; }; template @@ -93,19 +93,19 @@ namespace nodetool { return false; } - virtual bool block_ip(uint32_t adress, time_t seconds) + virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds) { return true; } - virtual bool unblock_ip(uint32_t adress) + virtual bool unblock_host(const epee::net_utils::network_address &address) { return true; } - virtual std::map get_blocked_ips() + virtual std::map get_blocked_hosts() { - return std::map(); + return std::map(); } - virtual bool add_ip_fail(uint32_t adress) + virtual bool add_host_fail(const epee::net_utils::network_address &address) { return true; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index de0f51cc3..a30a05422 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -54,7 +54,7 @@ #include "net_peerlist_boost_serialization.h" -#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 5 +#define CURRENT_PEERLIST_STORAGE_ARCHIVE_VER 6 namespace nodetool { @@ -78,14 +78,13 @@ namespace nodetool bool append_with_peer_white(const peerlist_entry& pr); bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); - bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); - bool set_peer_just_seen(peerid_type peer, const net_address& addr); + bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr); bool set_peer_unreachable(const peerlist_entry& pr); - bool is_ip_allowed(uint32_t ip); + bool is_host_allowed(const epee::net_utils::network_address &address); bool get_random_gray_peer(peerlist_entry& pe); bool remove_from_peer_gray(const peerlist_entry& pe); bool get_and_empty_anchor_peerlist(std::vector& apl); - bool remove_from_peer_anchor(const net_address& addr); + bool remove_from_peer_anchor(const epee::net_utils::network_address& addr); private: struct by_time{}; @@ -130,7 +129,7 @@ namespace nodetool peerlist_entry, boost::multi_index::indexed_by< // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, + boost::multi_index::ordered_unique, boost::multi_index::member >, // sort by peerlist_entry::last_seen< boost::multi_index::ordered_non_unique, boost::multi_index::member > > @@ -142,7 +141,7 @@ namespace nodetool // access by peerlist_entry::id< boost::multi_index::ordered_unique, boost::multi_index::member >, // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, + boost::multi_index::ordered_unique, boost::multi_index::member >, // sort by peerlist_entry::last_seen< boost::multi_index::ordered_non_unique, boost::multi_index::member > > @@ -152,16 +151,45 @@ namespace nodetool anchor_peerlist_entry, boost::multi_index::indexed_by< // access by anchor_peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, + boost::multi_index::ordered_unique, boost::multi_index::member >, // sort by anchor_peerlist_entry::first_seen boost::multi_index::ordered_non_unique, boost::multi_index::member > > > anchor_peers_indexed; public: + template + void serialize_peers(Archive &a, List &list, Element ple, const t_version_type ver) + { + if (typename Archive::is_saving()) + { + uint64_t size = list.size(); + a & size; + for (auto p: list) + { + a & p; + } + } + else + { + uint64_t size; + a & size; + list.clear(); + while (size--) + { + a & ple; + list.insert(ple); + } + } + } + template void serialize(Archive &a, const t_version_type ver) { + // at v6, we drop existing peerlists, because annoying change + if (ver < 6) + return; + if(ver < 3) return; CRITICAL_REGION_LOCAL(m_peerlist_lock); @@ -174,14 +202,25 @@ namespace nodetool return; } +#if 0 + // trouble loading more than one peer, can't find why a & m_peers_white; a & m_peers_gray; +#else + serialize_peers(a, m_peers_white, peerlist_entry(), ver); + serialize_peers(a, m_peers_gray, peerlist_entry(), ver); +#endif if(ver < 5) { return; } +#if 0 + // trouble loading more than one peer, can't find why a & m_peers_anchor; +#else + serialize_peers(a, m_peers_anchor, anchor_peerlist_entry(), ver); +#endif } private: @@ -284,13 +323,13 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::is_ip_allowed(uint32_t ip) + bool peerlist_manager::is_host_allowed(const epee::net_utils::network_address &address) { //never allow loopback ip - if(epee::net_utils::is_ip_loopback(ip)) + if(address.is_loopback()) return false; - if(!m_allow_local_ip && epee::net_utils::is_ip_local(ip)) + if(!m_allow_local_ip && address.is_local()) return false; return true; @@ -336,16 +375,7 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port) - { - net_address addr; - addr.ip = ip; - addr.port = port; - return set_peer_just_seen(peer, addr); - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) + bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr) { TRY_ENTRY(); CRITICAL_REGION_LOCAL(m_peerlist_lock); @@ -362,7 +392,7 @@ namespace nodetool bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) { TRY_ENTRY(); - if(!is_ip_allowed(ple.adr.ip)) + if(!is_host_allowed(ple.adr)) return true; CRITICAL_REGION_LOCAL(m_peerlist_lock); @@ -392,7 +422,7 @@ namespace nodetool bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) { TRY_ENTRY(); - if(!is_ip_allowed(ple.adr.ip)) + if(!is_host_allowed(ple.adr)) return true; CRITICAL_REGION_LOCAL(m_peerlist_lock); @@ -496,7 +526,7 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::remove_from_peer_anchor(const net_address& addr) + bool peerlist_manager::remove_from_peer_anchor(const epee::net_utils::network_address& addr) { TRY_ENTRY(); diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 6891ac80c..0a21895cf 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -30,16 +30,43 @@ #pragma once +#include "net/net_utils_base.h" + namespace boost { namespace serialization { - //BOOST_CLASS_VERSION(odetool::net_adress, 1) - template - inline void serialize(Archive &a, nodetool::net_address& na, const ver_type ver) + enum { sertype_ipv4_address }; + static inline uint8_t get_type(const epee::net_utils::network_address &na) { - a & na.ip; - a & na.port; + if (na.type() == typeid(epee::net_utils::ipv4_network_address)) + return sertype_ipv4_address; + throw std::runtime_error("Unsupported network address type"); + return 0; + } + template + inline void serialize(Archive &a, epee::net_utils::network_address& na, const ver_type ver) + { + uint8_t type; + if (typename Archive::is_saving()) + type = get_type(na); + a & type; + switch (type) + { + case sertype_ipv4_address: + if (!typename Archive::is_saving()) + na.reset(new epee::net_utils::ipv4_network_address(0, 0)); + a & na.as(); + break; + default: + throw std::runtime_error("Unsupported network address type"); + } + } + template + inline void serialize(Archive &a, epee::net_utils::ipv4_network_address& na, const ver_type ver) + { + a & na.m_ip; + a & na.m_port; } diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 5ec012714..d60990f8a 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -32,6 +32,7 @@ #include #include "serialization/keyvalue_serialization.h" +#include "net/net_utils_base.h" #include "misc_language.h" #include "cryptonote_config.h" #include "crypto/crypto.h" @@ -43,46 +44,64 @@ namespace nodetool #pragma pack (push, 1) - struct net_address + struct network_address_old { uint32_t ip; uint32_t port; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(ip) + KV_SERIALIZE(port) + END_KV_SERIALIZE_MAP() }; - struct peerlist_entry + template + struct peerlist_entry_base { - net_address adr; + AddressType adr; peerid_type id; int64_t last_seen; - }; - struct anchor_peerlist_entry + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(adr) + KV_SERIALIZE(id) + KV_SERIALIZE(last_seen) + END_KV_SERIALIZE_MAP() + }; + typedef peerlist_entry_base peerlist_entry; + + template + struct anchor_peerlist_entry_base { - net_address adr; + AddressType adr; peerid_type id; int64_t first_seen; - }; - struct connection_entry + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(adr) + KV_SERIALIZE(id) + KV_SERIALIZE(first_seen) + END_KV_SERIALIZE_MAP() + }; + typedef anchor_peerlist_entry_base anchor_peerlist_entry; + + template + struct connection_entry_base { - net_address adr; + AddressType adr; peerid_type id; bool is_income; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(adr) + KV_SERIALIZE(id) + KV_SERIALIZE(is_income) + END_KV_SERIALIZE_MAP() }; + typedef connection_entry_base connection_entry; #pragma pack(pop) - inline - bool operator < (const net_address& a, const net_address& b) - { - return epee::misc_utils::is_less_as_pod(a, b); - } - - inline - bool operator == (const net_address& a, const net_address& b) - { - return memcmp(&a, &b, sizeof(a)) == 0; - } inline std::string print_peerlist_to_string(const std::list& pl) { @@ -92,7 +111,7 @@ namespace nodetool ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; for(const peerlist_entry& pe: pl) { - ss << pe.id << "\t" << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; + ss << pe.id << "\t" << pe.adr->str() << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; } return ss.str(); } @@ -157,12 +176,40 @@ namespace nodetool { basic_node_data node_data; t_playload_type payload_data; - std::list local_peerlist; + std::list local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(node_data) KV_SERIALIZE(payload_data) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) + if (is_store) + { + // saving: save both, so old and new peers can understand it + KV_SERIALIZE(local_peerlist_new) + std::list> local_peerlist; + for (const auto &p: this_ref.local_peerlist_new) + { + if (p.adr.type() == typeid(epee::net_utils::ipv4_network_address)) + { + const epee::net_utils::network_address &na = p.adr; + const epee::net_utils::ipv4_network_address &ipv4 = na.as(); + local_peerlist.push_back(peerlist_entry_base({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen})); + } + else + MDEBUG("Not including in legacy peer list: " << p.adr.str()); + } + epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); + } + else + { + // loading: load old list only if there is no new one + if (!epee::serialization::selector::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) + { + std::list> local_peerlist; + epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); + for (const auto &p: local_peerlist) + ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({new epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); + } + } END_KV_SERIALIZE_MAP() }; }; @@ -188,12 +235,40 @@ namespace nodetool { uint64_t local_time; t_playload_type payload_data; - std::list local_peerlist; + std::list local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(local_time) KV_SERIALIZE(payload_data) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) + if (is_store) + { + // saving: save both, so old and new peers can understand it + KV_SERIALIZE(local_peerlist_new) + std::list> local_peerlist; + for (const auto &p: this_ref.local_peerlist_new) + { + if (p.adr.type() == typeid(epee::net_utils::ipv4_network_address)) + { + const epee::net_utils::network_address &na = p.adr; + const epee::net_utils::ipv4_network_address &ipv4 = na.as(); + local_peerlist.push_back(peerlist_entry_base({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen})); + } + else + MDEBUG("Not including in legacy peer list: " << p.adr.str()); + } + epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); + } + else + { + // loading: load old list only if there is no new one + if (!epee::serialization::selector::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) + { + std::list> local_peerlist; + epee::serialization::selector::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); + for (const auto &p: local_peerlist) + ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({new epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); + } + } END_KV_SERIALIZE_MAP() }; }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 43891458c..7dc5a1fd9 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -736,12 +736,20 @@ namespace cryptonote for (auto & entry : white_list) { - res.white_list.emplace_back(entry.id, entry.adr.ip, entry.adr.port, entry.last_seen); + if (entry.adr.type() == typeid(epee::net_utils::ipv4_network_address)) + res.white_list.emplace_back(entry.id, entry.adr.as().ip(), + entry.adr.as().port(), entry.last_seen); + else + res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen); } for (auto & entry : gray_list) { - res.gray_list.emplace_back(entry.id, entry.adr.ip, entry.adr.port, entry.last_seen); + if (entry.adr.type() == typeid(epee::net_utils::ipv4_network_address)) + res.gray_list.emplace_back(entry.id, entry.adr.as().ip(), + entry.adr.as().port(), entry.last_seen); + else + res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen); } res.status = CORE_RPC_STATUS_OK; @@ -1308,12 +1316,16 @@ namespace cryptonote } auto now = time(nullptr); - std::map blocked_ips = m_p2p.get_blocked_ips(); - for (std::map::const_iterator i = blocked_ips.begin(); i != blocked_ips.end(); ++i) + std::map blocked_hosts = m_p2p.get_blocked_hosts(); + for (std::map::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i) { if (i->second > now) { COMMAND_RPC_GETBANS::ban b; - b.ip = i->first; + b.host = i->first; + b.ip = 0; + uint32_t ip; + if (epee::string_tools::get_ip_int32_from_string(ip, i->first)) + b.ip = ip; b.seconds = i->second - now; res.bans.push_back(b); } @@ -1334,10 +1346,24 @@ namespace cryptonote for (auto i = req.bans.begin(); i != req.bans.end(); ++i) { - if (i->ban) - m_p2p.block_ip(i->ip, i->seconds); + epee::net_utils::network_address na; + if (!i->host.empty()) + { + if (!epee::net_utils::create_network_address(na, i->host)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Unsupported host type"; + return false; + } + } else - m_p2p.unblock_ip(i->ip); + { + na.reset(new epee::net_utils::ipv4_network_address(i->ip, 0)); + } + if (i->ban) + m_p2p.block_host(na, i->seconds); + else + m_p2p.unblock_host(na); } res.status = CORE_RPC_STATUS_OK; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 8b5f189ca..f5fe46375 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -861,18 +861,23 @@ namespace cryptonote struct peer { uint64_t id; + std::string host; uint32_t ip; uint16_t port; uint64_t last_seen; peer() = default; + peer(uint64_t id, const std::string &host, uint64_t last_seen) + : id(id), host(host), ip(0), port(0), last_seen(last_seen) + {} peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen) - : id(id), ip(ip), port(port), last_seen(last_seen) + : id(id), host(std::to_string(ip)), ip(ip), port(port), last_seen(last_seen) {} BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(id) + KV_SERIALIZE(host) KV_SERIALIZE(ip) KV_SERIALIZE(port) KV_SERIALIZE(last_seen) @@ -1271,10 +1276,12 @@ namespace cryptonote { struct ban { + std::string host; uint32_t ip; uint32_t seconds; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(host) KV_SERIALIZE(ip) KV_SERIALIZE(seconds) END_KV_SERIALIZE_MAP() @@ -1302,11 +1309,13 @@ namespace cryptonote { struct ban { + std::string host; uint32_t ip; bool ban; uint32_t seconds; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(host) KV_SERIALIZE(ip) KV_SERIALIZE(ban) KV_SERIALIZE(seconds)