diff options
Diffstat (limited to 'src/p2p/net_node.inl')
-rw-r--r-- | src/p2p/net_node.inl | 214 |
1 files changed, 162 insertions, 52 deletions
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index aa16e93d5..2f47de0f0 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -106,6 +106,7 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_seed_node); command_line::add_arg(desc, arg_tx_proxy); command_line::add_arg(desc, arg_anonymous_inbound); + command_line::add_arg(desc, arg_ban_list); command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_sync); command_line::add_arg(desc, arg_no_igd); @@ -245,6 +246,10 @@ namespace nodetool zone.second.m_net_server.get_config_object().close(c); conns.clear(); + + peerlist_entry pe{}; + pe.adr = addr; + zone.second.m_peerlist.remove_from_peer_white(pe); } MCLOG_CYAN(el::Level::Info, "global", "Host " << addr.host_str() << " blocked."); @@ -386,7 +391,7 @@ namespace nodetool m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6); m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4); public_zone.m_notifier = cryptonote::levin::notify{ - public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true, pad_txs, m_payload_handler.get_core() + public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core() }; if (command_line::has_arg(vm, arg_p2p_add_peer)) @@ -435,12 +440,42 @@ namespace nodetool if (command_line::has_arg(vm, arg_p2p_seed_node)) { - boost::unique_lock<boost::shared_mutex> lock(m_seed_nodes_lock); + boost::unique_lock<boost::shared_mutex> lock(public_zone.m_seed_nodes_lock); - if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, m_seed_nodes)) + if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, public_zone.m_seed_nodes)) return false; } + if (!command_line::is_arg_defaulted(vm, arg_ban_list)) + { + const std::string ban_list = command_line::get_arg(vm, arg_ban_list); + + const boost::filesystem::path ban_list_path(ban_list); + boost::system::error_code ec; + if (!boost::filesystem::exists(ban_list_path, ec)) + { + throw std::runtime_error("Can't find ban list file " + ban_list + " - " + ec.message()); + } + + std::string banned_ips; + if (!epee::file_io_utils::load_file_to_string(ban_list_path.string(), banned_ips)) + { + throw std::runtime_error("Failed to read ban list file " + ban_list); + } + + std::istringstream iss(banned_ips); + for (std::string line; std::getline(iss, line); ) + { + const expect<epee::net_utils::network_address> parsed_addr = net::get_network_address(line, 0); + if (!parsed_addr) + { + MERROR("Invalid IP address: " << line << " - " << parsed_addr.error()); + continue; + } + block_host(*parsed_addr, std::numeric_limits<time_t>::max()); + } + } + if(command_line::has_arg(vm, arg_p2p_hide_my_port)) m_hide_my_port = true; @@ -499,7 +534,7 @@ namespace nodetool } zone.m_notifier = cryptonote::levin::notify{ - zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false, pad_txs, m_payload_handler.get_core() + zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core() }; } @@ -598,21 +633,21 @@ namespace nodetool //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes(cryptonote::network_type nettype) const + std::set<std::string> node_server<t_payload_net_handler>::get_ip_seed_nodes() const { std::set<std::string> full_addrs; - if (nettype == cryptonote::TESTNET) + if (m_nettype == cryptonote::TESTNET) { full_addrs.insert("212.83.175.67:28080"); full_addrs.insert("212.83.172.165:28080"); full_addrs.insert("192.110.160.146:28080"); } - else if (nettype == cryptonote::STAGENET) + else if (m_nettype == cryptonote::STAGENET) { full_addrs.insert("162.210.173.150:38080"); full_addrs.insert("192.110.160.146:38080"); } - else if (nettype == cryptonote::FAKECHAIN) + else if (m_nettype == cryptonote::FAKECHAIN) { } else @@ -630,7 +665,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes() + std::set<std::string> node_server<t_payload_net_handler>::get_dns_seed_nodes() { if (!m_exclusive_peers.empty() || m_offline) { @@ -638,11 +673,11 @@ namespace nodetool } if (m_nettype == cryptonote::TESTNET) { - return get_seed_nodes(cryptonote::TESTNET); + return get_ip_seed_nodes(); } if (m_nettype == cryptonote::STAGENET) { - return get_seed_nodes(cryptonote::STAGENET); + return get_ip_seed_nodes(); } std::set<std::string> full_addrs; @@ -730,7 +765,7 @@ namespace nodetool else MINFO("Not enough DNS seed nodes found, using fallback defaults too"); - for (const auto &peer: get_seed_nodes(cryptonote::MAINNET)) + for (const auto &peer: get_ip_seed_nodes()) full_addrs.insert(peer); m_fallback_seed_nodes_added.test_and_set(); } @@ -739,6 +774,23 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes(epee::net_utils::zone zone) + { + switch (zone) + { + case epee::net_utils::zone::public_: + return get_dns_seed_nodes(); + case epee::net_utils::zone::tor: + return {}; + case epee::net_utils::zone::i2p: + return {}; + default: + break; + } + throw std::logic_error{"Bad zone given to get_seed_nodes"}; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> typename node_server<t_payload_net_handler>::network_zone& node_server<t_payload_net_handler>::add_zone(const epee::net_utils::zone zone) { const auto zone_ = m_network_zones.lower_bound(zone); @@ -1432,17 +1484,46 @@ namespace nodetool const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); classB.insert(actual_ip & 0x0000ffff); } +#if BOOST_VERSION > 106600 + else if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) + { + const epee::net_utils::network_address na = cntxt.m_remote_address; + const boost::asio::ip::address_v6 &actual_ip = na.as<const epee::net_utils::ipv6_network_address>().ip(); + if (actual_ip.is_v4_mapped()) + { + boost::asio::ip::address_v4 v4ip = make_address_v4(boost::asio::ip::v4_mapped, actual_ip); + uint32_t actual_ipv4; + memcpy(&actual_ipv4, v4ip.to_bytes().data(), sizeof(actual_ipv4)); + classB.insert(actual_ipv4 & ntohl(0xffff0000)); + } + } +#endif return true; }); } + auto get_host_string = [](const epee::net_utils::network_address &address) { +#if BOOST_VERSION > 106600 + if (address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) + { + boost::asio::ip::address_v6 actual_ip = address.as<const epee::net_utils::ipv6_network_address>().ip(); + if (actual_ip.is_v4_mapped()) + { + boost::asio::ip::address_v4 v4ip = make_address_v4(boost::asio::ip::v4_mapped, actual_ip); + return epee::net_utils::ipv4_network_address(v4ip.to_uint(), 0).host_str(); + } + } +#endif + return address.host_str(); + }; + std::unordered_set<std::string> hosts_added; std::deque<size_t> filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); for (int step = 0; step < 2; ++step) { bool skip_duplicate_class_B = step == 0; size_t idx = 0, skipped = 0; - zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ + zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe, &hosts_added, &get_host_string](const peerlist_entry &pe){ if (filtered.size() >= limit) return false; bool skip = false; @@ -1452,6 +1533,29 @@ namespace nodetool uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); skip = classB.find(actual_ip & 0x0000ffff) != classB.end(); } +#if BOOST_VERSION > 106600 + else if (skip_duplicate_class_B && pe.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) + { + const epee::net_utils::network_address na = pe.adr; + const boost::asio::ip::address_v6 &actual_ip = na.as<const epee::net_utils::ipv6_network_address>().ip(); + if (actual_ip.is_v4_mapped()) + { + boost::asio::ip::address_v4 v4ip = make_address_v4(boost::asio::ip::v4_mapped, actual_ip); + uint32_t actual_ipv4; + memcpy(&actual_ipv4, v4ip.to_bytes().data(), sizeof(actual_ipv4)); + skip = classB.find(actual_ipv4 & ntohl(0xffff0000)) != classB.end(); + } + } +#endif + + // consider each host once, to avoid giving undue inflence to hosts running several nodes + if (!skip) + { + const auto i = hosts_added.find(get_host_string(pe.adr)); + if (i != hosts_added.end()) + skip = true; + } + if (skip) ++skipped; else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) @@ -1459,16 +1563,17 @@ namespace nodetool else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) filtered.push_front(idx); ++idx; + hosts_added.insert(get_host_string(pe.adr)); return true; }); if (skipped == 0 || !filtered.empty()) break; if (skipped) - MINFO("Skipping " << skipped << " possible peers as they share a class B with existing peers"); + MDEBUG("Skipping " << skipped << " possible peers as they share a class B with existing peers"); } if (filtered.empty()) { - MDEBUG("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe); + MINFO("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe); return false; } if (use_white_list) @@ -1541,56 +1646,59 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::connect_to_seed() + bool node_server<t_payload_net_handler>::connect_to_seed(epee::net_utils::zone zone) { - boost::upgrade_lock<boost::shared_mutex> seed_nodes_upgrade_lock(m_seed_nodes_lock); + network_zone& server = m_network_zones.at(zone); + boost::upgrade_lock<boost::shared_mutex> seed_nodes_upgrade_lock(server.m_seed_nodes_lock); - if (!m_seed_nodes_initialized) + if (!server.m_seed_nodes_initialized) { + const std::uint16_t default_port = cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT; boost::upgrade_to_unique_lock<boost::shared_mutex> seed_nodes_lock(seed_nodes_upgrade_lock); - m_seed_nodes_initialized = true; - for (const auto& full_addr : get_seed_nodes()) + server.m_seed_nodes_initialized = true; + for (const auto& full_addr : get_seed_nodes(zone)) { + // seeds should have hostname converted to IP already MDEBUG("Seed node: " << full_addr); - append_net_address(m_seed_nodes, full_addr, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); + server.m_seed_nodes.push_back(MONERO_UNWRAP(net::get_network_address(full_addr, default_port))); } - MDEBUG("Number of seed nodes: " << m_seed_nodes.size()); + MDEBUG("Number of seed nodes: " << server.m_seed_nodes.size()); } - if (m_seed_nodes.empty() || m_offline || !m_exclusive_peers.empty()) + if (server.m_seed_nodes.empty() || m_offline || !m_exclusive_peers.empty()) return true; size_t try_count = 0; bool is_connected_to_at_least_one_seed_node = false; - size_t current_index = crypto::rand_idx(m_seed_nodes.size()); - const net_server& server = m_network_zones.at(epee::net_utils::zone::public_).m_net_server; + size_t current_index = crypto::rand_idx(server.m_seed_nodes.size()); while(true) { - if(server.is_stop_signal_sent()) + if(server.m_net_server.is_stop_signal_sent()) return false; peerlist_entry pe_seed{}; - pe_seed.adr = m_seed_nodes[current_index]; + pe_seed.adr = server.m_seed_nodes[current_index]; if (is_peer_used(pe_seed)) is_connected_to_at_least_one_seed_node = true; - else if (try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) + else if (try_to_connect_and_handshake_with_new_peer(server.m_seed_nodes[current_index], true)) break; - if(++try_count > m_seed_nodes.size()) + if(++try_count > server.m_seed_nodes.size()) { - if (!m_fallback_seed_nodes_added.test_and_set()) + // only IP zone has fallback (to direct IP) seeds + if (zone == epee::net_utils::zone::public_ && !m_fallback_seed_nodes_added.test_and_set()) { MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); - current_index = m_seed_nodes.size() - 1; + current_index = server.m_seed_nodes.size() - 1; { boost::upgrade_to_unique_lock<boost::shared_mutex> seed_nodes_lock(seed_nodes_upgrade_lock); - for (const auto &peer: get_seed_nodes(m_nettype)) + for (const auto &peer: get_ip_seed_nodes()) { MDEBUG("Fallback seed node: " << peer); - append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); + append_net_address(server.m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } } - if (current_index == m_seed_nodes.size() - 1) + if (current_index == server.m_seed_nodes.size() - 1) { MWARNING("No fallback seeds, continuing without seeds"); break; @@ -1604,7 +1712,7 @@ namespace nodetool break; } } - if(++current_index >= m_seed_nodes.size()) + if(++current_index >= server.m_seed_nodes.size()) current_index = 0; } return true; @@ -1620,20 +1728,21 @@ namespace nodetool if (!m_exclusive_peers.empty()) return true; - // Only have seeds in the public zone right now. - - size_t start_conn_count = get_public_outgoing_connections_count(); - if(!get_public_white_peers_count() && !connect_to_seed()) + bool one_succeeded = false; + for(auto& zone : m_network_zones) { - return false; - } + size_t start_conn_count = get_outgoing_connections_count(zone.second); + if(!zone.second.m_peerlist.get_white_peers_count() && !connect_to_seed(zone.first)) + { + continue; + } - if (!connect_to_peerlist(m_priority_peers)) return false; + if (zone.first == zone_type::public_ && !connect_to_peerlist(m_priority_peers)) continue; - for(auto& zone : m_network_zones) - { size_t base_expected_white_connections = (zone.second.m_config.m_net_config.max_out_connection_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + // carefully avoid `continue` in nested loop + size_t conn_count = get_outgoing_connections_count(zone.second); while(conn_count < zone.second.m_config.m_net_config.max_out_connection_count) { @@ -1670,16 +1779,17 @@ namespace nodetool } conn_count = new_conn_count; } - } - if (start_conn_count == get_public_outgoing_connections_count() && start_conn_count < m_network_zones.at(zone_type::public_).m_config.m_net_config.max_out_connection_count) - { - MINFO("Failed to connect to any, trying seeds"); - if (!connect_to_seed()) - return false; + if (start_conn_count == get_outgoing_connections_count(zone.second) && start_conn_count < zone.second.m_config.m_net_config.max_out_connection_count) + { + MINFO("Failed to connect to any, trying seeds"); + if (!connect_to_seed(zone.first)) + continue; + } + one_succeeded = true; } - return true; + return one_succeeded; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -2095,7 +2205,7 @@ namespace nodetool const epee::net_utils::network_address na = context.m_remote_address; std::string ip; - uint32_t ipv4_addr; + uint32_t ipv4_addr = 0; boost::asio::ip::address_v6 ipv6_addr; bool is_ipv4; if (na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) |