diff options
Diffstat (limited to 'src/p2p/net_node.inl')
-rw-r--r-- | src/p2p/net_node.inl | 442 |
1 files changed, 374 insertions, 68 deletions
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index be97edbe5..426767365 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -93,7 +93,11 @@ namespace nodetool void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_p2p_bind_ip); + command_line::add_arg(desc, arg_p2p_bind_ipv6_address); command_line::add_arg(desc, arg_p2p_bind_port, false); + command_line::add_arg(desc, arg_p2p_bind_port_ipv6, false); + command_line::add_arg(desc, arg_p2p_use_ipv6); + command_line::add_arg(desc, arg_p2p_require_ipv4); command_line::add_arg(desc, arg_p2p_external_port); command_line::add_arg(desc, arg_p2p_allow_local_ip); command_line::add_arg(desc, arg_p2p_add_peer); @@ -105,13 +109,13 @@ namespace nodetool 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); + command_line::add_arg(desc, arg_igd); command_line::add_arg(desc, arg_out_peers); command_line::add_arg(desc, arg_in_peers); command_line::add_arg(desc, arg_tos_flag); command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); command_line::add_arg(desc, arg_limit_rate); - command_line::add_arg(desc, arg_save_graph); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -155,19 +159,55 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address) + bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - auto it = m_blocked_hosts.find(address.host_str()); - if(it == m_blocked_hosts.end()) - return true; - if(time(nullptr) >= it->second) + + const time_t now = time(nullptr); + + // look in the hosts list + auto it = m_blocked_hosts.find(address); + if (it != m_blocked_hosts.end()) { - m_blocked_hosts.erase(it); - MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); - return true; + if (now >= it->second) + { + m_blocked_hosts.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); + it = m_blocked_hosts.end(); + } + else + { + if (t) + *t = it->second - now; + return false; + } } - return false; + + // manually loop in subnets + if (address.get_type_id() == epee::net_utils::address_type::ipv4) + { + auto ipv4_address = address.template as<epee::net_utils::ipv4_network_address>(); + std::map<epee::net_utils::ipv4_network_subnet, time_t>::iterator it; + for (it = m_blocked_subnets.begin(); it != m_blocked_subnets.end(); ) + { + if (now >= it->second) + { + it = m_blocked_subnets.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked."); + continue; + } + if (it->first.matches(ipv4_address)) + { + if (t) + *t = it->second - now; + return false; + } + ++it; + } + } + + // not found in hosts or subnets, allowed + return true; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -184,7 +224,7 @@ namespace nodetool limit = std::numeric_limits<time_t>::max(); else limit = now + seconds; - m_blocked_hosts[addr.host_str()] = limit; + m_blocked_hosts[addr] = limit; // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is @@ -214,7 +254,7 @@ namespace nodetool bool node_server<t_payload_net_handler>::unblock_host(const epee::net_utils::network_address &address) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - auto i = m_blocked_hosts.find(address.host_str()); + auto i = m_blocked_hosts.find(address); if (i == m_blocked_hosts.end()) return false; m_blocked_hosts.erase(i); @@ -223,6 +263,58 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds) + { + const time_t now = time(nullptr); + + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + time_t limit; + if (now > std::numeric_limits<time_t>::max() - seconds) + limit = std::numeric_limits<time_t>::max(); + else + limit = now + seconds; + m_blocked_subnets[subnet] = limit; + + // drop any connection to that subnet. This should only have to look into + // the zone related to the connection, but really make sure everything is + // swept ... + std::vector<boost::uuids::uuid> conns; + for(auto& zone : m_network_zones) + { + zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if (cntxt.m_remote_address.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id()) + return true; + auto ipv4_address = cntxt.m_remote_address.template as<epee::net_utils::ipv4_network_address>(); + if (subnet.matches(ipv4_address)) + { + conns.push_back(cntxt.m_connection_id); + } + return true; + }); + for (const auto &c: conns) + zone.second.m_net_server.get_config_object().close(c); + + conns.clear(); + } + + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " blocked."); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet) + { + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + auto i = m_blocked_subnets.find(subnet); + if (i == m_blocked_subnets.end()) + return false; + m_blocked_subnets.erase(i); + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " unblocked."); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address) { if(!address.is_blockable()) @@ -253,12 +345,44 @@ namespace nodetool network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_]; public_zone.m_connect = &public_connect; public_zone.m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); + public_zone.m_bind_ipv6_address = command_line::get_arg(vm, arg_p2p_bind_ipv6_address); public_zone.m_port = command_line::get_arg(vm, arg_p2p_bind_port); + public_zone.m_port_ipv6 = command_line::get_arg(vm, arg_p2p_bind_port_ipv6); public_zone.m_can_pingback = true; m_external_port = command_line::get_arg(vm, arg_p2p_external_port); m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); - m_no_igd = command_line::get_arg(vm, arg_no_igd); + const bool has_no_igd = command_line::get_arg(vm, arg_no_igd); + const std::string sigd = command_line::get_arg(vm, arg_igd); + if (sigd == "enabled") + { + if (has_no_igd) + { + MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " enabled"); + return false; + } + m_igd = igd; + } + else if (sigd == "disabled") + { + m_igd = no_igd; + } + else if (sigd == "delayed") + { + if (has_no_igd && !command_line::is_arg_defaulted(vm, arg_igd)) + { + MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " delayed"); + return false; + } + m_igd = has_no_igd ? no_igd : delayed_igd; + } + else + { + MFATAL("Invalid value for --" << arg_igd.name << ", expected enabled, disabled or delayed"); + return false; + } m_offline = command_line::get_arg(vm, cryptonote::arg_offline); + m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6); + m_require_ipv4 = command_line::get_arg(vm, arg_p2p_require_ipv4); if (command_line::has_arg(vm, arg_p2p_add_peer)) { @@ -292,11 +416,6 @@ namespace nodetool } } - if(command_line::has_arg(vm, arg_save_graph)) - { - set_save_graph(true); - } - if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) @@ -407,12 +526,17 @@ namespace nodetool std::string host = addr; std::string port = std::to_string(default_port); - size_t pos = addr.find_last_of(':'); - if (std::string::npos != pos) + size_t colon_pos = addr.find_last_of(':'); + size_t dot_pos = addr.find_last_of('.'); + size_t square_brace_pos = addr.find('['); + + // IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [ + // as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port" + // One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case + // the square braces will be stripped here. + if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos) { - CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\''); - host = addr.substr(0, pos); - port = addr.substr(pos + 1); + net::get_network_address_host_and_port(addr, host, port); } MINFO("Resolving node address: host=" << host << ", port=" << port); @@ -435,7 +559,9 @@ namespace nodetool } else { - MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); + epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}}; + seed_nodes.push_back(na); + MINFO("Added node: " << na.str()); } } return true; @@ -669,21 +795,40 @@ namespace nodetool if (!zone.second.m_bind_ip.empty()) { + std::string ipv6_addr = ""; + std::string ipv6_port = ""; zone.second.m_net_server.set_connection_filter(this); - MINFO("Binding on " << zone.second.m_bind_ip << ":" << zone.second.m_port); - res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" << zone.second.m_port); + if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6) + { + ipv6_addr = zone.second.m_bind_ipv6_address; + ipv6_port = zone.second.m_port_ipv6; + MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6); + } + res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, epee::net_utils::ssl_support_t::e_ssl_support_disabled); CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); } } m_listening_port = public_zone.m_net_server.get_binded_port(); - MLOG_GREEN(el::Level::Info, "Net service bound to " << public_zone.m_bind_ip << ":" << m_listening_port); + MLOG_GREEN(el::Level::Info, "Net service bound (IPv4) to " << public_zone.m_bind_ip << ":" << m_listening_port); + if (m_use_ipv6) + { + m_listening_port_ipv6 = public_zone.m_net_server.get_binded_port_ipv6(); + MLOG_GREEN(el::Level::Info, "Net service bound (IPv6) to " << public_zone.m_bind_ipv6_address << ":" << m_listening_port_ipv6); + } if(m_external_port) MDEBUG("External port defined as " << m_external_port); // add UPnP port mapping - if(!m_no_igd) - add_upnp_port_mapping(m_listening_port); + if(m_igd == igd) + { + add_upnp_port_mapping_v4(m_listening_port); + if (m_use_ipv6) + { + add_upnp_port_mapping_v6(m_listening_port_ipv6); + } + } return res; } @@ -776,7 +921,7 @@ namespace nodetool for(auto& zone : m_network_zones) zone.second.m_net_server.deinit_server(); // remove UPnP port mapping - if(!m_no_igd) + if(m_igd == igd) delete_upnp_port_mapping(m_listening_port); } return store_config(); @@ -944,7 +1089,10 @@ namespace nodetool } if(!context.m_is_income) m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); - m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); + if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false)) + { + m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); + } }); if(!r) @@ -1090,6 +1238,7 @@ namespace nodetool LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str() /*<< ", try " << try_count*/); + zone.m_net_server.get_config_object().close(con->m_connection_id); return false; } @@ -1149,7 +1298,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str()); - + zone.m_net_server.get_config_object().close(con->m_connection_id); return false; } @@ -1226,19 +1375,53 @@ namespace nodetool size_t random_index; const uint32_t next_needed_pruning_stripe = m_payload_handler.get_next_needed_pruning_stripe().second; + // build a set of all the /16 we're connected to, and prefer a peer that's not in that set + std::set<uint32_t> classB; + if (&zone == &m_network_zones.at(epee::net_utils::zone::public_)) // at returns reference, not copy + { + zone.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) + { + + const epee::net_utils::network_address na = cntxt.m_remote_address; + const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); + classB.insert(actual_ip & 0x0000ffff); + } + return true; + }); + } + std::deque<size_t> filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); - size_t idx = 0; - zone.m_peerlist.foreach (use_white_list, [&filtered, &idx, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ - if (filtered.size() >= limit) - return false; - if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) - filtered.push_back(idx); - else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) - filtered.push_front(idx); - ++idx; - return true; - }); + size_t idx = 0, skipped = 0; + for (int step = 0; step < 2; ++step) + { + bool skip_duplicate_class_B = step == 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){ + if (filtered.size() >= limit) + return false; + bool skip = false; + if (skip_duplicate_class_B && pe.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) + { + const epee::net_utils::network_address na = pe.adr; + uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); + skip = classB.find(actual_ip & 0x0000ffff) != classB.end(); + } + if (skip) + ++skipped; + else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) + filtered.push_back(idx); + else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) + filtered.push_front(idx); + ++idx; + return true; + }); + if (skipped == 0 || !filtered.empty()) + break; + if (skipped) + MGINFO("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); @@ -1581,8 +1764,17 @@ namespace nodetool } else { - const el::Level level = el::Level::Warning; - MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + if (m_igd == delayed_igd) + { + MWARNING("No incoming connections, trying to setup IGD"); + add_upnp_port_mapping(m_listening_port); + m_igd = igd; + } + else + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + } } } return true; @@ -1839,19 +2031,43 @@ namespace nodetool if(!node_data.my_port) return false; - CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), false, - "Only IPv4 addresses are supported here"); + bool address_ok = (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()); + CHECK_AND_ASSERT_MES(address_ok, false, + "Only IPv4 or IPv6 addresses are supported here"); const epee::net_utils::network_address na = context.m_remote_address; - uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); + std::string ip; + uint32_t ipv4_addr; + boost::asio::ip::address_v6 ipv6_addr; + bool is_ipv4; + if (na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) + { + ipv4_addr = na.as<const epee::net_utils::ipv4_network_address>().ip(); + ip = epee::string_tools::get_ip_string_from_int32(ipv4_addr); + is_ipv4 = true; + } + else + { + ipv6_addr = na.as<const epee::net_utils::ipv6_network_address>().ip(); + ip = ipv6_addr.to_string(); + is_ipv4 = false; + } network_zone& zone = m_network_zones.at(na.get_zone()); if(!zone.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{epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)}; + + epee::net_utils::network_address address; + if (is_ipv4) + { + address = epee::net_utils::network_address{epee::net_utils::ipv4_network_address(ipv4_addr, node_data.my_port)}; + } + else + { + address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)}; + } peerid_type pr = node_data.peer_id; bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this]( const typename net_server::t_connection_context& ping_context, @@ -1955,7 +2171,7 @@ namespace nodetool const epee::net_utils::zone zone_type = context.m_remote_address.get_zone(); network_zone& zone = m_network_zones.at(zone_type); - zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new); + zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true); m_payload_handler.get_payload_sync_data(rsp.payload_data); /* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon) @@ -2035,12 +2251,19 @@ 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.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), void(), - "Only IPv4 addresses are supported here"); + CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(), + "Only IPv4 or IPv6 addresses are supported here"); //called only(!) if success pinged, update local peerlist peerlist_entry pe; const epee::net_utils::network_address na = context.m_remote_address; - pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l); + if (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) + { + pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l); + } + else + { + pe.adr = epee::net_utils::ipv6_network_address(na.as<epee::net_utils::ipv6_network_address>().ip(), port_l); + } time_t last_seen; time(&last_seen); pe.last_seen = static_cast<int64_t>(last_seen); @@ -2058,7 +2281,7 @@ namespace nodetool }); //fill response - zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new); + zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true); get_local_node_data(rsp.node_data, zone); m_payload_handler.get_payload_sync_data(rsp.payload_data); LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE"); @@ -2217,6 +2440,15 @@ namespace nodetool } template<class t_payload_net_handler> + uint32_t node_server<t_payload_net_handler>::get_max_out_public_peers() const + { + const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); + if (public_zone == m_network_zones.end()) + return 0; + return public_zone->second.m_config.m_net_config.max_out_connection_count; + } + + template<class t_payload_net_handler> void node_server<t_payload_net_handler>::change_max_in_public_peers(size_t count) { auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); @@ -2230,6 +2462,15 @@ namespace nodetool } template<class t_payload_net_handler> + uint32_t node_server<t_payload_net_handler>::get_max_in_public_peers() const + { + const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); + if (public_zone == m_network_zones.end()) + return 0; + return public_zone->second.m_config.m_net_config.max_in_connection_count; + } + + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag) { if(flag==-1){ @@ -2389,16 +2630,19 @@ namespace nodetool } template<class t_payload_net_handler> - void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port) + void node_server<t_payload_net_handler>::add_upnp_port_mapping_impl(uint32_t port, bool ipv6) // if ipv6 false, do ipv4 { - MDEBUG("Attempting to add IGD port mapping."); + std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)"; + MDEBUG("Attempting to add IGD port mapping " << ipversion << "."); int result; + const int ipv6_arg = ipv6 ? 1 : 0; + #if MINIUPNPC_API_VERSION > 13 // default according to miniupnpc.h unsigned char ttl = 2; - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result); + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result); #else - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result); #endif UPNPUrls urls; IGDdatas igdData; @@ -2435,16 +2679,38 @@ namespace nodetool } template<class t_payload_net_handler> - void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port) + void node_server<t_payload_net_handler>::add_upnp_port_mapping_v4(uint32_t port) { - MDEBUG("Attempting to delete IGD port mapping."); + add_upnp_port_mapping_impl(port, false); + } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::add_upnp_port_mapping_v6(uint32_t port) + { + add_upnp_port_mapping_impl(port, true); + } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port, bool ipv4, bool ipv6) + { + if (ipv4) add_upnp_port_mapping_v4(port); + if (ipv6) add_upnp_port_mapping_v6(port); + } + + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_upnp_port_mapping_impl(uint32_t port, bool ipv6) + { + std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)"; + MDEBUG("Attempting to delete IGD port mapping " << ipversion << "."); int result; + const int ipv6_arg = ipv6 ? 1 : 0; #if MINIUPNPC_API_VERSION > 13 // default according to miniupnpc.h unsigned char ttl = 2; - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result); + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result); #else - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result); #endif UPNPUrls urls; IGDdatas igdData; @@ -2477,6 +2743,25 @@ namespace nodetool } } + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v4(uint32_t port) + { + delete_upnp_port_mapping_impl(port, false); + } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v6(uint32_t port) + { + delete_upnp_port_mapping_impl(port, true); + } + + template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port) + { + delete_upnp_port_mapping_v4(port); + delete_upnp_port_mapping_v6(port); + } + template<typename t_payload_net_handler> boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>> node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support) @@ -2495,13 +2780,34 @@ namespace nodetool boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>> node_server<t_payload_net_handler>::public_connect(network_zone& zone, epee::net_utils::network_address const& na, epee::net_utils::ssl_support_t ssl_support) { - CHECK_AND_ASSERT_MES(na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), boost::none, - "Only IPv4 addresses are supported here"); - const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>(); + bool is_ipv4 = na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(); + bool is_ipv6 = na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id(); + CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none, + "Only IPv4 or IPv6 addresses are supported here"); + + std::string address; + std::string port; + + if (is_ipv4) + { + const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>(); + address = epee::string_tools::get_ip_string_from_int32(ipv4.ip()); + port = epee::string_tools::num_to_string_fast(ipv4.port()); + } + else if (is_ipv6) + { + const epee::net_utils::ipv6_network_address &ipv6 = na.as<const epee::net_utils::ipv6_network_address>(); + address = ipv6.ip().to_string(); + port = epee::string_tools::num_to_string_fast(ipv6.port()); + } + else + { + LOG_ERROR("Only IPv4 or IPv6 addresses are supported here"); + return boost::none; + } typename net_server::t_connection_context con{}; - const bool res = zone.m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()), - epee::string_tools::num_to_string_fast(ipv4.port()), + const bool res = zone.m_net_server.connect(address, port, zone.m_config.m_net_config.connection_timeout, con, "0.0.0.0", ssl_support); |