From bbaa871971b34b43be04c550d439fda983439f9a Mon Sep 17 00:00:00 2001 From: darkfi Date: Sat, 8 Feb 2025 11:04:06 +0100 Subject: [PATCH] net: use the version exchange to auto-discover our own address when connecting to outbound (protocol_address) or seed (protocol_seed), AND our inbound is set to tcp or tcp+tls with host = [::] (ipv6). --- example/dchat/dchatd/example/node1.toml | 6 +-- example/dchat/dchatd/example/node2.toml | 7 ++- example/dchat/dchatd/example/seed.toml | 5 +- src/net/channel.rs | 21 +++++---- src/net/protocol/protocol_address.rs | 62 ++++++++++++++++++++++--- src/net/protocol/protocol_seed.rs | 17 +++++-- 6 files changed, 90 insertions(+), 28 deletions(-) diff --git a/example/dchat/dchatd/example/node1.toml b/example/dchat/dchatd/example/node1.toml index f0b42fd68..6860449ac 100644 --- a/example/dchat/dchatd/example/node1.toml +++ b/example/dchat/dchatd/example/node1.toml @@ -3,10 +3,10 @@ verbose = 2 [net] hostlist = "example/node1.tsv" -inbound = ["tcp://127.0.0.1:10202"] -external_addrs = ["tcp://127.0.0.1:10202"] +inbound = ["tcp://[::]:10202"] allowed_transports = ["tcp"] -seeds = ["tcp://127.0.0.1:10200"] +external_addrs = ["tcp://[::1]:10202"] +seeds = ["tcp://[::1]:10200"] outbound_connections = 5 magic_bytes = [127, 64, 12, 201] localnet = true diff --git a/example/dchat/dchatd/example/node2.toml b/example/dchat/dchatd/example/node2.toml index bddd6e65f..0ba02cd88 100644 --- a/example/dchat/dchatd/example/node2.toml +++ b/example/dchat/dchatd/example/node2.toml @@ -3,11 +3,10 @@ verbose = 2 [net] hostlist = "example/node2.tsv" -inbound = ["tcp://127.0.0.1:10201"] -#auto_advertise = true -external_addrs=["tcp://127.0.0.1:10201"] +inbound = ["tcp://[::]:10201"] allowed_transports = ["tcp"] -seeds = ["tcp://127.0.0.1:10200"] +external_addrs=["tcp://[::1]:10201"] +seeds = ["tcp://[::1]:10200"] outbound_connections = 5 magic_bytes = [127, 64, 12, 201] localnet = true diff --git a/example/dchat/dchatd/example/seed.toml b/example/dchat/dchatd/example/seed.toml index 0569aa9ef..e070006b9 100644 --- a/example/dchat/dchatd/example/seed.toml +++ b/example/dchat/dchatd/example/seed.toml @@ -3,11 +3,10 @@ verbose = 2 [net] hostlist = "example/seed.tsv" -inbound=["tcp://127.0.0.1:10200"] -external_addrs=["tcp://127.0.0.1:10200"] +inbound=["tcp://[::]:10200"] allowed_transports = ["tcp"] seeds=[] -outbound_connections = 5 +# no outbounds for seed node magic_bytes=[127, 64, 12, 201] localnet = true diff --git a/src/net/channel.rs b/src/net/channel.rs index 44e0c21ad..84d3b16a0 100644 --- a/src/net/channel.rs +++ b/src/net/channel.rs @@ -32,7 +32,7 @@ use log::{debug, error, info, trace, warn}; use rand::{rngs::OsRng, Rng}; use smol::{ io::{self, AsyncRead, AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf}, - lock::Mutex, + lock::{Mutex as AsyncMutex, OnceCell}, Executor, }; use url::Url; @@ -77,9 +77,9 @@ impl ChannelInfo { /// Async channel for communication between nodes. pub struct Channel { /// The reading half of the transport stream - reader: Mutex>>, + reader: AsyncMutex>>, /// The writing half of the transport stream - writer: Mutex>>, + writer: AsyncMutex>>, /// The message subsystem instance for this channel message_subsystem: MessageSubsystem, /// Publisher listening for stop signal for closing this channel @@ -93,7 +93,7 @@ pub struct Channel { /// The version message of the node we are connected to. /// Some if the version exchange has already occurred, None /// otherwise. - pub version: Mutex>>, + pub version: OnceCell>, /// Channel debug info pub info: ChannelInfo, } @@ -109,13 +109,12 @@ impl Channel { session: SessionWeakPtr, ) -> Arc { let (reader, writer) = io::split(stream); - let reader = Mutex::new(reader); - let writer = Mutex::new(writer); + let reader = AsyncMutex::new(reader); + let writer = AsyncMutex::new(writer); let message_subsystem = MessageSubsystem::new(); Self::setup_dispatchers(&message_subsystem).await; - let version = Mutex::new(None); let start_time = UNIX_EPOCH.elapsed().unwrap().as_secs(); let info = ChannelInfo::new(resolve_addr, connect_addr.clone(), start_time); @@ -127,7 +126,7 @@ impl Channel { receive_task: StoppableTask::new(), stopped: AtomicBool::new(false), session, - version, + version: OnceCell::new(), info, }) } @@ -487,7 +486,11 @@ impl Channel { /// Set the VersionMessage of the node this channel is connected /// to. Called on receiving a version message in `ProtocolVersion`. pub(crate) async fn set_version(&self, version: Arc) { - *self.version.lock().await = Some(version); + self.version.set(version).await.unwrap(); + } + /// Should only be called after the version exchange has been completed. + pub fn get_version(&self) -> Arc { + self.version.get().unwrap().clone() } /// Returns the inner [`MessageSubsystem`] reference diff --git a/src/net/protocol/protocol_address.rs b/src/net/protocol/protocol_address.rs index 92321d1a1..62bf672db 100644 --- a/src/net/protocol/protocol_address.rs +++ b/src/net/protocol/protocol_address.rs @@ -16,15 +16,15 @@ * along with this program. If not, see . */ -use std::{sync::Arc, time::UNIX_EPOCH}; - use async_trait::async_trait; use log::debug; use smol::{lock::RwLock as AsyncRwLock, Executor}; +use std::{sync::Arc, time::UNIX_EPOCH}; +use url::{Host, Url}; use super::{ super::{ - channel::ChannelPtr, + channel::{Channel, ChannelPtr}, hosts::{HostColor, HostsPtr}, message::{AddrsMessage, GetAddrsMessage}, message_publisher::MessageSubscription, @@ -218,8 +218,7 @@ impl ProtocolAddress { "[START] channel address={}", self.channel.address(), ); - let type_id = self.channel.session_type_id(); - if type_id != SESSION_OUTBOUND { + if self.channel.session_type_id() != SESSION_OUTBOUND { debug!( target: "net::protocol_address::send_my_addrs", "Not an outbound session. Stopping", @@ -227,7 +226,16 @@ impl ProtocolAddress { return Ok(()) } - let external_addrs = self.settings.read().await.external_addrs.clone(); + let settings = self.settings.read().await; + let mut external_addrs = settings.external_addrs.clone(); + + // Auto-advertise the node's inbound address using the address that + // was sent to use by the node in the version exchange. + for inbound in settings.inbound_addrs.clone() { + let Some(inbound) = Self::patch_inbound(&self.channel, inbound) else { continue }; + external_addrs.push(inbound); + } + drop(settings); if external_addrs.is_empty() { debug!( @@ -259,6 +267,48 @@ impl ProtocolAddress { Ok(()) } + + /// If the inbound is an Ipv6 address, then replace it with the ip address reported to + /// us by the version exchange. + /// + /// Also used by ProtocolSeed. + pub(super) fn patch_inbound(channel: &Channel, mut inbound: Url) -> Option { + if inbound.scheme() != "tcp" && inbound.scheme() != "tcp+tls" { + return None + } + + let inbound_host = inbound.host()?; + // Is it an Ipv6 listener? + match inbound_host { + Host::Ipv6(addr) => { + // We are only interested if it's localhost + if !addr.is_loopback() { + return None + } + } + _ => return None, + } + + // We should loop over the endpoints from the listeners + // But inbound session should be changed so the acceptors and listeners + // are accessible. + /* + let Some(mut port) = inbound.port() else { continue }; + if port == 0 { + } + */ + + // Get our auto-discovered IP + let version = channel.get_version(); + let discover_host = version.connect_recv_addr.host()?; + // Check the reported address is Ipv6 + let _ = match discover_host { + Host::Ipv6(_) => {} + _ => return None, + }; + inbound.set_host(version.connect_recv_addr.host_str()).ok()?; + Some(inbound) + } } #[async_trait] diff --git a/src/net/protocol/protocol_seed.rs b/src/net/protocol/protocol_seed.rs index c79fd70ef..8190f5629 100644 --- a/src/net/protocol/protocol_seed.rs +++ b/src/net/protocol/protocol_seed.rs @@ -16,11 +16,10 @@ * along with this program. If not, see . */ -use std::{sync::Arc, time::UNIX_EPOCH}; - use async_trait::async_trait; use log::debug; use smol::{lock::RwLock as AsyncRwLock, Executor}; +use std::{sync::Arc, time::UNIX_EPOCH}; use super::{ super::{ @@ -31,6 +30,7 @@ use super::{ p2p::P2pPtr, settings::Settings, }, + protocol_address::ProtocolAddress, protocol_base::{ProtocolBase, ProtocolBasePtr}, }; use crate::Result; @@ -63,7 +63,18 @@ impl ProtocolSeed { "[START] channel address={}", self.channel.address(), ); - let external_addrs = self.settings.read().await.external_addrs.clone(); + let settings = self.settings.read().await; + let mut external_addrs = settings.external_addrs.clone(); + + // Auto-advertise the node's inbound address using the address that + // was sent to use by the node in the version exchange. + for inbound in settings.inbound_addrs.clone() { + let Some(inbound) = ProtocolAddress::patch_inbound(&self.channel, inbound) else { + continue + }; + external_addrs.push(inbound); + } + drop(settings); if external_addrs.is_empty() { debug!(