diff --git a/p2p/src/connection/outgoing/mod.rs b/p2p/src/connection/outgoing/mod.rs index 6bed5c356e..e9b6bdf6ea 100644 --- a/p2p/src/connection/outgoing/mod.rs +++ b/p2p/src/connection/outgoing/mod.rs @@ -6,11 +6,13 @@ pub use p2p_connection_outgoing_actions::*; mod p2p_connection_outgoing_reducer; +use std::net::IpAddr; #[cfg(feature = "p2p-libp2p")] use std::net::SocketAddr; use std::{fmt, str::FromStr}; use binprot_derive::{BinProtRead, BinProtWrite}; +use multiaddr::{Multiaddr, Protocol}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -44,6 +46,42 @@ pub struct P2pConnectionOutgoingInitLibp2pOpts { pub port: u16, } +impl P2pConnectionOutgoingInitLibp2pOpts { + /// If the current host is local and there is a better host among the `addrs`, + /// replace the current one with the better one. + pub fn update_host_if_needed<'a>(&mut self, mut addrs: impl Iterator) { + fn is_local(ip: impl Into) -> bool { + match ip.into() { + IpAddr::V4(ip) => ip.is_loopback() || ip.is_private(), + IpAddr::V6(ip) => ip.is_loopback(), + } + } + + // if current dial opts is not good enough + let update = match &self.host { + Host::Domain(_) => false, + Host::Ipv4(ip) => is_local(*ip), + Host::Ipv6(ip) => is_local(*ip), + }; + if update { + // if new options is better + let new = addrs.find_map(|x| { + x.iter().find_map(|x| match x { + Protocol::Dns4(hostname) | Protocol::Dns6(hostname) => { + Some(Host::Domain(hostname.into_owned())) + } + Protocol::Ip4(ip) if !is_local(ip) => Some(Host::Ipv4(ip)), + Protocol::Ip6(ip) if !is_local(ip) => Some(Host::Ipv6(ip)), + _ => None, + }) + }); + if let Some(new) = new { + self.host = new; + } + } + } +} + pub(crate) mod libp2p_opts { use std::net::{IpAddr, SocketAddr}; diff --git a/p2p/src/identify/p2p_identify_reducer.rs b/p2p/src/identify/p2p_identify_reducer.rs index d0a5c1f0d4..0b887f2268 100644 --- a/p2p/src/identify/p2p_identify_reducer.rs +++ b/p2p/src/identify/p2p_identify_reducer.rs @@ -2,6 +2,7 @@ use openmina_core::{bug_condition, Substate}; use redux::ActionWithMeta; use crate::{ + connection::outgoing::P2pConnectionOutgoingInitOpts, disconnection::{P2pDisconnectionAction, P2pDisconnectionReason}, token::{BroadcastAlgorithm, DiscoveryAlgorithm, IdentifyAlgorithm, RpcAlgorithm, StreamKind}, P2pNetworkConnectionMuxState, P2pNetworkKadRequestAction, P2pNetworkKadState, @@ -60,6 +61,9 @@ impl P2pState { let info = *info; if let Some(peer) = p2p_state.peers.get_mut(&peer_id) { peer.identify = Some(info.clone()); + if let Some(P2pConnectionOutgoingInitOpts::LibP2P(opts)) = &mut peer.dial_opts { + opts.update_host_if_needed(info.listen_addrs.iter()); + } } else { bug_condition!( "Peer state not found for `P2pIdentifyAction::UpdatePeerInformation`"