From 326a2b9405fe387061336da52cbb880871f608d2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:56:02 +0200 Subject: [PATCH 001/179] Proposed API design --- core/src/transport.rs | 15 +++++++++++++-- swarm/src/dial_opts.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/core/src/transport.rs b/core/src/transport.rs index c98612971db..de14976ff99 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -48,7 +48,7 @@ pub mod upgrade; mod boxed; mod optional; -use crate::ConnectedPoint; +use crate::{ConnectedPoint, Endpoint}; pub use self::boxed::Boxed; pub use self::choice::OrTransport; @@ -57,6 +57,16 @@ pub use self::optional::OptionalTransport; pub use self::upgrade::Upgrade; static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1); +#[derive(Debug)] +pub enum PortMode { + New, + Reuse +} +#[derive(Debug)] +pub struct DialOpts { + pub endpoint: Endpoint, + pub port_mode: PortMode, +} /// A transport provides connection-oriented communication between two peers /// through ordered streams of data (i.e. connections). @@ -129,13 +139,14 @@ pub trait Transport { /// /// If [`TransportError::MultiaddrNotSupported`] is returned, it may be desirable to /// try an alternative [`Transport`], if available. - fn dial(&mut self, addr: Multiaddr) -> Result>; + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result>; /// As [`Transport::dial`] but has the local node act as a listener on the outgoing connection. /// /// This option is needed for NAT and firewall hole punching. /// /// See [`ConnectedPoint::Dialer`](crate::connection::ConnectedPoint::Dialer) for related option. + #[deprecated] fn dial_as_listener( &mut self, addr: Multiaddr, diff --git a/swarm/src/dial_opts.rs b/swarm/src/dial_opts.rs index 9be7280b3df..c11c3ac58bb 100644 --- a/swarm/src/dial_opts.rs +++ b/swarm/src/dial_opts.rs @@ -45,6 +45,7 @@ pub struct DialOpts { role_override: Endpoint, dial_concurrency_factor_override: Option, connection_id: ConnectionId, + port_mode: PortMode, } impl DialOpts { @@ -65,6 +66,7 @@ impl DialOpts { condition: Default::default(), role_override: Endpoint::Dialer, dial_concurrency_factor_override: Default::default(), + port_mode: Default::default(), } } @@ -144,6 +146,7 @@ pub struct WithPeerId { condition: PeerCondition, role_override: Endpoint, dial_concurrency_factor_override: Option, + port_mode: PortMode, } impl WithPeerId { @@ -169,6 +172,7 @@ impl WithPeerId { extend_addresses_through_behaviour: false, role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, + port_mode: self.port_mode, } } @@ -193,6 +197,7 @@ impl WithPeerId { role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, connection_id: ConnectionId::next(), + port_mode: self.port_mode, } } } @@ -205,6 +210,7 @@ pub struct WithPeerIdWithAddresses { extend_addresses_through_behaviour: bool, role_override: Endpoint, dial_concurrency_factor_override: Option, + port_mode: PortMode, } impl WithPeerIdWithAddresses { @@ -232,6 +238,11 @@ impl WithPeerIdWithAddresses { self } + pub fn override_port_mode(mut self, port_mode: PortMode) -> Self { + self.port_mode = port_mode; + self + } + /// Override /// Number of addresses concurrently dialed for a single outbound connection attempt. pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { @@ -249,6 +260,7 @@ impl WithPeerIdWithAddresses { role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, connection_id: ConnectionId::next(), + port_mode: self.port_mode, } } } @@ -262,6 +274,7 @@ impl WithoutPeerId { WithoutPeerIdWithAddress { address, role_override: Endpoint::Dialer, + port_mode: Default::default(), } } } @@ -270,6 +283,7 @@ impl WithoutPeerId { pub struct WithoutPeerIdWithAddress { address: Multiaddr, role_override: Endpoint, + port_mode: PortMode, } impl WithoutPeerIdWithAddress { @@ -283,6 +297,13 @@ impl WithoutPeerIdWithAddress { self.role_override = Endpoint::Listener; self } + + pub fn override_port_mode(mut self, port_mode: PortMode) -> Self { + self.port_mode = port_mode; + self + } + + /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { DialOpts { @@ -293,6 +314,7 @@ impl WithoutPeerIdWithAddress { role_override: self.role_override, dial_concurrency_factor_override: None, connection_id: ConnectionId::next(), + port_mode: self.port_mode, } } } @@ -323,3 +345,18 @@ pub enum PeerCondition { /// configured connection limits. Always, } + +/// The port mode to use. I.e. on which port an outgoing connection should dial. +/// Irrelevant when the transport doesn't support that option. +#[derive(Default, Debug, Clone)] +pub enum PortMode { + /// Leave the decision to the swarm: + /// - If we have 0 listen addresses, we allocate a new port by default. + /// - If we have at least one address, we reuse an existing port. + #[default] + Auto, + /// Always allocate a new port + New, + /// Always reuse an existing port + Reuse, +} From 25e6ccf366c9ba0f536f882251b283c2b6da4218 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Sep 2023 23:13:39 +0200 Subject: [PATCH 002/179] Implement proposed changes to the Transport trait. --- core/src/connection.rs | 3 +- core/src/either.rs | 7 +- core/src/transport.rs | 20 +++-- core/src/transport/and_then.rs | 5 +- core/src/transport/boxed.rs | 12 +-- core/src/transport/choice.rs | 8 +- core/src/transport/dummy.rs | 4 +- core/src/transport/global_only.rs | 7 +- core/src/transport/map.rs | 5 +- core/src/transport/map_err.rs | 6 +- core/src/transport/memory.rs | 11 +-- core/src/transport/optional.rs | 6 +- core/src/transport/timeout.rs | 5 +- core/src/transport/upgrade.rs | 9 +- swarm/src/dial_opts.rs | 5 +- swarm/src/lib.rs | 136 +++++++++++++++++------------- transports/tcp/src/lib.rs | 10 +-- 17 files changed, 141 insertions(+), 118 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 7a339b0661b..4c6418a6c9c 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -21,9 +21,10 @@ use crate::multiaddr::{Multiaddr, Protocol}; /// The endpoint roles associated with a peer-to-peer communication channel. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] pub enum Endpoint { /// The socket comes from a dialer. + #[default] Dialer, /// The socket comes from a listener. Listener, diff --git a/core/src/either.rs b/core/src/either.rs index 3f79b2b37a9..e14c2136849 100644 --- a/core/src/either.rs +++ b/core/src/either.rs @@ -28,6 +28,7 @@ use either::Either; use futures::prelude::*; use pin_project::pin_project; use std::{pin::Pin, task::Context, task::Poll}; +use crate::transport::DialOpts; impl StreamMuxer for future::Either where @@ -172,15 +173,15 @@ where } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { use TransportError::*; match self { - Either::Left(a) => match a.dial(addr) { + Either::Left(a) => match a.dial(addr, dial_opts) { Ok(connec) => Ok(EitherFuture::First(connec)), Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), Err(Other(err)) => Err(Other(Either::Left(err))), }, - Either::Right(b) => match b.dial(addr) { + Either::Right(b) => match b.dial(addr, dial_opts) { Ok(connec) => Ok(EitherFuture::Second(connec)), Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), Err(Other(err)) => Err(Other(Either::Right(err))), diff --git a/core/src/transport.rs b/core/src/transport.rs index de14976ff99..5b026bb9be3 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -57,12 +57,13 @@ pub use self::optional::OptionalTransport; pub use self::upgrade::Upgrade; static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1); -#[derive(Debug)] +#[derive(Debug, Default, Copy, Clone)] pub enum PortMode { + #[default] New, - Reuse + Reuse, } -#[derive(Debug)] +#[derive(Debug, Default, Copy, Clone)] pub struct DialOpts { pub endpoint: Endpoint, pub port_mode: PortMode, @@ -139,7 +140,11 @@ pub trait Transport { /// /// If [`TransportError::MultiaddrNotSupported`] is returned, it may be desirable to /// try an alternative [`Transport`], if available. - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result>; + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result>; /// As [`Transport::dial`] but has the local node act as a listener on the outgoing connection. /// @@ -150,7 +155,12 @@ pub trait Transport { fn dial_as_listener( &mut self, addr: Multiaddr, - ) -> Result>; + ) -> Result> { + self.dial(addr, DialOpts { + endpoint: Endpoint::Listener, + ..Default::default() + }) + } /// Poll for [`TransportEvent`]s. /// diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index 6e0c7e32067..f322d2c5da5 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -26,6 +26,7 @@ use either::Either; use futures::prelude::*; use multiaddr::Multiaddr; use std::{error, marker::PhantomPinned, pin::Pin, task::Context, task::Poll}; +use crate::transport::DialOpts; /// See the [`Transport::and_then`] method. #[pin_project::pin_project] @@ -68,10 +69,10 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { let dialed_fut = self .transport - .dial(addr.clone()) + .dial(addr.clone(), dial_opts) .map_err(|err| err.map(Either::Left))?; let future = AndThenFuture { inner: Either::Left(Box::pin(dialed_fut)), diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index 1cede676c8e..b2505ff3443 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; +use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}; use futures::{prelude::*, stream::FusedStream}; use multiaddr::Multiaddr; use std::{ @@ -58,7 +58,7 @@ trait Abstract { addr: Multiaddr, ) -> Result<(), TransportError>; fn remove_listener(&mut self, id: ListenerId) -> bool; - fn dial(&mut self, addr: Multiaddr) -> Result, TransportError>; + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result, TransportError>; fn dial_as_listener(&mut self, addr: Multiaddr) -> Result, TransportError>; fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option; fn poll( @@ -86,8 +86,8 @@ where Transport::remove_listener(self, id) } - fn dial(&mut self, addr: Multiaddr) -> Result, TransportError> { - let fut = Transport::dial(self, addr) + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result, TransportError> { + let fut = Transport::dial(self, addr, dial_opts) .map(|r| r.map_err(box_err)) .map_err(|e| e.map(box_err))?; Ok(Box::pin(fut) as Dial<_>) @@ -143,8 +143,8 @@ impl Transport for Boxed { self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.inner.dial(addr) + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + self.inner.dial(addr, dial_opts) } fn dial_as_listener( diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index 8d3bfdecb79..24774d37133 100644 --- a/core/src/transport/choice.rs +++ b/core/src/transport/choice.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use crate::either::EitherFuture; -use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; +use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}; use either::Either; use futures::future; use log::{debug, trace}; @@ -93,13 +93,13 @@ where self.0.remove_listener(id) || self.1.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { trace!( "Attempting to dial {} using {}", addr, std::any::type_name::() ); - let addr = match self.0.dial(addr) { + let addr = match self.0.dial(addr, dial_opts) { Ok(connec) => return Ok(EitherFuture::First(connec)), Err(TransportError::MultiaddrNotSupported(addr)) => { debug!( @@ -119,7 +119,7 @@ where addr, std::any::type_name::() ); - let addr = match self.1.dial(addr) { + let addr = match self.1.dial(addr, dial_opts) { Ok(connec) => return Ok(EitherFuture::Second(connec)), Err(TransportError::MultiaddrNotSupported(addr)) => { debug!( diff --git a/core/src/transport/dummy.rs b/core/src/transport/dummy.rs index 951d1039328..aa71d83fd48 100644 --- a/core/src/transport/dummy.rs +++ b/core/src/transport/dummy.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; +use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}; use crate::Multiaddr; use futures::{prelude::*, task::Context, task::Poll}; use std::{fmt, io, marker::PhantomData, pin::Pin}; @@ -71,7 +71,7 @@ impl Transport for DummyTransport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, _opts: DialOpts) -> Result> { Err(TransportError::MultiaddrNotSupported(addr)) } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 4f1fe8ab794..b04b8bbaf20 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -27,6 +27,7 @@ use std::{ pin::Pin, task::{Context, Poll}, }; +use crate::transport::DialOpts; /// Dropping all dial requests to non-global IP addresses. #[derive(Debug, Clone, Default)] @@ -288,21 +289,21 @@ impl crate::Transport for Transport { self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { if !ipv4_global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } - self.inner.dial(addr) + self.inner.dial(addr, dial_opts) } Some(Protocol::Ip6(a)) => { if !ipv6_global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } - self.inner.dial(addr) + self.inner.dial(addr, dial_opts) } _ => { debug!("Not dialing unsupported Multiaddress {:?}.", addr); diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index 553f3e6338d..f59650f1fd0 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -25,6 +25,7 @@ use crate::{ use futures::prelude::*; use multiaddr::Multiaddr; use std::{pin::Pin, task::Context, task::Poll}; +use crate::transport::DialOpts; use super::ListenerId; @@ -73,8 +74,8 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let future = self.transport.dial(addr.clone())?; + fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + let future = self.transport.dial(addr.clone(), dial_opts)?; let p = ConnectedPoint::Dialer { address: addr, role_override: Endpoint::Dialer, diff --git a/core/src/transport/map_err.rs b/core/src/transport/map_err.rs index 56e1ebf2929..3e1cab398c1 100644 --- a/core/src/transport/map_err.rs +++ b/core/src/transport/map_err.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; +use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}; use futures::prelude::*; use multiaddr::Multiaddr; use std::{error, pin::Pin, task::Context, task::Poll}; @@ -65,9 +65,9 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { let map = self.map.clone(); - match self.transport.dial(addr) { + match self.transport.dial(addr, opts) { Ok(future) => Ok(MapErrDial { inner: future, map: Some(map), diff --git a/core/src/transport/memory.rs b/core/src/transport/memory.rs index 4c30ee9b65d..9a553aa7175 100644 --- a/core/src/transport/memory.rs +++ b/core/src/transport/memory.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; +use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}; use fnv::FnvHashMap; use futures::{ channel::mpsc, @@ -219,7 +219,7 @@ impl Transport for MemoryTransport { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { let port = if let Ok(port) = parse_memory_addr(&addr) { if let Some(port) = NonZeroU64::new(port) { port @@ -233,13 +233,6 @@ impl Transport for MemoryTransport { DialFuture::new(port).ok_or(TransportError::Other(MemoryTransportError::Unreachable)) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { None } diff --git a/core/src/transport/optional.rs b/core/src/transport/optional.rs index 839f55a4000..5785047e1cd 100644 --- a/core/src/transport/optional.rs +++ b/core/src/transport/optional.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; +use crate::transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}; use multiaddr::Multiaddr; use std::{pin::Pin, task::Context, task::Poll}; @@ -80,9 +80,9 @@ where } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { if let Some(inner) = self.0.as_mut() { - inner.dial(addr) + inner.dial(addr, opts) } else { Err(TransportError::MultiaddrNotSupported(addr)) } diff --git a/core/src/transport/timeout.rs b/core/src/transport/timeout.rs index 0e8ab3f5201..8831fd3ee93 100644 --- a/core/src/transport/timeout.rs +++ b/core/src/transport/timeout.rs @@ -31,6 +31,7 @@ use crate::{ use futures::prelude::*; use futures_timer::Delay; use std::{error, fmt, io, pin::Pin, task::Context, task::Poll, time::Duration}; +use crate::transport::DialOpts; /// A `TransportTimeout` is a `Transport` that wraps another `Transport` and adds /// timeouts to all inbound and outbound connection attempts. @@ -99,10 +100,10 @@ where self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { let dial = self .inner - .dial(addr) + .dial(addr, opts) .map_err(|err| err.map(TransportTimeoutError::Other))?; Ok(Timeout { inner: dial, diff --git a/core/src/transport/upgrade.rs b/core/src/transport/upgrade.rs index 8525ab741ff..c90c5870f26 100644 --- a/core/src/transport/upgrade.rs +++ b/core/src/transport/upgrade.rs @@ -45,6 +45,7 @@ use std::{ task::{Context, Poll}, time::Duration, }; +use crate::transport::DialOpts; /// A `Builder` facilitates upgrading of a [`Transport`] for use with /// a `Swarm`. @@ -335,8 +336,8 @@ where type ListenerUpgrade = T::ListenerUpgrade; type Dial = T::Dial; - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.0.dial(addr) + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + self.0.dial(addr, opts) } fn remove_listener(&mut self, id: ListenerId) -> bool { @@ -404,10 +405,10 @@ where type ListenerUpgrade = ListenerUpgradeFuture; type Dial = DialUpgradeFuture; - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { let future = self .inner - .dial(addr) + .dial(addr, opts) .map_err(|err| err.map(TransportUpgradeError::Transport))?; Ok(DialUpgradeFuture { future: Box::pin(future), diff --git a/swarm/src/dial_opts.rs b/swarm/src/dial_opts.rs index c11c3ac58bb..2636de666fa 100644 --- a/swarm/src/dial_opts.rs +++ b/swarm/src/dial_opts.rs @@ -126,6 +126,8 @@ impl DialOpts { pub(crate) fn role_override(&self) -> Endpoint { self.role_override } + + pub(crate) fn port_mode(&self) -> PortMode { self.port_mode } } impl From for DialOpts { @@ -303,7 +305,6 @@ impl WithoutPeerIdWithAddress { self } - /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { DialOpts { @@ -348,7 +349,7 @@ pub enum PeerCondition { /// The port mode to use. I.e. on which port an outgoing connection should dial. /// Irrelevant when the transport doesn't support that option. -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, Copy)] pub enum PortMode { /// Leave the decision to the swarm: /// - If we have 0 listen addresses, we allocate a new port by default. diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index e9f08d4c14c..61b851d7e7a 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -68,7 +68,7 @@ pub mod dial_opts; pub mod dummy; pub mod handler; #[deprecated( - note = "Configure an appropriate idle connection timeout via `SwarmBuilder::idle_connection_timeout` instead. To keep connections alive 'forever', use `Duration::from_secs(u64::MAX)`." +note = "Configure an appropriate idle connection timeout via `SwarmBuilder::idle_connection_timeout` instead. To keep connections alive 'forever', use `Duration::from_secs(u64::MAX)`." )] pub mod keep_alive; mod listen_opts; @@ -142,8 +142,8 @@ use libp2p_core::{ connection::ConnectedPoint, multiaddr, muxing::StreamMuxerBox, - transport::{self, ListenerId, TransportError, TransportEvent}, - Endpoint, Multiaddr, Transport, + transport::{self, ListenerId, TransportError, TransportEvent, DialOpts as TransportDialOpts, PortMode as TransportPortMode}, + Multiaddr, Transport, }; use libp2p_identity::PeerId; use smallvec::SmallVec; @@ -156,6 +156,7 @@ use std::{ pin::Pin, task::{Context, Poll}, }; +use crate::dial_opts::PortMode; /// Substream for which a protocol has been chosen. /// @@ -324,8 +325,8 @@ impl SwarmEvent` in order to make /// progress. pub struct Swarm -where - TBehaviour: NetworkBehaviour, + where + TBehaviour: NetworkBehaviour, { /// [`Transport`] for dialing remote peers and listening for incoming connection. transport: transport::Boxed<(PeerId, StreamMuxerBox)>, @@ -357,8 +358,8 @@ where impl Unpin for Swarm where TBehaviour: NetworkBehaviour {} impl Swarm -where - TBehaviour: NetworkBehaviour, + where + TBehaviour: NetworkBehaviour, { /// Returns information about the connections underlying the [`Swarm`]. pub fn network_info(&self) -> NetworkInfo { @@ -496,14 +497,13 @@ where addresses_from_opts }; + let transport_dial_opts = self.convert_dial_opts(&dial_opts); + let dials = addresses .into_iter() .map(|a| match p2p_addr(peer_id, a) { Ok(address) => { - let dial = match dial_opts.role_override() { - Endpoint::Dialer => self.transport.dial(address.clone()), - Endpoint::Listener => self.transport.dial_as_listener(address.clone()), - }; + let dial = self.transport.dial(address.clone(), transport_dial_opts); match dial { Ok(fut) => fut .map(|r| (address, r.map_err(TransportError::Other))) @@ -515,7 +515,7 @@ where address.clone(), Err(TransportError::MultiaddrNotSupported(address)), )) - .boxed(), + .boxed(), }) .collect(); @@ -531,7 +531,7 @@ where } /// Returns an iterator that produces the list of addresses we're listening on. - pub fn listeners(&self) -> impl Iterator { + pub fn listeners(&self) -> impl Iterator { self.listened_addrs.values().flatten() } @@ -541,7 +541,7 @@ where } /// List all **confirmed** external address for the local node. - pub fn external_addresses(&self) -> impl Iterator { + pub fn external_addresses(&self) -> impl Iterator { self.confirmed_external_addr.iter() } @@ -634,7 +634,7 @@ where } /// Returns the currently connected peers. - pub fn connected_peers(&self) -> impl Iterator { + pub fn connected_peers(&self) -> impl Iterator { self.pool.iter_connected() } @@ -737,7 +737,7 @@ where let num_established = NonZeroU32::new( u32::try_from(other_established_connection_ids.len() + 1).unwrap(), ) - .expect("n + 1 is always non-zero; qed"); + .expect("n + 1 is always non-zero; qed"); self.pool .spawn_connection(id, peer_id, &endpoint, connection, handler); @@ -1217,6 +1217,25 @@ where return Poll::Pending; } } + + fn convert_port_mode(&self, port_mode: PortMode) -> TransportPortMode { + match port_mode { + PortMode::Auto => if self.listened_addrs.is_empty() { + TransportPortMode::New + } else { + TransportPortMode::Reuse + }, + PortMode::New => TransportPortMode::New, + PortMode::Reuse => TransportPortMode::Reuse, + } + } + + fn convert_dial_opts(&self, dial_opts: &DialOpts) -> TransportDialOpts { + TransportDialOpts { + endpoint: dial_opts.role_override(), + port_mode: self.convert_port_mode(dial_opts.port_mode()), + } + } } /// Connection to notify of a pending event. @@ -1270,12 +1289,12 @@ fn notify_any( event: THandlerInEvent, cx: &mut Context<'_>, ) -> Option<(THandlerInEvent, SmallVec<[ConnectionId; 10]>)> -where - TBehaviour: NetworkBehaviour, - THandler: ConnectionHandler< - FromBehaviour = THandlerInEvent, - ToBehaviour = THandlerOutEvent, - >, + where + TBehaviour: NetworkBehaviour, + THandler: ConnectionHandler< + FromBehaviour=THandlerInEvent, + ToBehaviour=THandlerOutEvent, + >, { let mut pending = SmallVec::new(); let mut event = Some(event); // (1) @@ -1313,8 +1332,8 @@ where /// Note: This stream is infinite and it is guaranteed that /// [`futures::Stream::poll_next`] will never return `Poll::Ready(None)`. impl futures::Stream for Swarm -where - TBehaviour: NetworkBehaviour, + where + TBehaviour: NetworkBehaviour, { type Item = SwarmEvent, THandlerErr>; @@ -1325,8 +1344,8 @@ where /// The stream of swarm events never terminates, so we can implement fused for it. impl FusedStream for Swarm -where - TBehaviour: NetworkBehaviour, + where + TBehaviour: NetworkBehaviour, { fn is_terminated(&self) -> bool { false @@ -1356,8 +1375,8 @@ pub struct SwarmBuilder { } impl SwarmBuilder -where - TBehaviour: NetworkBehaviour, + where + TBehaviour: NetworkBehaviour, { /// Creates a new [`SwarmBuilder`] from the given transport, behaviour, local peer ID and /// executor. The `Swarm` with its underlying `Network` is obtained via @@ -1402,8 +1421,8 @@ where /// Builds a new [`SwarmBuilder`] from the given transport, behaviour, local peer ID and a /// `tokio` executor. #[cfg(all( - feature = "tokio", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) + feature = "tokio", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) ))] pub fn with_tokio_executor( transport: transport::Boxed<(PeerId, StreamMuxerBox)>, @@ -1421,8 +1440,8 @@ where /// Builds a new [`SwarmBuilder`] from the given transport, behaviour, local peer ID and a /// `async-std` executor. #[cfg(all( - feature = "async-std", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) + feature = "async-std", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) ))] pub fn with_async_std_executor( transport: transport::Boxed<(PeerId, StreamMuxerBox)>, @@ -1736,8 +1755,8 @@ impl ConnectionDenied { /// Attempt to downcast to a particular reason for why the connection was denied. pub fn downcast(self) -> Result - where - E: error::Error + Send + Sync + 'static, + where + E: error::Error + Send + Sync + 'static, { let inner = self .inner @@ -1749,8 +1768,8 @@ impl ConnectionDenied { /// Attempt to downcast to a particular reason for why the connection was denied. pub fn downcast_ref(&self) -> Option<&E> - where - E: error::Error + Send + Sync + 'static, + where + E: error::Error + Send + Sync + 'static, { self.inner.downcast_ref::() } @@ -1842,8 +1861,7 @@ mod tests { Disconnecting, } - fn new_test_swarm( - ) -> SwarmBuilder>> { + fn new_test_swarm() -> SwarmBuilder>> { let id_keys = identity::Keypair::generate_ed25519(); let local_public_key = id_keys.public(); let transport = transport::MemoryTransport::default() @@ -1869,18 +1887,18 @@ mod tests { swarm2: &Swarm>, num_connections: usize, ) -> bool - where - TBehaviour: NetworkBehaviour, - THandlerOutEvent: Clone, + where + TBehaviour: NetworkBehaviour, + THandlerOutEvent: Clone, { swarm1 .behaviour() .num_connections_to_peer(*swarm2.local_peer_id()) == num_connections && swarm2 - .behaviour() - .num_connections_to_peer(*swarm1.local_peer_id()) - == num_connections + .behaviour() + .num_connections_to_peer(*swarm1.local_peer_id()) + == num_connections && swarm1.is_connected(swarm2.local_peer_id()) && swarm2.is_connected(swarm1.local_peer_id()) } @@ -1889,18 +1907,18 @@ mod tests { swarm1: &Swarm>, swarm2: &Swarm>, ) -> bool - where - TBehaviour: NetworkBehaviour, - THandlerOutEvent: Clone, + where + TBehaviour: NetworkBehaviour, + THandlerOutEvent: Clone, { swarm1 .behaviour() .num_connections_to_peer(*swarm2.local_peer_id()) == 0 && swarm2 - .behaviour() - .num_connections_to_peer(*swarm1.local_peer_id()) - == 0 + .behaviour() + .num_connections_to_peer(*swarm1.local_peer_id()) + == 0 && !swarm1.is_connected(swarm2.local_peer_id()) && !swarm2.is_connected(swarm1.local_peer_id()) } @@ -2213,8 +2231,8 @@ mod tests { match swarm2.poll_next_unpin(cx) { Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { - peer_id, error, .. - })) => Poll::Ready((peer_id, error)), + peer_id, error, .. + })) => Poll::Ready((peer_id, error)), Poll::Ready(x) => panic!("unexpected {x:?}"), Poll::Pending => Poll::Pending, } @@ -2269,10 +2287,10 @@ mod tests { loop { match swarm.poll_next_unpin(cx) { Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { - peer_id, - error: DialError::LocalPeerId { .. }, - .. - })) => { + peer_id, + error: DialError::LocalPeerId { .. }, + .. + })) => { assert_eq!(&peer_id.unwrap(), swarm.local_peer_id()); assert!(!got_dial_err); got_dial_err = true; @@ -2281,8 +2299,8 @@ mod tests { } } Poll::Ready(Some(SwarmEvent::IncomingConnectionError { - local_addr, .. - })) => { + local_addr, .. + })) => { assert!(!got_inc_err); assert_eq!(local_addr, local_address); got_inc_err = true; @@ -2300,7 +2318,7 @@ mod tests { } } })) - .unwrap(); + .unwrap(); } #[test] diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 5efdf16fff5..142a09219c3 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -59,6 +59,7 @@ use std::{ task::{Context, Poll, Waker}, time::Duration, }; +use libp2p_core::transport::DialOpts; /// The configuration for a TCP/IP transport capability for libp2p. #[derive(Clone, Debug)] @@ -463,7 +464,7 @@ where } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, _opts: DialOpts) -> Result> { let socket_addr = if let Ok(socket_addr) = multiaddr_to_socketaddr(addr.clone()) { if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { return Err(TransportError::MultiaddrNotSupported(addr)); @@ -503,13 +504,6 @@ where .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - /// When port reuse is disabled and hence ephemeral local ports are /// used for outgoing connections, the returned address is the /// `observed` address with the port replaced by the port of the From c9e33e1c2176fda251c8b757b25eca9a83b08ec5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:27:03 +0200 Subject: [PATCH 003/179] Reworked proposed API changes --- core/src/either.rs | 8 +- core/src/transport.rs | 15 ++- core/src/transport/and_then.rs | 8 +- core/src/transport/boxed.rs | 18 ++- core/src/transport/choice.rs | 6 +- core/src/transport/dummy.rs | 6 +- core/src/transport/global_only.rs | 8 +- core/src/transport/map.rs | 8 +- core/src/transport/map_err.rs | 6 +- core/src/transport/memory.rs | 6 +- core/src/transport/optional.rs | 6 +- core/src/transport/timeout.rs | 8 +- core/src/transport/upgrade.rs | 14 ++- swarm/src/dial_opts.rs | 120 +++++++++----------- swarm/src/lib.rs | 178 +++++++++++++++++------------- transports/tcp/src/lib.rs | 8 +- 16 files changed, 254 insertions(+), 169 deletions(-) diff --git a/core/src/either.rs b/core/src/either.rs index e14c2136849..d8fbe047099 100644 --- a/core/src/either.rs +++ b/core/src/either.rs @@ -19,6 +19,7 @@ // DEALINGS IN THE SOFTWARE. use crate::muxing::StreamMuxerEvent; +use crate::transport::DialOpts; use crate::{ muxing::StreamMuxer, transport::{ListenerId, Transport, TransportError, TransportEvent}, @@ -28,7 +29,6 @@ use either::Either; use futures::prelude::*; use pin_project::pin_project; use std::{pin::Pin, task::Context, task::Poll}; -use crate::transport::DialOpts; impl StreamMuxer for future::Either where @@ -173,7 +173,11 @@ where } } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { use TransportError::*; match self { Either::Left(a) => match a.dial(addr, dial_opts) { diff --git a/core/src/transport.rs b/core/src/transport.rs index 5b026bb9be3..581e52c820b 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -58,7 +58,7 @@ pub use self::upgrade::Upgrade; static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1); #[derive(Debug, Default, Copy, Clone)] -pub enum PortMode { +pub enum PortUse { #[default] New, Reuse, @@ -66,7 +66,7 @@ pub enum PortMode { #[derive(Debug, Default, Copy, Clone)] pub struct DialOpts { pub endpoint: Endpoint, - pub port_mode: PortMode, + pub port_use: PortUse, } /// A transport provides connection-oriented communication between two peers @@ -156,10 +156,13 @@ pub trait Transport { &mut self, addr: Multiaddr, ) -> Result> { - self.dial(addr, DialOpts { - endpoint: Endpoint::Listener, - ..Default::default() - }) + self.dial( + addr, + DialOpts { + endpoint: Endpoint::Listener, + ..Default::default() + }, + ) } /// Poll for [`TransportEvent`]s. diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index f322d2c5da5..bc5720e5af3 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -18,6 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use crate::transport::DialOpts; use crate::{ connection::{ConnectedPoint, Endpoint}, transport::{ListenerId, Transport, TransportError, TransportEvent}, @@ -26,7 +27,6 @@ use either::Either; use futures::prelude::*; use multiaddr::Multiaddr; use std::{error, marker::PhantomPinned, pin::Pin, task::Context, task::Poll}; -use crate::transport::DialOpts; /// See the [`Transport::and_then`] method. #[pin_project::pin_project] @@ -69,7 +69,11 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { let dialed_fut = self .transport .dial(addr.clone(), dial_opts) diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index b2505ff3443..eeadadbb466 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -58,7 +58,11 @@ trait Abstract { addr: Multiaddr, ) -> Result<(), TransportError>; fn remove_listener(&mut self, id: ListenerId) -> bool; - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result, TransportError>; + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result, TransportError>; fn dial_as_listener(&mut self, addr: Multiaddr) -> Result, TransportError>; fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option; fn poll( @@ -86,7 +90,11 @@ where Transport::remove_listener(self, id) } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result, TransportError> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result, TransportError> { let fut = Transport::dial(self, addr, dial_opts) .map(|r| r.map_err(box_err)) .map_err(|e| e.map(box_err))?; @@ -143,7 +151,11 @@ impl Transport for Boxed { self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { self.inner.dial(addr, dial_opts) } diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index 24774d37133..d6e143d5c90 100644 --- a/core/src/transport/choice.rs +++ b/core/src/transport/choice.rs @@ -93,7 +93,11 @@ where self.0.remove_listener(id) || self.1.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { trace!( "Attempting to dial {} using {}", addr, diff --git a/core/src/transport/dummy.rs b/core/src/transport/dummy.rs index aa71d83fd48..7079308943b 100644 --- a/core/src/transport/dummy.rs +++ b/core/src/transport/dummy.rs @@ -71,7 +71,11 @@ impl Transport for DummyTransport { false } - fn dial(&mut self, addr: Multiaddr, _opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + _opts: DialOpts, + ) -> Result> { Err(TransportError::MultiaddrNotSupported(addr)) } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index b04b8bbaf20..7fb45237078 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -18,6 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use crate::transport::DialOpts; use crate::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, @@ -27,7 +28,6 @@ use std::{ pin::Pin, task::{Context, Poll}, }; -use crate::transport::DialOpts; /// Dropping all dial requests to non-global IP addresses. #[derive(Debug, Clone, Default)] @@ -289,7 +289,11 @@ impl crate::Transport for Transport { self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { if !ipv4_global::is_global(a) { diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index f59650f1fd0..ad6e34f7116 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -18,6 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use crate::transport::DialOpts; use crate::{ connection::{ConnectedPoint, Endpoint}, transport::{Transport, TransportError, TransportEvent}, @@ -25,7 +26,6 @@ use crate::{ use futures::prelude::*; use multiaddr::Multiaddr; use std::{pin::Pin, task::Context, task::Poll}; -use crate::transport::DialOpts; use super::ListenerId; @@ -74,7 +74,11 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, dial_opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { let future = self.transport.dial(addr.clone(), dial_opts)?; let p = ConnectedPoint::Dialer { address: addr, diff --git a/core/src/transport/map_err.rs b/core/src/transport/map_err.rs index 3e1cab398c1..6859445882e 100644 --- a/core/src/transport/map_err.rs +++ b/core/src/transport/map_err.rs @@ -65,7 +65,11 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { let map = self.map.clone(); match self.transport.dial(addr, opts) { Ok(future) => Ok(MapErrDial { diff --git a/core/src/transport/memory.rs b/core/src/transport/memory.rs index 9a553aa7175..ad862a4d4f9 100644 --- a/core/src/transport/memory.rs +++ b/core/src/transport/memory.rs @@ -219,7 +219,11 @@ impl Transport for MemoryTransport { } } - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { let port = if let Ok(port) = parse_memory_addr(&addr) { if let Some(port) = NonZeroU64::new(port) { port diff --git a/core/src/transport/optional.rs b/core/src/transport/optional.rs index 5785047e1cd..ee86bff6fd8 100644 --- a/core/src/transport/optional.rs +++ b/core/src/transport/optional.rs @@ -80,7 +80,11 @@ where } } - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { if let Some(inner) = self.0.as_mut() { inner.dial(addr, opts) } else { diff --git a/core/src/transport/timeout.rs b/core/src/transport/timeout.rs index 8831fd3ee93..1946f76bfa4 100644 --- a/core/src/transport/timeout.rs +++ b/core/src/transport/timeout.rs @@ -24,6 +24,7 @@ //! underlying `Transport`. // TODO: add example +use crate::transport::DialOpts; use crate::{ transport::{ListenerId, TransportError, TransportEvent}, Multiaddr, Transport, @@ -31,7 +32,6 @@ use crate::{ use futures::prelude::*; use futures_timer::Delay; use std::{error, fmt, io, pin::Pin, task::Context, task::Poll, time::Duration}; -use crate::transport::DialOpts; /// A `TransportTimeout` is a `Transport` that wraps another `Transport` and adds /// timeouts to all inbound and outbound connection attempts. @@ -100,7 +100,11 @@ where self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { let dial = self .inner .dial(addr, opts) diff --git a/core/src/transport/upgrade.rs b/core/src/transport/upgrade.rs index c90c5870f26..5d849042b79 100644 --- a/core/src/transport/upgrade.rs +++ b/core/src/transport/upgrade.rs @@ -22,6 +22,7 @@ pub use crate::upgrade::Version; +use crate::transport::DialOpts; use crate::{ connection::ConnectedPoint, muxing::{StreamMuxer, StreamMuxerBox}, @@ -45,7 +46,6 @@ use std::{ task::{Context, Poll}, time::Duration, }; -use crate::transport::DialOpts; /// A `Builder` facilitates upgrading of a [`Transport`] for use with /// a `Swarm`. @@ -336,7 +336,11 @@ where type ListenerUpgrade = T::ListenerUpgrade; type Dial = T::Dial; - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { self.0.dial(addr, opts) } @@ -405,7 +409,11 @@ where type ListenerUpgrade = ListenerUpgradeFuture; type Dial = DialUpgradeFuture; - fn dial(&mut self, addr: Multiaddr, opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { let future = self .inner .dial(addr, opts) diff --git a/swarm/src/dial_opts.rs b/swarm/src/dial_opts.rs index 2636de666fa..ac88bffbe0d 100644 --- a/swarm/src/dial_opts.rs +++ b/swarm/src/dial_opts.rs @@ -22,10 +22,48 @@ use crate::ConnectionId; use libp2p_core::connection::Endpoint; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use std::num::NonZeroU8; +macro_rules! port_use_and_role_setter { + () => { + /// Override role of local node on connection. I.e. execute the dial _as a + /// listener_. + /// + /// See + /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer) + /// for details. + pub fn override_role(mut self) -> Self { + self.role_override = Endpoint::Listener; + self + } + + /// Enforce the reuse of an existing port. + /// Default behaviour is: + /// 1. Swarm tracks if there already exists a listener address for this protocol + /// 2. Decide port use behaviour: + /// - If we have 0 listen addresses, we allocate a new port + /// - If we have >1 listen addresses, we reuse an existing port + pub fn reuse_existing_port(mut self) -> Self { + self.port_use = Some(PortUse::Reuse); + self + } + + /// Enforce the allocation of a new port. + /// Default behaviour is: + /// 1. Swarm tracks if there already exists a listener address for this protocol + /// 2. Decide port use behaviour: + /// - If we have 0 listen addresses, we allocate a new port + /// - If we have >1 listen addresses, we reuse an existing port + pub fn allocate_new_port(mut self) -> Self { + self.port_use = Some(PortUse::New); + self + } + }; +} + /// Options to configure a dial to a known or unknown peer. /// /// Used in [`Swarm::dial`](crate::Swarm::dial) and @@ -45,7 +83,7 @@ pub struct DialOpts { role_override: Endpoint, dial_concurrency_factor_override: Option, connection_id: ConnectionId, - port_mode: PortMode, + port_use: Option, } impl DialOpts { @@ -66,7 +104,7 @@ impl DialOpts { condition: Default::default(), role_override: Endpoint::Dialer, dial_concurrency_factor_override: Default::default(), - port_mode: Default::default(), + port_use: None, } } @@ -127,7 +165,9 @@ impl DialOpts { self.role_override } - pub(crate) fn port_mode(&self) -> PortMode { self.port_mode } + pub(crate) fn port_use(&self) -> Option { + self.port_use + } } impl From for DialOpts { @@ -148,7 +188,7 @@ pub struct WithPeerId { condition: PeerCondition, role_override: Endpoint, dial_concurrency_factor_override: Option, - port_mode: PortMode, + port_use: Option, } impl WithPeerId { @@ -174,20 +214,11 @@ impl WithPeerId { extend_addresses_through_behaviour: false, role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, - port_mode: self.port_mode, + port_use: self.port_use, } } - /// Override role of local node on connection. I.e. execute the dial _as a - /// listener_. - /// - /// See - /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer) - /// for details. - pub fn override_role(mut self) -> Self { - self.role_override = Endpoint::Listener; - self - } + port_use_and_role_setter!(); /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { @@ -199,7 +230,7 @@ impl WithPeerId { role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, connection_id: ConnectionId::next(), - port_mode: self.port_mode, + port_use: self.port_use, } } } @@ -212,7 +243,7 @@ pub struct WithPeerIdWithAddresses { extend_addresses_through_behaviour: bool, role_override: Endpoint, dial_concurrency_factor_override: Option, - port_mode: PortMode, + port_use: Option, } impl WithPeerIdWithAddresses { @@ -229,21 +260,7 @@ impl WithPeerIdWithAddresses { self } - /// Override role of local node on connection. I.e. execute the dial _as a - /// listener_. - /// - /// See - /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer) - /// for details. - pub fn override_role(mut self) -> Self { - self.role_override = Endpoint::Listener; - self - } - - pub fn override_port_mode(mut self, port_mode: PortMode) -> Self { - self.port_mode = port_mode; - self - } + port_use_and_role_setter!(); /// Override /// Number of addresses concurrently dialed for a single outbound connection attempt. @@ -262,7 +279,7 @@ impl WithPeerIdWithAddresses { role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, connection_id: ConnectionId::next(), - port_mode: self.port_mode, + port_use: self.port_use, } } } @@ -276,7 +293,7 @@ impl WithoutPeerId { WithoutPeerIdWithAddress { address, role_override: Endpoint::Dialer, - port_mode: Default::default(), + port_use: None, } } } @@ -285,25 +302,11 @@ impl WithoutPeerId { pub struct WithoutPeerIdWithAddress { address: Multiaddr, role_override: Endpoint, - port_mode: PortMode, + port_use: Option, } impl WithoutPeerIdWithAddress { - /// Override role of local node on connection. I.e. execute the dial _as a - /// listener_. - /// - /// See - /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer) - /// for details. - pub fn override_role(mut self) -> Self { - self.role_override = Endpoint::Listener; - self - } - - pub fn override_port_mode(mut self, port_mode: PortMode) -> Self { - self.port_mode = port_mode; - self - } + port_use_and_role_setter!(); /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { @@ -315,7 +318,7 @@ impl WithoutPeerIdWithAddress { role_override: self.role_override, dial_concurrency_factor_override: None, connection_id: ConnectionId::next(), - port_mode: self.port_mode, + port_use: self.port_use, } } } @@ -346,18 +349,3 @@ pub enum PeerCondition { /// configured connection limits. Always, } - -/// The port mode to use. I.e. on which port an outgoing connection should dial. -/// Irrelevant when the transport doesn't support that option. -#[derive(Default, Debug, Clone, Copy)] -pub enum PortMode { - /// Leave the decision to the swarm: - /// - If we have 0 listen addresses, we allocate a new port by default. - /// - If we have at least one address, we reuse an existing port. - #[default] - Auto, - /// Always allocate a new port - New, - /// Always reuse an existing port - Reuse, -} diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index a9f81b76296..020a78bc65c 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -68,7 +68,7 @@ pub mod dial_opts; pub mod dummy; pub mod handler; #[deprecated( -note = "Configure an appropriate idle connection timeout via `SwarmBuilder::idle_connection_timeout` instead. To keep connections alive 'forever', use `Duration::from_secs(u64::MAX)`." + note = "Configure an appropriate idle connection timeout via `SwarmBuilder::idle_connection_timeout` instead. To keep connections alive 'forever', use `Duration::from_secs(u64::MAX)`." )] pub mod keep_alive; mod listen_opts; @@ -138,15 +138,22 @@ use connection::{ }; use dial_opts::{DialOpts, PeerCondition}; use futures::{prelude::*, stream::FusedStream}; + +use libp2p_core::transport::PortUse; use libp2p_core::{ connection::ConnectedPoint, multiaddr, muxing::StreamMuxerBox, - transport::{self, ListenerId, TransportError, TransportEvent, DialOpts as TransportDialOpts, PortMode as TransportPortMode}, + transport::{ + self, DialOpts as TransportDialOpts, ListenerId, + TransportError, TransportEvent, + }, Multiaddr, Transport, }; use libp2p_identity::PeerId; + use smallvec::SmallVec; + use std::collections::{HashMap, HashSet}; use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; use std::time::Duration; @@ -156,7 +163,6 @@ use std::{ pin::Pin, task::{Context, Poll}, }; -use crate::dial_opts::PortMode; /// Substream for which a protocol has been chosen. /// @@ -325,8 +331,8 @@ impl SwarmEvent` in order to make /// progress. pub struct Swarm - where - TBehaviour: NetworkBehaviour, +where + TBehaviour: NetworkBehaviour, { /// [`Transport`] for dialing remote peers and listening for incoming connection. transport: transport::Boxed<(PeerId, StreamMuxerBox)>, @@ -358,8 +364,8 @@ pub struct Swarm impl Unpin for Swarm where TBehaviour: NetworkBehaviour {} impl Swarm - where - TBehaviour: NetworkBehaviour, +where + TBehaviour: NetworkBehaviour, { /// Returns information about the connections underlying the [`Swarm`]. pub fn network_info(&self) -> NetworkInfo { @@ -497,12 +503,54 @@ impl Swarm addresses_from_opts }; - let transport_dial_opts = self.convert_dial_opts(&dial_opts); + // allows to map a protocol, if the tag is one, to an idx. Allows for O(1) operations on presence checking. + // This might be a premature optimization and is prone to errors. If Protocol::tag would be const one could write a simpler variant, with comparable performance. + fn protocol_idx(tag: &str) -> Option { + match tag { + "memory" => Some(0), + "onion" => Some(1), + "onion3" => Some(2), + "quic" => Some(3), + "quic-v1" => Some(4), + "tcp" => Some(5), + "udp" => Some(6), + "webtransport" => Some(7), + "ws" => Some(8), + "wss" => Some(9), + _ => None, + } + } + + // data structure to efficiently check for protocol presence + let mut protocol_present = [false; 10]; + self.listeners() + .filter_map(|m| m.protocol_stack().find_map(protocol_idx)) + .for_each(|idx| { + protocol_present[idx] = true; + }); let dials = addresses .into_iter() .map(|a| match p2p_addr(peer_id, a) { Ok(address) => { + let transport_dial_opts = { + let transport_port_use = dial_opts.port_use().unwrap_or_else(|| { + if address + .protocol_stack() + .find_map(protocol_idx) + .map(|idx| protocol_present[idx]) + .unwrap_or(false) + { + PortUse::Reuse + } else { + PortUse::New + } + }); + TransportDialOpts { + endpoint: dial_opts.role_override(), + port_use: transport_port_use, + } + }; let dial = self.transport.dial(address.clone(), transport_dial_opts); match dial { Ok(fut) => fut @@ -515,7 +563,7 @@ impl Swarm address.clone(), Err(TransportError::MultiaddrNotSupported(address)), )) - .boxed(), + .boxed(), }) .collect(); @@ -531,7 +579,7 @@ impl Swarm } /// Returns an iterator that produces the list of addresses we're listening on. - pub fn listeners(&self) -> impl Iterator { + pub fn listeners(&self) -> impl Iterator { self.listened_addrs.values().flatten() } @@ -541,7 +589,7 @@ impl Swarm } /// List all **confirmed** external address for the local node. - pub fn external_addresses(&self) -> impl Iterator { + pub fn external_addresses(&self) -> impl Iterator { self.confirmed_external_addr.iter() } @@ -634,7 +682,7 @@ impl Swarm } /// Returns the currently connected peers. - pub fn connected_peers(&self) -> impl Iterator { + pub fn connected_peers(&self) -> impl Iterator { self.pool.iter_connected() } @@ -737,7 +785,7 @@ impl Swarm let num_established = NonZeroU32::new( u32::try_from(other_established_connection_ids.len() + 1).unwrap(), ) - .expect("n + 1 is always non-zero; qed"); + .expect("n + 1 is always non-zero; qed"); self.pool .spawn_connection(id, peer_id, &endpoint, connection, handler); @@ -1217,25 +1265,6 @@ impl Swarm return Poll::Pending; } } - - fn convert_port_mode(&self, port_mode: PortMode) -> TransportPortMode { - match port_mode { - PortMode::Auto => if self.listened_addrs.is_empty() { - TransportPortMode::New - } else { - TransportPortMode::Reuse - }, - PortMode::New => TransportPortMode::New, - PortMode::Reuse => TransportPortMode::Reuse, - } - } - - fn convert_dial_opts(&self, dial_opts: &DialOpts) -> TransportDialOpts { - TransportDialOpts { - endpoint: dial_opts.role_override(), - port_mode: self.convert_port_mode(dial_opts.port_mode()), - } - } } /// Connection to notify of a pending event. @@ -1289,12 +1318,12 @@ fn notify_any( event: THandlerInEvent, cx: &mut Context<'_>, ) -> Option<(THandlerInEvent, SmallVec<[ConnectionId; 10]>)> - where - TBehaviour: NetworkBehaviour, - THandler: ConnectionHandler< - FromBehaviour=THandlerInEvent, - ToBehaviour=THandlerOutEvent, - >, +where + TBehaviour: NetworkBehaviour, + THandler: ConnectionHandler< + FromBehaviour = THandlerInEvent, + ToBehaviour = THandlerOutEvent, + >, { let mut pending = SmallVec::new(); let mut event = Some(event); // (1) @@ -1332,8 +1361,8 @@ fn notify_any( /// Note: This stream is infinite and it is guaranteed that /// [`futures::Stream::poll_next`] will never return `Poll::Ready(None)`. impl futures::Stream for Swarm - where - TBehaviour: NetworkBehaviour, +where + TBehaviour: NetworkBehaviour, { type Item = SwarmEvent, THandlerErr>; @@ -1344,8 +1373,8 @@ impl futures::Stream for Swarm /// The stream of swarm events never terminates, so we can implement fused for it. impl FusedStream for Swarm - where - TBehaviour: NetworkBehaviour, +where + TBehaviour: NetworkBehaviour, { fn is_terminated(&self) -> bool { false @@ -1375,8 +1404,8 @@ pub struct SwarmBuilder { } impl SwarmBuilder - where - TBehaviour: NetworkBehaviour, +where + TBehaviour: NetworkBehaviour, { /// Creates a new [`SwarmBuilder`] from the given transport, behaviour, local peer ID and /// executor. The `Swarm` with its underlying `Network` is obtained via @@ -1421,8 +1450,8 @@ impl SwarmBuilder /// Builds a new [`SwarmBuilder`] from the given transport, behaviour, local peer ID and a /// `tokio` executor. #[cfg(all( - feature = "tokio", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) + feature = "tokio", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) ))] pub fn with_tokio_executor( transport: transport::Boxed<(PeerId, StreamMuxerBox)>, @@ -1440,8 +1469,8 @@ impl SwarmBuilder /// Builds a new [`SwarmBuilder`] from the given transport, behaviour, local peer ID and a /// `async-std` executor. #[cfg(all( - feature = "async-std", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) + feature = "async-std", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) ))] pub fn with_async_std_executor( transport: transport::Boxed<(PeerId, StreamMuxerBox)>, @@ -1755,8 +1784,8 @@ impl ConnectionDenied { /// Attempt to downcast to a particular reason for why the connection was denied. pub fn downcast(self) -> Result - where - E: error::Error + Send + Sync + 'static, + where + E: error::Error + Send + Sync + 'static, { let inner = self .inner @@ -1768,8 +1797,8 @@ impl ConnectionDenied { /// Attempt to downcast to a particular reason for why the connection was denied. pub fn downcast_ref(&self) -> Option<&E> - where - E: error::Error + Send + Sync + 'static, + where + E: error::Error + Send + Sync + 'static, { self.inner.downcast_ref::() } @@ -1861,7 +1890,8 @@ mod tests { Disconnecting, } - fn new_test_swarm() -> SwarmBuilder>> { + fn new_test_swarm( + ) -> SwarmBuilder>> { let id_keys = identity::Keypair::generate_ed25519(); let local_public_key = id_keys.public(); let transport = transport::MemoryTransport::default() @@ -1885,18 +1915,18 @@ mod tests { swarm2: &Swarm>, num_connections: usize, ) -> bool - where - TBehaviour: NetworkBehaviour, - THandlerOutEvent: Clone, + where + TBehaviour: NetworkBehaviour, + THandlerOutEvent: Clone, { swarm1 .behaviour() .num_connections_to_peer(*swarm2.local_peer_id()) == num_connections && swarm2 - .behaviour() - .num_connections_to_peer(*swarm1.local_peer_id()) - == num_connections + .behaviour() + .num_connections_to_peer(*swarm1.local_peer_id()) + == num_connections && swarm1.is_connected(swarm2.local_peer_id()) && swarm2.is_connected(swarm1.local_peer_id()) } @@ -1905,18 +1935,18 @@ mod tests { swarm1: &Swarm>, swarm2: &Swarm>, ) -> bool - where - TBehaviour: NetworkBehaviour, - THandlerOutEvent: Clone, + where + TBehaviour: NetworkBehaviour, + THandlerOutEvent: Clone, { swarm1 .behaviour() .num_connections_to_peer(*swarm2.local_peer_id()) == 0 && swarm2 - .behaviour() - .num_connections_to_peer(*swarm1.local_peer_id()) - == 0 + .behaviour() + .num_connections_to_peer(*swarm1.local_peer_id()) + == 0 && !swarm1.is_connected(swarm2.local_peer_id()) && !swarm2.is_connected(swarm1.local_peer_id()) } @@ -2229,8 +2259,8 @@ mod tests { match swarm2.poll_next_unpin(cx) { Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { - peer_id, error, .. - })) => Poll::Ready((peer_id, error)), + peer_id, error, .. + })) => Poll::Ready((peer_id, error)), Poll::Ready(x) => panic!("unexpected {x:?}"), Poll::Pending => Poll::Pending, } @@ -2285,10 +2315,10 @@ mod tests { loop { match swarm.poll_next_unpin(cx) { Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { - peer_id, - error: DialError::LocalPeerId { .. }, - .. - })) => { + peer_id, + error: DialError::LocalPeerId { .. }, + .. + })) => { assert_eq!(&peer_id.unwrap(), swarm.local_peer_id()); assert!(!got_dial_err); got_dial_err = true; @@ -2297,8 +2327,8 @@ mod tests { } } Poll::Ready(Some(SwarmEvent::IncomingConnectionError { - local_addr, .. - })) => { + local_addr, .. + })) => { assert!(!got_inc_err); assert_eq!(local_addr, local_address); got_inc_err = true; @@ -2316,7 +2346,7 @@ mod tests { } } })) - .unwrap(); + .unwrap(); } #[test] diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 142a09219c3..d3fdbc2b90b 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -43,6 +43,7 @@ use futures::{ }; use futures_timer::Delay; use if_watch::IfEvent; +use libp2p_core::transport::DialOpts; use libp2p_core::{ address_translation, multiaddr::{Multiaddr, Protocol}, @@ -59,7 +60,6 @@ use std::{ task::{Context, Poll, Waker}, time::Duration, }; -use libp2p_core::transport::DialOpts; /// The configuration for a TCP/IP transport capability for libp2p. #[derive(Clone, Debug)] @@ -464,7 +464,11 @@ where } } - fn dial(&mut self, addr: Multiaddr, _opts: DialOpts) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + _opts: DialOpts, + ) -> Result> { let socket_addr = if let Ok(socket_addr) = multiaddr_to_socketaddr(addr.clone()) { if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { return Err(TransportError::MultiaddrNotSupported(addr)); From 461494b8f13be2cd5127ced7372bfe907d7bfe08 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:30:58 +0200 Subject: [PATCH 004/179] Introduced listener presence structure. Allows for efficient detection if we already have a listener for outgoing connections. --- swarm/src/lib.rs | 34 ++------------- swarm/src/listener_presence.rs | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 30 deletions(-) create mode 100644 swarm/src/listener_presence.rs diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 020a78bc65c..b94cebc3846 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -72,6 +72,7 @@ pub mod handler; )] pub mod keep_alive; mod listen_opts; +mod listener_presence; /// Bundles all symbols required for the [`libp2p_swarm_derive::NetworkBehaviour`] macro. #[doc(hidden)] @@ -163,6 +164,7 @@ use std::{ pin::Pin, task::{Context, Poll}, }; +use crate::listener_presence::ListenerPresence; /// Substream for which a protocol has been chosen. /// @@ -503,31 +505,7 @@ where addresses_from_opts }; - // allows to map a protocol, if the tag is one, to an idx. Allows for O(1) operations on presence checking. - // This might be a premature optimization and is prone to errors. If Protocol::tag would be const one could write a simpler variant, with comparable performance. - fn protocol_idx(tag: &str) -> Option { - match tag { - "memory" => Some(0), - "onion" => Some(1), - "onion3" => Some(2), - "quic" => Some(3), - "quic-v1" => Some(4), - "tcp" => Some(5), - "udp" => Some(6), - "webtransport" => Some(7), - "ws" => Some(8), - "wss" => Some(9), - _ => None, - } - } - - // data structure to efficiently check for protocol presence - let mut protocol_present = [false; 10]; - self.listeners() - .filter_map(|m| m.protocol_stack().find_map(protocol_idx)) - .for_each(|idx| { - protocol_present[idx] = true; - }); + let listener_presence: ListenerPresence = self.listeners().collect(); let dials = addresses .into_iter() @@ -535,11 +513,7 @@ where Ok(address) => { let transport_dial_opts = { let transport_port_use = dial_opts.port_use().unwrap_or_else(|| { - if address - .protocol_stack() - .find_map(protocol_idx) - .map(|idx| protocol_present[idx]) - .unwrap_or(false) + if listener_presence.contains(&address) { PortUse::Reuse } else { diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs new file mode 100644 index 00000000000..677c2bddf22 --- /dev/null +++ b/swarm/src/listener_presence.rs @@ -0,0 +1,80 @@ +//! The listener presence module allows for easy and fast checking if there is a listener present +//! for a multiaddress we want to dial. + +use std::collections::HashSet; +use libp2p_core::Multiaddr; + +fn is_not_protocol(tag: &str) -> bool { + // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's supposed to be and it's a protocol and an address. But since I wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just ignore that. + // We might also chose to ignore encryption, like noise and tls + !matches!(tag, "dns" | "dns4" | "dns6" | "dnsaddr" | "ip4" | "ip6" | "p2p") +} + +// Turns a multiaddress into a vector of just the protocols. +fn clean_multiaddr(address: &Multiaddr) -> Vec<&'static str> { + address.protocol_stack().filter(|e| is_not_protocol(e)).collect() +} + +type ProtocolStack = Vec<&'static str>; + +#[derive(Debug, Clone, Default)] +pub(super) struct ListenerPresence { + inner: HashSet, +} + +impl<'a> FromIterator<&'a Multiaddr> for ListenerPresence { + fn from_iter>(iter: T) -> Self { + let inner = iter.into_iter().map(clean_multiaddr).collect(); + Self { + inner + } + } +} + +impl ListenerPresence { + pub fn contains(&self, address: &Multiaddr) -> bool { + let protocol_stack = clean_multiaddr(address); + self.inner.contains(&protocol_stack) + } +} + +// Some tests with real world data would be appreciated +#[cfg(test)] +mod tests { + use libp2p_core::multiaddr::multiaddr; + use libp2p_identity::PeerId; + use crate::listener_presence::ListenerPresence; + use std::str::FromStr; + + #[test] + fn basic_ops() { + let bootstrap_libp2p_node_peer_id = PeerId::from_str("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN").unwrap(); + let test_addrs = [ + multiaddr!(Ip4([127, 0, 0, 1]), Tcp(1234u16)), + multiaddr!(Ip6([11, 22, 33, 44, 55, 66, 77, 88]), Udp(199u16), Tls, Quic), + multiaddr!(Dns4("heise.de"), Tcp(443u16), Tls, Https), + multiaddr!(Dnsaddr("bootstrap.libp2p.io"), P2p(bootstrap_libp2p_node_peer_id)), + multiaddr!(Ip4([104, 131, 131, 82]), Udp(4001u16), Quic, P2p(bootstrap_libp2p_node_peer_id)), + ]; + let listener_presence: ListenerPresence = test_addrs.iter().collect(); + assert!(test_addrs.iter().all(|addr| listener_presence.contains(addr)), "Basic input operations are not working. Likely cleaning function is not pure."); + } + + #[test] + fn reducing_functionality() { + let build_up_address = [ + multiaddr!(Dnsaddr("libp2p.io"), Tls, Tcp(10u16)), + multiaddr!(Dnsaddr("libp2p.io"), Tls, Tcp(12u16), Udp(13u16), Quic), + multiaddr!(Ip4([1, 1, 1, 1]), Udp(100u16)), + ]; + let listener_presence: ListenerPresence = build_up_address.iter().collect(); + assert!(build_up_address.iter().all(|addr| listener_presence.contains(addr))); + assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); + assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16), Dnsaddr("bootstrap.libp2p.io")))); + assert!(listener_presence.contains(&multiaddr!(Dns("one.one.one.one"), Tls, Tcp(100u16)))); + assert!(!listener_presence.contains(&multiaddr!(Dns("one.one.one.one"), Tcp(100u16)))); + assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("libp2p.io"), Tcp(10u16), Tls))); + assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("libp2p.io"), Quic, Udp(13u16), Tcp(12u16), Tls))); + assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("one.one.one.one"), Udp(100u16), Tls))); + } +} From d6ea5bc7a5b91d229203b9fc2967381d496e5114 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:08:48 +0200 Subject: [PATCH 005/179] Continous updates and BTree --- swarm/src/lib.rs | 19 +++--- swarm/src/listener_presence.rs | 118 ++++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 33 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index fd81562bf97..398c4f01dd4 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -145,16 +145,14 @@ use libp2p_core::{ connection::ConnectedPoint, multiaddr, muxing::StreamMuxerBox, - transport::{ - self, DialOpts as TransportDialOpts, ListenerId, - TransportError, TransportEvent, - }, + transport::{self, DialOpts as TransportDialOpts, ListenerId, TransportError, TransportEvent}, Multiaddr, Transport, }; use libp2p_identity::PeerId; use smallvec::SmallVec; +use crate::listener_presence::ListenerPresence; use std::collections::{HashMap, HashSet}; use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; use std::time::Duration; @@ -164,7 +162,6 @@ use std::{ pin::Pin, task::{Context, Poll}, }; -use crate::listener_presence::ListenerPresence; /// Substream for which a protocol has been chosen. /// @@ -357,6 +354,9 @@ where /// Multiaddresses that our listeners are listening on, listened_addrs: HashMap>, + /// Listener presence monitor + listener_presence: ListenerPresence, + /// Pending event to be delivered to connection handlers /// (or dropped if the peer disconnected) before the `behaviour` /// can be polled again. @@ -505,16 +505,13 @@ where addresses_from_opts }; - let listener_presence: ListenerPresence = self.listeners().collect(); - let dials = addresses .into_iter() .map(|a| match p2p_addr(peer_id, a) { Ok(address) => { let transport_dial_opts = { let transport_port_use = dial_opts.port_use().unwrap_or_else(|| { - if listener_presence.contains(&address) - { + if self.listener_presence.contains(&address) { PortUse::Reuse } else { PortUse::New @@ -980,6 +977,7 @@ where if !addrs.contains(&listen_addr) { addrs.push(listen_addr.clone()) } + self.listener_presence.new_listener(&listen_addr); self.behaviour .on_swarm_event(FromSwarm::NewListenAddr(NewListenAddr { listener_id, @@ -1002,6 +1000,7 @@ where if let Some(addrs) = self.listened_addrs.get_mut(&listener_id) { addrs.retain(|a| a != &listen_addr); } + self.listener_presence.expired_listener(&listen_addr); self.behaviour .on_swarm_event(FromSwarm::ExpiredListenAddr(ExpiredListenAddr { listener_id, @@ -1019,6 +1018,7 @@ where log::debug!("Listener {:?}; Closed by {:?}.", listener_id, reason); let addrs = self.listened_addrs.remove(&listener_id).unwrap_or_default(); for addr in addrs.iter() { + self.listener_presence.expired_listener(addr); self.behaviour.on_swarm_event(FromSwarm::ExpiredListenAddr( ExpiredListenAddr { listener_id, addr }, )); @@ -1563,6 +1563,7 @@ where supported_protocols: Default::default(), confirmed_external_addr: Default::default(), listened_addrs: HashMap::new(), + listener_presence: Default::default(), pending_event: None, } } diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs index 677c2bddf22..66617750c92 100644 --- a/swarm/src/listener_presence.rs +++ b/swarm/src/listener_presence.rs @@ -1,63 +1,116 @@ //! The listener presence module allows for easy and fast checking if there is a listener present //! for a multiaddress we want to dial. -use std::collections::HashSet; use libp2p_core::Multiaddr; +use smallvec::SmallVec; +use std::collections::BTreeMap; + +type ProtocolStack = SmallVec<[&'static str; 4]>; fn is_not_protocol(tag: &str) -> bool { // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's supposed to be and it's a protocol and an address. But since I wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just ignore that. - // We might also chose to ignore encryption, like noise and tls - !matches!(tag, "dns" | "dns4" | "dns6" | "dnsaddr" | "ip4" | "ip6" | "p2p") + // We might also choose to ignore encryption, like noise and tls + !matches!( + tag, + "dns" | "dns4" | "dns6" | "dnsaddr" | "ip4" | "ip6" | "p2p" + ) } // Turns a multiaddress into a vector of just the protocols. -fn clean_multiaddr(address: &Multiaddr) -> Vec<&'static str> { - address.protocol_stack().filter(|e| is_not_protocol(e)).collect() +fn clean_multiaddr(address: &Multiaddr) -> ProtocolStack { + address + .protocol_stack() + .filter(|e| is_not_protocol(e)) + .collect() } -type ProtocolStack = Vec<&'static str>; - #[derive(Debug, Clone, Default)] pub(super) struct ListenerPresence { - inner: HashSet, + inner: BTreeMap, } +#[cfg(test)] impl<'a> FromIterator<&'a Multiaddr> for ListenerPresence { - fn from_iter>(iter: T) -> Self { - let inner = iter.into_iter().map(clean_multiaddr).collect(); - Self { - inner - } + fn from_iter>(iter: T) -> Self { + let inner = iter + .into_iter() + .map(clean_multiaddr) + .map(|key| (key, 1)) + .collect(); + Self { inner } } } impl ListenerPresence { pub fn contains(&self, address: &Multiaddr) -> bool { let protocol_stack = clean_multiaddr(address); - self.inner.contains(&protocol_stack) + #[cfg(debug_assertions)] + if let Some(key) = self.inner.get(&protocol_stack) { + debug_assert_ne!(*key, 0, "The entry value should never be zero and exist."); + } + self.inner.contains_key(&protocol_stack) + } + + pub fn new_listener(&mut self, address: &Multiaddr) { + self.inner + .entry(clean_multiaddr(address)) + .and_modify(|e| *e += 1) + .or_insert(1); + } + + pub fn expired_listener(&mut self, address: &Multiaddr) { + let protocol_stack = clean_multiaddr(address); + match self.inner.get_mut(&protocol_stack) { + Some(1) => { + self.inner.remove(&protocol_stack); + } + Some(n) => { + *n -= 1; + } + None => {} + } } } // Some tests with real world data would be appreciated #[cfg(test)] mod tests { + use crate::listener_presence::ListenerPresence; use libp2p_core::multiaddr::multiaddr; use libp2p_identity::PeerId; - use crate::listener_presence::ListenerPresence; use std::str::FromStr; #[test] fn basic_ops() { - let bootstrap_libp2p_node_peer_id = PeerId::from_str("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN").unwrap(); + let bootstrap_libp2p_node_peer_id = + PeerId::from_str("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN").unwrap(); let test_addrs = [ multiaddr!(Ip4([127, 0, 0, 1]), Tcp(1234u16)), - multiaddr!(Ip6([11, 22, 33, 44, 55, 66, 77, 88]), Udp(199u16), Tls, Quic), + multiaddr!( + Ip6([11, 22, 33, 44, 55, 66, 77, 88]), + Udp(199u16), + Tls, + Quic + ), multiaddr!(Dns4("heise.de"), Tcp(443u16), Tls, Https), - multiaddr!(Dnsaddr("bootstrap.libp2p.io"), P2p(bootstrap_libp2p_node_peer_id)), - multiaddr!(Ip4([104, 131, 131, 82]), Udp(4001u16), Quic, P2p(bootstrap_libp2p_node_peer_id)), + multiaddr!( + Dnsaddr("bootstrap.libp2p.io"), + P2p(bootstrap_libp2p_node_peer_id) + ), + multiaddr!( + Ip4([104, 131, 131, 82]), + Udp(4001u16), + Quic, + P2p(bootstrap_libp2p_node_peer_id) + ), ]; let listener_presence: ListenerPresence = test_addrs.iter().collect(); - assert!(test_addrs.iter().all(|addr| listener_presence.contains(addr)), "Basic input operations are not working. Likely cleaning function is not pure."); + assert!( + test_addrs + .iter() + .all(|addr| listener_presence.contains(addr)), + "Basic input operations are not working. Likely cleaning function is not pure." + ); } #[test] @@ -68,13 +121,30 @@ mod tests { multiaddr!(Ip4([1, 1, 1, 1]), Udp(100u16)), ]; let listener_presence: ListenerPresence = build_up_address.iter().collect(); - assert!(build_up_address.iter().all(|addr| listener_presence.contains(addr))); + assert!(build_up_address + .iter() + .all(|addr| listener_presence.contains(addr))); assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); - assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16), Dnsaddr("bootstrap.libp2p.io")))); + assert!(listener_presence.contains(&multiaddr!( + Dns4("libp2p.io"), + Tls, + Tcp(10u16), + Dnsaddr("bootstrap.libp2p.io") + ))); assert!(listener_presence.contains(&multiaddr!(Dns("one.one.one.one"), Tls, Tcp(100u16)))); assert!(!listener_presence.contains(&multiaddr!(Dns("one.one.one.one"), Tcp(100u16)))); assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("libp2p.io"), Tcp(10u16), Tls))); - assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("libp2p.io"), Quic, Udp(13u16), Tcp(12u16), Tls))); - assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("one.one.one.one"), Udp(100u16), Tls))); + assert!(!listener_presence.contains(&multiaddr!( + Dnsaddr("libp2p.io"), + Quic, + Udp(13u16), + Tcp(12u16), + Tls + ))); + assert!(!listener_presence.contains(&multiaddr!( + Dnsaddr("one.one.one.one"), + Udp(100u16), + Tls + ))); } } From 9cc3ff5c43520b58769c9bad5c29ec949892c566 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:43:54 +0200 Subject: [PATCH 006/179] Make for better testability --- swarm/src/listener_presence.rs | 38 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs index 66617750c92..461c4cc88ec 100644 --- a/swarm/src/listener_presence.rs +++ b/swarm/src/listener_presence.rs @@ -7,13 +7,13 @@ use std::collections::BTreeMap; type ProtocolStack = SmallVec<[&'static str; 4]>; +// I'm not sure about the selection here. A good example is Onion. It's not good defined what that's supposed to be and it's a protocol and an address. But since I wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just ignore that. +// We might also choose to ignore encryption, like noise and tls +const NON_PROTOCOL_TAGS: &[&str] = &["dns", "dns4", "dns6", "dnsaddr", "ip4", "ip6", "p2p"]; + fn is_not_protocol(tag: &str) -> bool { - // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's supposed to be and it's a protocol and an address. But since I wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just ignore that. - // We might also choose to ignore encryption, like noise and tls - !matches!( - tag, - "dns" | "dns4" | "dns6" | "dnsaddr" | "ip4" | "ip6" | "p2p" - ) + // using contains instead of matches! isn't a lot different, when we look at the generated assembly (https://godbolt.org/z/1x9f3K16x) + !NON_PROTOCOL_TAGS.contains(&tag) } // Turns a multiaddress into a vector of just the protocols. @@ -75,9 +75,11 @@ impl ListenerPresence { // Some tests with real world data would be appreciated #[cfg(test)] mod tests { - use crate::listener_presence::ListenerPresence; - use libp2p_core::multiaddr::multiaddr; + use crate::listener_presence::{clean_multiaddr, ListenerPresence, NON_PROTOCOL_TAGS}; + use libp2p_core::multiaddr::{multiaddr, Protocol}; use libp2p_identity::PeerId; + use std::borrow::Cow; + use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; #[test] @@ -124,6 +126,7 @@ mod tests { assert!(build_up_address .iter() .all(|addr| listener_presence.contains(addr))); + assert_eq!(clean_multiaddr(&build_up_address[0]), clean_multiaddr(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); assert!(listener_presence.contains(&multiaddr!( Dns4("libp2p.io"), @@ -147,4 +150,23 @@ mod tests { Tls ))); } + + #[test] + fn tags_correct() { + let protocols = &[ + Protocol::Dns(Cow::Borrowed("")), + Protocol::Dns4(Cow::Borrowed("")), + Protocol::Dns6(Cow::Borrowed("")), + Protocol::Dnsaddr(Cow::Borrowed("")), + Protocol::Ip4(Ipv4Addr::new(1, 1, 1, 1)), + Protocol::Ip6(Ipv6Addr::new(1, 1, 1, 1, 1, 1, 1, 1)), + Protocol::P2p(PeerId::random()), + ]; + assert_eq!(protocols.len(), NON_PROTOCOL_TAGS.len()); + protocols + .iter() + .map(|e| e.tag()) + .zip(NON_PROTOCOL_TAGS.iter()) + .for_each(|(a, b)| assert_eq!(a, *b)); + } } From 5570b605156a82b493e274268803a4739dc7efc6 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:06:00 +0200 Subject: [PATCH 007/179] Nevermind let's use HashMap --- swarm/src/listener_presence.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs index 461c4cc88ec..5c975468c52 100644 --- a/swarm/src/listener_presence.rs +++ b/swarm/src/listener_presence.rs @@ -1,11 +1,10 @@ //! The listener presence module allows for easy and fast checking if there is a listener present //! for a multiaddress we want to dial. +use std::collections::HashMap; use libp2p_core::Multiaddr; -use smallvec::SmallVec; -use std::collections::BTreeMap; -type ProtocolStack = SmallVec<[&'static str; 4]>; +type ProtocolStack = Vec<&'static str>; // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's supposed to be and it's a protocol and an address. But since I wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just ignore that. // We might also choose to ignore encryption, like noise and tls @@ -26,7 +25,7 @@ fn clean_multiaddr(address: &Multiaddr) -> ProtocolStack { #[derive(Debug, Clone, Default)] pub(super) struct ListenerPresence { - inner: BTreeMap, + inner: HashMap, } #[cfg(test)] From cfd8786e8c7d8077ae1c4668f867cd52cca9b100 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:18:19 +0200 Subject: [PATCH 008/179] Resolved conflict --- swarm/src/listener_presence.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs index 5c975468c52..db039524891 100644 --- a/swarm/src/listener_presence.rs +++ b/swarm/src/listener_presence.rs @@ -53,18 +53,19 @@ impl ListenerPresence { pub fn new_listener(&mut self, address: &Multiaddr) { self.inner .entry(clean_multiaddr(address)) - .and_modify(|e| *e += 1) + .and_modify(|e| *e = e.saturating_add(1)) .or_insert(1); } pub fn expired_listener(&mut self, address: &Multiaddr) { let protocol_stack = clean_multiaddr(address); match self.inner.get_mut(&protocol_stack) { - Some(1) => { - self.inner.remove(&protocol_stack); - } + Some(0) => panic!("The value associated with a ProtocolStack should never be zero"), Some(n) => { *n -= 1; + if n == 0 { + self.inner.remove(&protocol_stack); + } } None => {} } @@ -125,7 +126,10 @@ mod tests { assert!(build_up_address .iter() .all(|addr| listener_presence.contains(addr))); - assert_eq!(clean_multiaddr(&build_up_address[0]), clean_multiaddr(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); + assert_eq!( + clean_multiaddr(&build_up_address[0]), + clean_multiaddr(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16))) + ); assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); assert!(listener_presence.contains(&multiaddr!( Dns4("libp2p.io"), From 31ad325bda6f9e2ffb511a9ea7ec6a3a90297b71 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:19:17 +0200 Subject: [PATCH 009/179] Little error --- swarm/src/listener_presence.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs index db039524891..88b4f2b9f3b 100644 --- a/swarm/src/listener_presence.rs +++ b/swarm/src/listener_presence.rs @@ -1,8 +1,8 @@ //! The listener presence module allows for easy and fast checking if there is a listener present //! for a multiaddress we want to dial. -use std::collections::HashMap; use libp2p_core::Multiaddr; +use std::collections::HashMap; type ProtocolStack = Vec<&'static str>; @@ -63,7 +63,7 @@ impl ListenerPresence { Some(0) => panic!("The value associated with a ProtocolStack should never be zero"), Some(n) => { *n -= 1; - if n == 0 { + if *n == 0 { self.inner.remove(&protocol_stack); } } From dbd32776bd8684c1734a82b1d9e39336969e9051 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:41:47 +0200 Subject: [PATCH 010/179] Write more comments and switch to u16. --- swarm/src/listener_presence.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs index 88b4f2b9f3b..2b1e3036554 100644 --- a/swarm/src/listener_presence.rs +++ b/swarm/src/listener_presence.rs @@ -6,12 +6,18 @@ use std::collections::HashMap; type ProtocolStack = Vec<&'static str>; -// I'm not sure about the selection here. A good example is Onion. It's not good defined what that's supposed to be and it's a protocol and an address. But since I wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just ignore that. -// We might also choose to ignore encryption, like noise and tls +// I'm not sure about the selection here. A good example is Onion. It's not good defined what that's +// supposed to be and it's a protocol and an address. But since I (https://github.com/umgefahren) +// wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just +// ignore that. +// We might also choose to ignore encryption, like noise and tls. +// We might also consider the memory transport. const NON_PROTOCOL_TAGS: &[&str] = &["dns", "dns4", "dns6", "dnsaddr", "ip4", "ip6", "p2p"]; +// We check weather a Protocol tag is a network protocol like quic, tcp and udp. fn is_not_protocol(tag: &str) -> bool { - // using contains instead of matches! isn't a lot different, when we look at the generated assembly (https://godbolt.org/z/1x9f3K16x) + // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated + // assembly. https://godbolt.org/z/1x9f3K16x !NON_PROTOCOL_TAGS.contains(&tag) } @@ -25,7 +31,11 @@ fn clean_multiaddr(address: &Multiaddr) -> ProtocolStack { #[derive(Debug, Clone, Default)] pub(super) struct ListenerPresence { - inner: HashMap, + // We use a u16 because this is primarily target towards transports that use ports and this + // is implies that at max 2^16 - 1 ports can be listened on. While it's still possible to create + // a pathological example where the u16 would still overflow, but it's very unlikely to occur in + // the wild. + inner: HashMap, } #[cfg(test)] @@ -41,27 +51,32 @@ impl<'a> FromIterator<&'a Multiaddr> for ListenerPresence { } impl ListenerPresence { + /// Check weather an address we want to dial already has a listener with the same ProtocolStack. pub fn contains(&self, address: &Multiaddr) -> bool { let protocol_stack = clean_multiaddr(address); - #[cfg(debug_assertions)] if let Some(key) = self.inner.get(&protocol_stack) { debug_assert_ne!(*key, 0, "The entry value should never be zero and exist."); } self.inner.contains_key(&protocol_stack) } + /// Add a new listener to the collection. pub fn new_listener(&mut self, address: &Multiaddr) { self.inner .entry(clean_multiaddr(address)) - .and_modify(|e| *e = e.saturating_add(1)) + .and_modify(|e| *e += 1) .or_insert(1); } + /// Remove an expired listener to the collection. pub fn expired_listener(&mut self, address: &Multiaddr) { let protocol_stack = clean_multiaddr(address); match self.inner.get_mut(&protocol_stack) { - Some(0) => panic!("The value associated with a ProtocolStack should never be zero"), Some(n) => { + debug_assert_ne!( + 0, *n, + "The protocol stack counter should never be zero and exist." + ); *n -= 1; if *n == 0 { self.inner.remove(&protocol_stack); From 6c11a8b50af2bb37a4269f2e9fa64903039ffa6d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:17:40 +0200 Subject: [PATCH 011/179] extremly reduced complexity --- swarm/src/lib.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 398c4f01dd4..0d6ba54cef3 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -152,7 +152,6 @@ use libp2p_identity::PeerId; use smallvec::SmallVec; -use crate::listener_presence::ListenerPresence; use std::collections::{HashMap, HashSet}; use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; use std::time::Duration; @@ -354,9 +353,6 @@ where /// Multiaddresses that our listeners are listening on, listened_addrs: HashMap>, - /// Listener presence monitor - listener_presence: ListenerPresence, - /// Pending event to be delivered to connection handlers /// (or dropped if the peer disconnected) before the `behaviour` /// can be polled again. @@ -511,7 +507,11 @@ where Ok(address) => { let transport_dial_opts = { let transport_port_use = dial_opts.port_use().unwrap_or_else(|| { - if self.listener_presence.contains(&address) { + let is_listening_on_same_transport = self + .listeners() + .map(clean_multiaddr) + .any(|e| e == clean_multiaddr(&address)); + if is_listening_on_same_transport { PortUse::Reuse } else { PortUse::New @@ -977,7 +977,6 @@ where if !addrs.contains(&listen_addr) { addrs.push(listen_addr.clone()) } - self.listener_presence.new_listener(&listen_addr); self.behaviour .on_swarm_event(FromSwarm::NewListenAddr(NewListenAddr { listener_id, @@ -1000,7 +999,6 @@ where if let Some(addrs) = self.listened_addrs.get_mut(&listener_id) { addrs.retain(|a| a != &listen_addr); } - self.listener_presence.expired_listener(&listen_addr); self.behaviour .on_swarm_event(FromSwarm::ExpiredListenAddr(ExpiredListenAddr { listener_id, @@ -1018,7 +1016,6 @@ where log::debug!("Listener {:?}; Closed by {:?}.", listener_id, reason); let addrs = self.listened_addrs.remove(&listener_id).unwrap_or_default(); for addr in addrs.iter() { - self.listener_presence.expired_listener(addr); self.behaviour.on_swarm_event(FromSwarm::ExpiredListenAddr( ExpiredListenAddr { listener_id, addr }, )); @@ -1563,7 +1560,6 @@ where supported_protocols: Default::default(), confirmed_external_addr: Default::default(), listened_addrs: HashMap::new(), - listener_presence: Default::default(), pending_event: None, } } @@ -1840,6 +1836,29 @@ fn p2p_addr(peer: Option, addr: Multiaddr) -> Result bool { + // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated + // assembly. https://godbolt.org/z/1x9f3K16x + !NON_PROTOCOL_TAGS.contains(&tag) +} + +// Turns a multiaddress into a vector of just the protocols. +fn clean_multiaddr(address: &Multiaddr) -> Vec<&'static str> { + address + .protocol_stack() + .filter(|e| is_not_protocol(e)) + .collect() +} + #[cfg(test)] mod tests { use super::*; From 9aa1df2ca62ebc3ca26db3038977df95a567ac70 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:17:40 +0200 Subject: [PATCH 012/179] extremly reduced complexity and removed the now unnecessary files --- swarm/src/listener_presence.rs | 190 --------------------------------- 1 file changed, 190 deletions(-) delete mode 100644 swarm/src/listener_presence.rs diff --git a/swarm/src/listener_presence.rs b/swarm/src/listener_presence.rs deleted file mode 100644 index 2b1e3036554..00000000000 --- a/swarm/src/listener_presence.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! The listener presence module allows for easy and fast checking if there is a listener present -//! for a multiaddress we want to dial. - -use libp2p_core::Multiaddr; -use std::collections::HashMap; - -type ProtocolStack = Vec<&'static str>; - -// I'm not sure about the selection here. A good example is Onion. It's not good defined what that's -// supposed to be and it's a protocol and an address. But since I (https://github.com/umgefahren) -// wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just -// ignore that. -// We might also choose to ignore encryption, like noise and tls. -// We might also consider the memory transport. -const NON_PROTOCOL_TAGS: &[&str] = &["dns", "dns4", "dns6", "dnsaddr", "ip4", "ip6", "p2p"]; - -// We check weather a Protocol tag is a network protocol like quic, tcp and udp. -fn is_not_protocol(tag: &str) -> bool { - // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated - // assembly. https://godbolt.org/z/1x9f3K16x - !NON_PROTOCOL_TAGS.contains(&tag) -} - -// Turns a multiaddress into a vector of just the protocols. -fn clean_multiaddr(address: &Multiaddr) -> ProtocolStack { - address - .protocol_stack() - .filter(|e| is_not_protocol(e)) - .collect() -} - -#[derive(Debug, Clone, Default)] -pub(super) struct ListenerPresence { - // We use a u16 because this is primarily target towards transports that use ports and this - // is implies that at max 2^16 - 1 ports can be listened on. While it's still possible to create - // a pathological example where the u16 would still overflow, but it's very unlikely to occur in - // the wild. - inner: HashMap, -} - -#[cfg(test)] -impl<'a> FromIterator<&'a Multiaddr> for ListenerPresence { - fn from_iter>(iter: T) -> Self { - let inner = iter - .into_iter() - .map(clean_multiaddr) - .map(|key| (key, 1)) - .collect(); - Self { inner } - } -} - -impl ListenerPresence { - /// Check weather an address we want to dial already has a listener with the same ProtocolStack. - pub fn contains(&self, address: &Multiaddr) -> bool { - let protocol_stack = clean_multiaddr(address); - if let Some(key) = self.inner.get(&protocol_stack) { - debug_assert_ne!(*key, 0, "The entry value should never be zero and exist."); - } - self.inner.contains_key(&protocol_stack) - } - - /// Add a new listener to the collection. - pub fn new_listener(&mut self, address: &Multiaddr) { - self.inner - .entry(clean_multiaddr(address)) - .and_modify(|e| *e += 1) - .or_insert(1); - } - - /// Remove an expired listener to the collection. - pub fn expired_listener(&mut self, address: &Multiaddr) { - let protocol_stack = clean_multiaddr(address); - match self.inner.get_mut(&protocol_stack) { - Some(n) => { - debug_assert_ne!( - 0, *n, - "The protocol stack counter should never be zero and exist." - ); - *n -= 1; - if *n == 0 { - self.inner.remove(&protocol_stack); - } - } - None => {} - } - } -} - -// Some tests with real world data would be appreciated -#[cfg(test)] -mod tests { - use crate::listener_presence::{clean_multiaddr, ListenerPresence, NON_PROTOCOL_TAGS}; - use libp2p_core::multiaddr::{multiaddr, Protocol}; - use libp2p_identity::PeerId; - use std::borrow::Cow; - use std::net::{Ipv4Addr, Ipv6Addr}; - use std::str::FromStr; - - #[test] - fn basic_ops() { - let bootstrap_libp2p_node_peer_id = - PeerId::from_str("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN").unwrap(); - let test_addrs = [ - multiaddr!(Ip4([127, 0, 0, 1]), Tcp(1234u16)), - multiaddr!( - Ip6([11, 22, 33, 44, 55, 66, 77, 88]), - Udp(199u16), - Tls, - Quic - ), - multiaddr!(Dns4("heise.de"), Tcp(443u16), Tls, Https), - multiaddr!( - Dnsaddr("bootstrap.libp2p.io"), - P2p(bootstrap_libp2p_node_peer_id) - ), - multiaddr!( - Ip4([104, 131, 131, 82]), - Udp(4001u16), - Quic, - P2p(bootstrap_libp2p_node_peer_id) - ), - ]; - let listener_presence: ListenerPresence = test_addrs.iter().collect(); - assert!( - test_addrs - .iter() - .all(|addr| listener_presence.contains(addr)), - "Basic input operations are not working. Likely cleaning function is not pure." - ); - } - - #[test] - fn reducing_functionality() { - let build_up_address = [ - multiaddr!(Dnsaddr("libp2p.io"), Tls, Tcp(10u16)), - multiaddr!(Dnsaddr("libp2p.io"), Tls, Tcp(12u16), Udp(13u16), Quic), - multiaddr!(Ip4([1, 1, 1, 1]), Udp(100u16)), - ]; - let listener_presence: ListenerPresence = build_up_address.iter().collect(); - assert!(build_up_address - .iter() - .all(|addr| listener_presence.contains(addr))); - assert_eq!( - clean_multiaddr(&build_up_address[0]), - clean_multiaddr(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16))) - ); - assert!(listener_presence.contains(&multiaddr!(Dns4("libp2p.io"), Tls, Tcp(10u16)))); - assert!(listener_presence.contains(&multiaddr!( - Dns4("libp2p.io"), - Tls, - Tcp(10u16), - Dnsaddr("bootstrap.libp2p.io") - ))); - assert!(listener_presence.contains(&multiaddr!(Dns("one.one.one.one"), Tls, Tcp(100u16)))); - assert!(!listener_presence.contains(&multiaddr!(Dns("one.one.one.one"), Tcp(100u16)))); - assert!(!listener_presence.contains(&multiaddr!(Dnsaddr("libp2p.io"), Tcp(10u16), Tls))); - assert!(!listener_presence.contains(&multiaddr!( - Dnsaddr("libp2p.io"), - Quic, - Udp(13u16), - Tcp(12u16), - Tls - ))); - assert!(!listener_presence.contains(&multiaddr!( - Dnsaddr("one.one.one.one"), - Udp(100u16), - Tls - ))); - } - - #[test] - fn tags_correct() { - let protocols = &[ - Protocol::Dns(Cow::Borrowed("")), - Protocol::Dns4(Cow::Borrowed("")), - Protocol::Dns6(Cow::Borrowed("")), - Protocol::Dnsaddr(Cow::Borrowed("")), - Protocol::Ip4(Ipv4Addr::new(1, 1, 1, 1)), - Protocol::Ip6(Ipv6Addr::new(1, 1, 1, 1, 1, 1, 1, 1)), - Protocol::P2p(PeerId::random()), - ]; - assert_eq!(protocols.len(), NON_PROTOCOL_TAGS.len()); - protocols - .iter() - .map(|e| e.tag()) - .zip(NON_PROTOCOL_TAGS.iter()) - .for_each(|(a, b)| assert_eq!(a, *b)); - } -} From 0fbe7e7b22cb22fb77a2aba72519ce472b92221b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:17:40 +0200 Subject: [PATCH 013/179] extremly reduced complexity and removed the now unnecessary files --- swarm/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 0d6ba54cef3..f070f1ac7b1 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -72,7 +72,6 @@ pub mod handler; )] pub mod keep_alive; mod listen_opts; -mod listener_presence; /// Bundles all symbols required for the [`libp2p_swarm_derive::NetworkBehaviour`] macro. #[doc(hidden)] From d7a0fb4ae25166fc7c2c57f0a8864b3cc089d243 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:26:20 +0200 Subject: [PATCH 014/179] Merge into single function --- swarm/src/lib.rs | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index f070f1ac7b1..cdc1ed84392 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -1835,27 +1835,32 @@ fn p2p_addr(peer: Option, addr: Multiaddr) -> Result bool { - // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated - // assembly. https://godbolt.org/z/1x9f3K16x - !NON_PROTOCOL_TAGS.contains(&tag) -} +fn any_with_same_transport( + mut haystack: impl Iterator, + needle: &Multiaddr, +) -> bool { + // Turns a multiaddress into an iterator of just the protocols. + fn clean_multiaddr(address: &Multiaddr) -> impl Iterator { + // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's + // supposed to be and it's a protocol and an address. But since I (https://github.com/umgefahren) + // wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just + // ignore that. + // We might also choose to ignore encryption, like noise and tls. + // We might also consider the memory transport. + const NON_PROTOCOL_TAGS: &[&'static str] = + &["dns", "dns4", "dns6", "dnsaddr", "ip4", "ip6", "p2p"]; + + // We check weather a Protocol tag is a network protocol like quic, tcp and udp. + fn is_not_protocol(tag: &'static str) -> bool { + // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated + // assembly. https://godbolt.org/z/1x9f3K16x + !NON_PROTOCOL_TAGS.contains(&tag) + } + + address.protocol_stack().filter(is_not_protocol) + } -// Turns a multiaddress into a vector of just the protocols. -fn clean_multiaddr(address: &Multiaddr) -> Vec<&'static str> { - address - .protocol_stack() - .filter(|e| is_not_protocol(e)) - .collect() + haystack.any(|e| clean_multiaddr(e).eq(clean_multiaddr(needle))) } #[cfg(test)] From a33cb6ba82dd43b2e0b96c13a5887db854026a00 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 6 Oct 2023 17:45:02 +0200 Subject: [PATCH 015/179] Add first tests --- swarm/src/lib.rs | 74 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index cdc1ed84392..f70344a0126 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -506,10 +506,8 @@ where Ok(address) => { let transport_dial_opts = { let transport_port_use = dial_opts.port_use().unwrap_or_else(|| { - let is_listening_on_same_transport = self - .listeners() - .map(clean_multiaddr) - .any(|e| e == clean_multiaddr(&address)); + let is_listening_on_same_transport = + any_with_same_transport(self.listeners(), &address); if is_listening_on_same_transport { PortUse::Reuse } else { @@ -1835,12 +1833,12 @@ fn p2p_addr(peer: Option, addr: Multiaddr) -> Result, +fn any_with_same_transport<'a>( + mut haystack: impl Iterator, needle: &Multiaddr, ) -> bool { // Turns a multiaddress into an iterator of just the protocols. - fn clean_multiaddr(address: &Multiaddr) -> impl Iterator { + fn clean_multiaddr(address: &Multiaddr) -> impl Iterator + '_ { // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's // supposed to be and it's a protocol and an address. But since I (https://github.com/umgefahren) // wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just @@ -1851,7 +1849,7 @@ fn any_with_same_transport( &["dns", "dns4", "dns6", "dnsaddr", "ip4", "ip6", "p2p"]; // We check weather a Protocol tag is a network protocol like quic, tcp and udp. - fn is_not_protocol(tag: &'static str) -> bool { + fn is_not_protocol(tag: &&'static str) -> bool { // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated // assembly. https://godbolt.org/z/1x9f3K16x !NON_PROTOCOL_TAGS.contains(&tag) @@ -2450,4 +2448,64 @@ mod tests { // Unfortunately, we have some "empty" errors that lead to multiple colons without text but that is the best we can do. assert_eq!("Failed to negotiate transport protocol(s): [(/ip4/127.0.0.1/tcp/80: : No listener on the given port.)]", string) } + + #[cfg(test)] + mod port_use { + use crate::any_with_same_transport; + use crate::Multiaddr; + + macro_rules! test_dial_with_listener { + ($test_name:ident, $listen_addrs:expr, $combination:expr, $test_explanation:literal) => { + #[test] + fn $test_name() { + let test_explanation = $test_explanation; + let listen_addrs: Vec = $listen_addrs.into_iter().map(|e| e.parse().unwrap()).collect(); + $combination.into_iter().map(|(dial_addr_str, expected_output)| (dial_addr_str.parse::().unwrap(), expected_output)).for_each(|(dial_addr, expected_output)| { + let should_port_reuse = any_with_same_transport(listen_addrs.iter(), &dial_addr); + if expected_output { + assert!(should_port_reuse, "Port should have been reused, since there exists at least one listener in {listen_addrs:?} for {dial_addr:?}. Details: {test_explanation}") + } else { + assert!(!should_port_reuse, "Port should not have been reused, since there doesn't exist a listener in {listen_addrs:?} for {dial_addr:?}. Details: {test_explanation}"); + } + }) + } + }; + } + + const BASIC_LISTEN_ADDRS: [&str; 6] = [ + "/ip4/127.0.0.1", + "/ip6/::1", + "/ip4/127.0.0.1/tcp/800/tls/ws", + "/ip4/127.0.0.1/udp/443/quic", + "/ip4/127.0.0.1/udp/443/quic-v1", + "/dns/bootstarp.libp2p.io/tcp/80/tls/ws/tls", + ]; + + test_dial_with_listener!( + reuse_if_dial_to_self, + BASIC_LISTEN_ADDRS, + BASIC_LISTEN_ADDRS.map(|addr| (addr, true)), + "If this test fails there is likely a major problem, because we won't reuse ports to dial ourselves." + ); + + test_dial_with_listener!( + no_reuse_ip4_listener_for_ip6, + [ + "/ip4/198.51.100.30/tcp/1234", + "/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + "/ip4/127.0.0.1/udp/1234/quic-v1", + "/ip4/12.34.56.78/udp/4321/quic", + "/ip4/102.0.2.0/udp/1234/quic/webtransport" + ], + [ + ("/ip4/198.51.100.50/tcp/42", true), + ("/ip6/2001:1380:2000:7a00::1/tcp/4001", false), + ("/ip6/fe80::883:a581:fff1:833/tcp/4002/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", false), + ("/ip6/2601:9:4f82:5fff:aefd:ecff:fe0b:7cfe/udp/2023/quic-v1", false), + ("/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/udp/4321/quic", false), + ("/ip6/1300::1/udp/300/quic/webtransport", false), + ], + "We shouldn't reuse an ip4 listener to dial to an ip6 address." + ); + } } From 35b80026d85061d3dfd5367cf9e05b4a98327e18 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 6 Oct 2023 20:22:21 +0200 Subject: [PATCH 016/179] I wrote so many tests i think i'm gonna be insane --- swarm/src/lib.rs | 175 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index f70344a0126..09609299090 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -2451,11 +2451,17 @@ mod tests { #[cfg(test)] mod port_use { + use std::str::FromStr; + + use libp2p_core::multiaddr::Protocol; + use libp2p_core::multihash::Multihash; + use libp2p_identity::PeerId; + use crate::any_with_same_transport; use crate::Multiaddr; macro_rules! test_dial_with_listener { - ($test_name:ident, $listen_addrs:expr, $combination:expr, $test_explanation:literal) => { + ($test_name:ident, $listen_addrs:expr, $combination:expr, $test_explanation:expr) => { #[test] fn $test_name() { let test_explanation = $test_explanation; @@ -2505,7 +2511,172 @@ mod tests { ("/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/udp/4321/quic", false), ("/ip6/1300::1/udp/300/quic/webtransport", false), ], - "We shouldn't reuse an ip4 listener to dial to an ip6 address." + "We shouldn't reuse an ip4 listener to dial to an ip6 address." + ); + + test_dial_with_listener!( + no_reuse_ip6_listener_for_ip4, + [ + "/ip6/2001:1380:2000:7a00::1/tcp/4001", + "/ip6/fe80::883:a581:fff1:833/tcp/4002/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + "/ip6/2601:9:4f82:5fff:aefd:ecff:fe0b:7cfe/udp/2023/quic-v1", + "/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/udp/4321/quic", + "/ip6/1300::1/udp/300/quic/webtransport", + ], + [ + ("/ip6/2001:1380:2000:7a00::1/tcp/4001", true), + ("/ip4/198.51.100.30/tcp/1234", false), + ("/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", false), + ("/ip4/127.0.0.1/udp/1234/quic-v1", false), + ("/ip4/12.34.56.78/udp/4321/quic", false), + ("/ip4/102.0.2.0/udp/1234/quic/webtransport", false) + ], + "We shouldn't reuse an ip6 listener to dial to an ip4 address." + ); + + const DNS_AND_IP_ADDRS: [&'static str; 6] = [ + "/ip4/143.244.56.51/tcp/4001", + "/ip6/2a02:2e0:3fe:1001:302::/tcp/4001", + "/dns/protocol.ai/tcp/4001", + "/dnsaddr/protocol.ai/tcp/4001", + "/dns4/protocol.ai/tcp/4001", + "/dns6/protocol.ai/tcp/4001", + ]; + + test_dial_with_listener!( + no_reuse_since_dns_is_ambigious, + ["/dns/bootstrap.libp2p.io/tcp/4001",], + DNS_AND_IP_ADDRS.map(|addr| (addr, false)), + "We can't know wheather a dns address resolves to an ip4 or ip6 address." + ); + + test_dial_with_listener!( + no_reuse_since_dnsaddr_is_ambigious, + ["/dnsaddr/bootstrap.libp2p.io/tcp/4001"], + DNS_AND_IP_ADDRS.map(|addr| (addr, false)), + "We can't know wheather a dns address resolves to an ip4 or ip6 address." + ); + + test_dial_with_listener!( + reuse_since_dns4_resolves_to_ip4, + ["/dns4/libp2p.io/tcp/80", "/ip4/185.93.3.233/udp/443/quic"], + [ + ("/ip4/185.93.3.233/tcp/80", true), + ("/ip6/2400:52e0:1e00::1080:1/tcp/80", false), + ("/dns4/libp2p.io/udp/443/quic", true), + ("/dns6/libp2p.io/udp/443/quic", false), + ], + "We know that dns4 resolves to an ip4 address and vice versa." + ); + + test_dial_with_listener!( + reuse_since_dns6_resolves_to_ip6, + [ + "/dns6/libp2p.io/tcp/80", + "/ip6/2400:52e0:1e00::1080:1/udp/443/quic", + ], + [ + ("/ip6/2400:52e0:1e00::1080:1/tcp/80", true), + ("/ip4/185.93.3.233/tcp/80", false), + ("/dns6/libp2p.io/udp/443/quic", true), + ("/dns4/libp2p.io/udp/443/quic", false), + ], + "We know that dns6 resolves to an ip6 address and vice versa." + ); + + const IP_DIFFERENT_PROTOCOLS: [&str; 4] = [ + "/ip4/1.1.1.1/tcp/80", + "/ip4/1.1.1.1/udp/443/quic", + "/ip4/1.1.1.1/udp/443/quic-v1", + "/ip4/1.1.1.1/udp/1234/webrtc-direct/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + ]; + + macro_rules! no_reuse_since_no_protocol_listener { + ($fun_name:ident, $protocol:literal) => { + test_dial_with_listener!( + $fun_name, + IP_DIFFERENT_PROTOCOLS + .into_iter() + .filter(|e| !e.contains($protocol)), + IP_DIFFERENT_PROTOCOLS + .into_iter() + .filter(|e| e.contains($protocol)) + .map(|addr| (addr, false)), + concat!( + "We can't reuse a port since there exists no ", + $protocol, + "listener yet." + ) + ); + }; + } + + no_reuse_since_no_protocol_listener!(no_reuse_since_no_tcp_listener, "tcp"); + no_reuse_since_no_protocol_listener!(no_reuse_since_no_quic_listener, "quic"); + no_reuse_since_no_protocol_listener!(no_reuse_since_no_quicv1_listener, "quic-v1"); + no_reuse_since_no_protocol_listener!( + no_reuse_since_no_webrtc_direct_listener, + "webrtc-direct" + ); + + const LISTEN_IP4_ADDRS: [&str; 4] = [ + "/ip4/127.0.0.1", + "/ip4/1.1.1.1", + "/dns4/libp2p.io", + "/dns4/filecoin.io", + ]; + const DIAL_IP4_ADDRS: [&str; 4] = [ + "/ip4/39.13.30.9", + "/ip4/8.8.8.8", + "/dns4/bootstrap.libp2p.io", + "/dns4/filecoin.io", + ]; + + macro_rules! reuse_same_protocol_different_ip { + ($fun_name:ident, $protocol_name:literal, $($protocol:expr),+) => { + test_dial_with_listener!( + $fun_name, + LISTEN_IP4_ADDRS.map(|e| { + let mut addr = e.parse::().unwrap(); + $( + addr.push($protocol); + )+ + addr.to_string() + }), + DIAL_IP4_ADDRS.map(|e| { + let mut addr = e.parse::().unwrap(); + $( + addr.push($protocol); + )+ + (addr.to_string(), true) + }), + concat!( + "We can reuse a port, since there exists a listener with different ip but same protocol (", $protocol_name, ")." + ) + ); + }; + } + reuse_same_protocol_different_ip!(reuse_since_tcp_listener, "tcp", Protocol::Tcp(800)); + reuse_same_protocol_different_ip!( + reuse_since_quic_listener, + "quic", + Protocol::Udp(10), + Protocol::Quic + ); + reuse_same_protocol_different_ip!( + reuse_since_quicv1_listener, + "quic-v1", + Protocol::Udp(20), + Protocol::QuicV1 + ); + reuse_same_protocol_different_ip!( + reuse_since_webrtc_direct_listener, + "webrtc-direct", + Protocol::WebRTCDirect, + Protocol::Certhash(Multihash::<64>::wrap(0x12, b"umgefahren was here").unwrap()), + Protocol::P2p( + PeerId::from_str("QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb").unwrap() + ) ); } } From 283c4e60757370b0481d2274e2bf86a4c83bd193 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:01:06 +0200 Subject: [PATCH 017/179] Make tests pass --- swarm/src/lib.rs | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 09609299090..7116d34f2b6 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -123,6 +123,7 @@ pub use handler::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerSelect, KeepAlive, OneShotHandler, OneShotHandlerConfig, StreamUpgradeError, SubstreamProtocol, }; +use libp2p_core::multiaddr::Protocol; #[cfg(feature = "macros")] pub use libp2p_swarm_derive::NetworkBehaviour; pub use listen_opts::ListenOpts; @@ -1837,16 +1838,22 @@ fn any_with_same_transport<'a>( mut haystack: impl Iterator, needle: &Multiaddr, ) -> bool { + // it's ambigious wheather a dns or dnsaddr resolves to an ipv4 or ipv6 address. To be sure we + // default to using a new port. + if needle + .iter() + .any(|p| matches!(p, Protocol::Dns(_) | Protocol::Dnsaddr(_))) + { + return false; + } + // Turns a multiaddress into an iterator of just the protocols. fn clean_multiaddr(address: &Multiaddr) -> impl Iterator + '_ { // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's // supposed to be and it's a protocol and an address. But since I (https://github.com/umgefahren) - // wrote the only Tor transport for libp2p and it doesn't support these Onion addresses, I will just + // wrote the only Tor transport for rust-libp2p and it doesn't support these Onion addresses, I will just // ignore that. - // We might also choose to ignore encryption, like noise and tls. - // We might also consider the memory transport. - const NON_PROTOCOL_TAGS: &[&'static str] = - &["dns", "dns4", "dns6", "dnsaddr", "ip4", "ip6", "p2p"]; + const NON_PROTOCOL_TAGS: &[&'static str] = &["p2p"]; // We check weather a Protocol tag is a network protocol like quic, tcp and udp. fn is_not_protocol(tag: &&'static str) -> bool { @@ -1855,7 +1862,28 @@ fn any_with_same_transport<'a>( !NON_PROTOCOL_TAGS.contains(&tag) } - address.protocol_stack().filter(is_not_protocol) + fn map_protocol(tag: &'static str) -> &'static str { + debug_assert_ne!( + tag, "dnsaddr", + "The case of dnsaddress should already have been ignored since it's ambigious." + ); + debug_assert_eq!( + tag, "dns", + "The case of dns should already have been ignored." + ); + match tag { + // DNS4 resolves to an IPv4 address + "dns4" => "ip4", + // DNS6 resolves to an IPv6 address + "dns6" => "ip6", + _ => tag, + } + } + + address + .protocol_stack() + .filter(is_not_protocol) + .map(map_protocol) } haystack.any(|e| clean_multiaddr(e).eq(clean_multiaddr(needle))) @@ -2478,13 +2506,14 @@ mod tests { }; } - const BASIC_LISTEN_ADDRS: [&str; 6] = [ + const BASIC_LISTEN_ADDRS: [&str; 7] = [ "/ip4/127.0.0.1", "/ip6/::1", "/ip4/127.0.0.1/tcp/800/tls/ws", "/ip4/127.0.0.1/udp/443/quic", "/ip4/127.0.0.1/udp/443/quic-v1", - "/dns/bootstarp.libp2p.io/tcp/80/tls/ws/tls", + "/dns4/bootstrap.libp2p.io/tcp/80/tls/ws/tls", + "/dns6/bootstrap.libp2p.io/tcp/443/tls/ws/tls", ]; test_dial_with_listener!( From b7dec2629c75cf34fe0e27d901d021459af8ae12 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:08:19 +0200 Subject: [PATCH 018/179] Remove major mistake --- swarm/src/lib.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 7116d34f2b6..193bbb3a8a1 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -1834,6 +1834,7 @@ fn p2p_addr(peer: Option, addr: Multiaddr) -> Result( mut haystack: impl Iterator, needle: &Multiaddr, @@ -1863,14 +1864,6 @@ fn any_with_same_transport<'a>( } fn map_protocol(tag: &'static str) -> &'static str { - debug_assert_ne!( - tag, "dnsaddr", - "The case of dnsaddress should already have been ignored since it's ambigious." - ); - debug_assert_eq!( - tag, "dns", - "The case of dns should already have been ignored." - ); match tag { // DNS4 resolves to an IPv4 address "dns4" => "ip4", From 9053dc7bd38468b5e33c6bc9ea681b522b6f56ae Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:52:38 +0200 Subject: [PATCH 019/179] Implement discussed changes --- core/src/connection.rs | 9 +- core/src/either.rs | 22 --- core/src/transport.rs | 30 +-- core/src/transport/and_then.rs | 24 +-- core/src/transport/boxed.rs | 15 -- core/src/transport/choice.rs | 23 --- core/src/transport/dummy.rs | 7 - core/src/transport/global_only.rs | 26 --- core/src/transport/map.rs | 15 -- core/src/transport/map_err.rs | 14 -- core/src/transport/memory.rs | 2 +- core/src/transport/optional.rs | 11 -- core/src/transport/timeout.rs | 14 -- core/src/transport/upgrade.rs | 21 --- swarm/src/connection/pool.rs | 1 + swarm/src/dial_opts.rs | 34 +--- swarm/src/lib.rs | 292 +----------------------------- 17 files changed, 32 insertions(+), 528 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 4c6418a6c9c..ded8eb65e32 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -18,7 +18,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::multiaddr::{Multiaddr, Protocol}; +use crate::{ + multiaddr::{Multiaddr, Protocol}, + transport::PortUse, +}; /// The endpoint roles associated with a peer-to-peer communication channel. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] @@ -81,6 +84,9 @@ pub enum ConnectedPoint { /// connection as a dialer and one peer dial the other and upgrade the /// connection _as a listener_ overriding its role. role_override: Endpoint, + /// Whether the port for the outgoing connection was reused from a listener + /// or a new port was allocated. This is useful for address translation. + port_use: PortUse, }, /// We received the node. Listener { @@ -134,6 +140,7 @@ impl ConnectedPoint { ConnectedPoint::Dialer { address, role_override: _, + port_use: _, } => address, ConnectedPoint::Listener { local_addr, .. } => local_addr, } diff --git a/core/src/either.rs b/core/src/either.rs index d8fbe047099..4e89c6227ee 100644 --- a/core/src/either.rs +++ b/core/src/either.rs @@ -193,28 +193,6 @@ where } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> - where - Self: Sized, - { - use TransportError::*; - match self { - Either::Left(a) => match a.dial_as_listener(addr) { - Ok(connec) => Ok(EitherFuture::First(connec)), - Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), - Err(Other(err)) => Err(Other(Either::Left(err))), - }, - Either::Right(b) => match b.dial_as_listener(addr) { - Ok(connec) => Ok(EitherFuture::Second(connec)), - Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), - Err(Other(err)) => Err(Other(Either::Right(err))), - }, - } - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { match self { Either::Left(a) => a.address_translation(server, observed), diff --git a/core/src/transport.rs b/core/src/transport.rs index e3f1a61b42c..5dc74f67c91 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -57,13 +57,20 @@ pub use self::optional::OptionalTransport; pub use self::upgrade::Upgrade; static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1); -#[derive(Debug, Default, Copy, Clone)] + +/// Defines the port use policy. Decides whether to reuse an existing port of a listener +/// or to allocate a new one. +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)] pub enum PortUse { - #[default] + /// Always allocate a new port for the dial. New, + /// Best effor reusing of the port. If there is no listener present that can be used to dial, + /// a new port is allocated. + #[default] Reuse, } -#[derive(Debug, Default, Copy, Clone)] + +#[derive(Debug, Copy, Clone)] pub struct DialOpts { pub endpoint: Endpoint, pub port_use: PortUse, @@ -146,23 +153,6 @@ pub trait Transport { opts: DialOpts, ) -> Result>; - /// As [`Transport::dial`] but has the local node act as a listener on the outgoing connection. - /// - /// This option is needed for NAT and firewall hole punching. - /// See [`ConnectedPoint::Dialer`] for related option. - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial( - addr, - DialOpts { - endpoint: Endpoint::Listener, - ..Default::default() - }, - ) - } - /// Poll for [`TransportEvent`]s. /// /// A [`TransportEvent::Incoming`] should be produced whenever a connection is received at the lowest diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index bc5720e5af3..4168176ef17 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -92,28 +92,6 @@ where Ok(future) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - let dialed_fut = self - .transport - .dial_as_listener(addr.clone()) - .map_err(|err| err.map(Either::Left))?; - let future = AndThenFuture { - inner: Either::Left(Box::pin(dialed_fut)), - args: Some(( - self.fun.clone(), - ConnectedPoint::Dialer { - address: addr, - role_override: Endpoint::Listener, - }, - )), - _marker: PhantomPinned, - }; - Ok(future) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { self.transport.address_translation(server, observed) } @@ -183,7 +161,7 @@ where Poll::Ready(Err(err)) => return Poll::Ready(Err(Either::Left(err))), Poll::Pending => return Poll::Pending, }; - let (f, a) = self + let (f, mut a) = self .args .take() .expect("AndThenFuture has already finished."); diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index eeadadbb466..051833ccf5f 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -63,7 +63,6 @@ trait Abstract { addr: Multiaddr, dial_opts: DialOpts, ) -> Result, TransportError>; - fn dial_as_listener(&mut self, addr: Multiaddr) -> Result, TransportError>; fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option; fn poll( self: Pin<&mut Self>, @@ -101,13 +100,6 @@ where Ok(Box::pin(fut) as Dial<_>) } - fn dial_as_listener(&mut self, addr: Multiaddr) -> Result, TransportError> { - let fut = Transport::dial_as_listener(self, addr) - .map(|r| r.map_err(box_err)) - .map_err(|e| e.map(box_err))?; - Ok(Box::pin(fut) as Dial<_>) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { Transport::address_translation(self, server, observed) } @@ -159,13 +151,6 @@ impl Transport for Boxed { self.inner.dial(addr, dial_opts) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.inner.dial_as_listener(addr) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { self.inner.address_translation(server, observed) } diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index d6e143d5c90..33799f8a9ce 100644 --- a/core/src/transport/choice.rs +++ b/core/src/transport/choice.rs @@ -141,29 +141,6 @@ where Err(TransportError::MultiaddrNotSupported(addr)) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - let addr = match self.0.dial_as_listener(addr) { - Ok(connec) => return Ok(EitherFuture::First(connec)), - Err(TransportError::MultiaddrNotSupported(addr)) => addr, - Err(TransportError::Other(err)) => { - return Err(TransportError::Other(Either::Left(err))) - } - }; - - let addr = match self.1.dial_as_listener(addr) { - Ok(connec) => return Ok(EitherFuture::Second(connec)), - Err(TransportError::MultiaddrNotSupported(addr)) => addr, - Err(TransportError::Other(err)) => { - return Err(TransportError::Other(Either::Right(err))) - } - }; - - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { if let Some(addr) = self.0.address_translation(server, observed) { Some(addr) diff --git a/core/src/transport/dummy.rs b/core/src/transport/dummy.rs index 7079308943b..b5375602c29 100644 --- a/core/src/transport/dummy.rs +++ b/core/src/transport/dummy.rs @@ -79,13 +79,6 @@ impl Transport for DummyTransport { Err(TransportError::MultiaddrNotSupported(addr)) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { None } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 7fb45237078..5aa53c91c78 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -316,32 +316,6 @@ impl crate::Transport for Transport { } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - match addr.iter().next() { - Some(Protocol::Ip4(a)) => { - if !ipv4_global::is_global(a) { - debug!("Not dialing non global IP address {:?}.", a); - return Err(TransportError::MultiaddrNotSupported(addr)); - } - self.inner.dial_as_listener(addr) - } - Some(Protocol::Ip6(a)) => { - if !ipv6_global::is_global(a) { - debug!("Not dialing non global IP address {:?}.", a); - return Err(TransportError::MultiaddrNotSupported(addr)); - } - self.inner.dial_as_listener(addr) - } - _ => { - debug!("Not dialing unsupported Multiaddress {:?}.", addr); - Err(TransportError::MultiaddrNotSupported(addr)) - } - } - } - fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { self.inner.address_translation(listen, observed) } diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index ad6e34f7116..8922dc72bc3 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -90,21 +90,6 @@ where }) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - let future = self.transport.dial_as_listener(addr.clone())?; - let p = ConnectedPoint::Dialer { - address: addr, - role_override: Endpoint::Listener, - }; - Ok(MapFuture { - inner: future, - args: Some((self.fun.clone(), p)), - }) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { self.transport.address_translation(server, observed) } diff --git a/core/src/transport/map_err.rs b/core/src/transport/map_err.rs index 6859445882e..20580c23537 100644 --- a/core/src/transport/map_err.rs +++ b/core/src/transport/map_err.rs @@ -80,20 +80,6 @@ where } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - let map = self.map.clone(); - match self.transport.dial_as_listener(addr) { - Ok(future) => Ok(MapErrDial { - inner: future, - map: Some(map), - }), - Err(err) => Err(err.map(map)), - } - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { self.transport.address_translation(server, observed) } diff --git a/core/src/transport/memory.rs b/core/src/transport/memory.rs index ad862a4d4f9..4f6c23c7fea 100644 --- a/core/src/transport/memory.rs +++ b/core/src/transport/memory.rs @@ -222,7 +222,7 @@ impl Transport for MemoryTransport { fn dial( &mut self, addr: Multiaddr, - opts: DialOpts, + _opts: DialOpts, ) -> Result> { let port = if let Ok(port) = parse_memory_addr(&addr) { if let Some(port) = NonZeroU64::new(port) { diff --git a/core/src/transport/optional.rs b/core/src/transport/optional.rs index ee86bff6fd8..f381d98f163 100644 --- a/core/src/transport/optional.rs +++ b/core/src/transport/optional.rs @@ -92,17 +92,6 @@ where } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - if let Some(inner) = self.0.as_mut() { - inner.dial_as_listener(addr) - } else { - Err(TransportError::MultiaddrNotSupported(addr)) - } - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { if let Some(inner) = &self.0 { inner.address_translation(server, observed) diff --git a/core/src/transport/timeout.rs b/core/src/transport/timeout.rs index 1946f76bfa4..1ed4f8a5796 100644 --- a/core/src/transport/timeout.rs +++ b/core/src/transport/timeout.rs @@ -115,20 +115,6 @@ where }) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - let dial = self - .inner - .dial_as_listener(addr) - .map_err(|err| err.map(TransportTimeoutError::Other))?; - Ok(Timeout { - inner: dial, - timer: Delay::new(self.outgoing_timeout), - }) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { self.inner.address_translation(server, observed) } diff --git a/core/src/transport/upgrade.rs b/core/src/transport/upgrade.rs index 5d849042b79..215c85e8709 100644 --- a/core/src/transport/upgrade.rs +++ b/core/src/transport/upgrade.rs @@ -348,13 +348,6 @@ where self.0.remove_listener(id) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.0.dial_as_listener(addr) - } - fn listen_on( &mut self, id: ListenerId, @@ -428,20 +421,6 @@ where self.inner.remove_listener(id) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - let future = self - .inner - .dial_as_listener(addr) - .map_err(|err| err.map(TransportUpgradeError::Transport))?; - Ok(DialUpgradeFuture { - future: Box::pin(future), - upgrade: future::Either::Left(Some(self.upgrade.clone())), - }) - } - fn listen_on( &mut self, id: ListenerId, diff --git a/swarm/src/connection/pool.rs b/swarm/src/connection/pool.rs index b6100989a04..c43d985f97e 100644 --- a/swarm/src/connection/pool.rs +++ b/swarm/src/connection/pool.rs @@ -646,6 +646,7 @@ where ConnectedPoint::Dialer { address, role_override, + port_use, }, Some(errors), ), diff --git a/swarm/src/dial_opts.rs b/swarm/src/dial_opts.rs index ac88bffbe0d..0fb58708db1 100644 --- a/swarm/src/dial_opts.rs +++ b/swarm/src/dial_opts.rs @@ -40,25 +40,11 @@ macro_rules! port_use_and_role_setter { self } - /// Enforce the reuse of an existing port. - /// Default behaviour is: - /// 1. Swarm tracks if there already exists a listener address for this protocol - /// 2. Decide port use behaviour: - /// - If we have 0 listen addresses, we allocate a new port - /// - If we have >1 listen addresses, we reuse an existing port - pub fn reuse_existing_port(mut self) -> Self { - self.port_use = Some(PortUse::Reuse); - self - } - /// Enforce the allocation of a new port. - /// Default behaviour is: - /// 1. Swarm tracks if there already exists a listener address for this protocol - /// 2. Decide port use behaviour: - /// - If we have 0 listen addresses, we allocate a new port - /// - If we have >1 listen addresses, we reuse an existing port + /// Default behaviour is best effort reuse of existing ports. If there is no existing + /// fitting listener, a new port is allocated. pub fn allocate_new_port(mut self) -> Self { - self.port_use = Some(PortUse::New); + self.port_use = PortUse::New; self } }; @@ -83,7 +69,7 @@ pub struct DialOpts { role_override: Endpoint, dial_concurrency_factor_override: Option, connection_id: ConnectionId, - port_use: Option, + port_use: PortUse, } impl DialOpts { @@ -104,7 +90,7 @@ impl DialOpts { condition: Default::default(), role_override: Endpoint::Dialer, dial_concurrency_factor_override: Default::default(), - port_use: None, + port_use: PortUse::Reuse, } } @@ -165,7 +151,7 @@ impl DialOpts { self.role_override } - pub(crate) fn port_use(&self) -> Option { + pub(crate) fn port_use(&self) -> PortUse { self.port_use } } @@ -188,7 +174,7 @@ pub struct WithPeerId { condition: PeerCondition, role_override: Endpoint, dial_concurrency_factor_override: Option, - port_use: Option, + port_use: PortUse, } impl WithPeerId { @@ -243,7 +229,7 @@ pub struct WithPeerIdWithAddresses { extend_addresses_through_behaviour: bool, role_override: Endpoint, dial_concurrency_factor_override: Option, - port_use: Option, + port_use: PortUse, } impl WithPeerIdWithAddresses { @@ -293,7 +279,7 @@ impl WithoutPeerId { WithoutPeerIdWithAddress { address, role_override: Endpoint::Dialer, - port_use: None, + port_use: PortUse::Reuse, } } } @@ -302,7 +288,7 @@ impl WithoutPeerId { pub struct WithoutPeerIdWithAddress { address: Multiaddr, role_override: Endpoint, - port_use: Option, + port_use: PortUse, } impl WithoutPeerIdWithAddress { diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 95438c9246a..a4ba3997550 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -123,7 +123,6 @@ pub use handler::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerSelect, KeepAlive, OneShotHandler, OneShotHandlerConfig, StreamUpgradeError, SubstreamProtocol, }; -use libp2p_core::multiaddr::Protocol; #[cfg(feature = "macros")] pub use libp2p_swarm_derive::NetworkBehaviour; pub use listen_opts::ListenOpts; @@ -140,7 +139,6 @@ use connection::{ use dial_opts::{DialOpts, PeerCondition}; use futures::{prelude::*, stream::FusedStream}; -use libp2p_core::transport::PortUse; use libp2p_core::{ connection::ConnectedPoint, multiaddr, @@ -505,15 +503,7 @@ where .map(|a| match p2p_addr(peer_id, a) { Ok(address) => { let transport_dial_opts = { - let transport_port_use = dial_opts.port_use().unwrap_or_else(|| { - let is_listening_on_same_transport = - any_with_same_transport(self.listeners(), &address); - if is_listening_on_same_transport { - PortUse::Reuse - } else { - PortUse::New - } - }); + let transport_port_use = dial_opts.port_use(); TransportDialOpts { endpoint: dial_opts.role_override(), port_use: transport_port_use, @@ -1832,54 +1822,6 @@ fn p2p_addr(peer: Option, addr: Multiaddr) -> Result( - mut haystack: impl Iterator, - needle: &Multiaddr, -) -> bool { - // it's ambigious wheather a dns or dnsaddr resolves to an ipv4 or ipv6 address. To be sure we - // default to using a new port. - if needle - .iter() - .any(|p| matches!(p, Protocol::Dns(_) | Protocol::Dnsaddr(_))) - { - return false; - } - - // Turns a multiaddress into an iterator of just the protocols. - fn clean_multiaddr(address: &Multiaddr) -> impl Iterator + '_ { - // I'm not sure about the selection here. A good example is Onion. It's not good defined what that's - // supposed to be and it's a protocol and an address. But since I (https://github.com/umgefahren) - // wrote the only Tor transport for rust-libp2p and it doesn't support these Onion addresses, I will just - // ignore that. - const NON_PROTOCOL_TAGS: &[&'static str] = &["p2p"]; - - // We check weather a Protocol tag is a network protocol like quic, tcp and udp. - fn is_not_protocol(tag: &&'static str) -> bool { - // Using `.contains()` instead of matches! isn't a lot different, when we look at the generated - // assembly. https://godbolt.org/z/1x9f3K16x - !NON_PROTOCOL_TAGS.contains(&tag) - } - - fn map_protocol(tag: &'static str) -> &'static str { - match tag { - // DNS4 resolves to an IPv4 address - "dns4" => "ip4", - // DNS6 resolves to an IPv6 address - "dns6" => "ip6", - _ => tag, - } - } - - address - .protocol_stack() - .filter(is_not_protocol) - .map(map_protocol) - } - - haystack.any(|e| clean_multiaddr(e).eq(clean_multiaddr(needle))) -} - #[cfg(test)] mod tests { use super::*; @@ -2467,236 +2409,4 @@ mod tests { // Unfortunately, we have some "empty" errors that lead to multiple colons without text but that is the best we can do. assert_eq!("Failed to negotiate transport protocol(s): [(/ip4/127.0.0.1/tcp/80: : No listener on the given port.)]", string) } - - #[cfg(test)] - mod port_use { - use std::str::FromStr; - - use libp2p_core::multiaddr::Protocol; - use libp2p_core::multihash::Multihash; - use libp2p_identity::PeerId; - - use crate::any_with_same_transport; - use crate::Multiaddr; - - macro_rules! test_dial_with_listener { - ($test_name:ident, $listen_addrs:expr, $combination:expr, $test_explanation:expr) => { - #[test] - fn $test_name() { - let test_explanation = $test_explanation; - let listen_addrs: Vec = $listen_addrs.into_iter().map(|e| e.parse().unwrap()).collect(); - $combination.into_iter().map(|(dial_addr_str, expected_output)| (dial_addr_str.parse::().unwrap(), expected_output)).for_each(|(dial_addr, expected_output)| { - let should_port_reuse = any_with_same_transport(listen_addrs.iter(), &dial_addr); - if expected_output { - assert!(should_port_reuse, "Port should have been reused, since there exists at least one listener in {listen_addrs:?} for {dial_addr:?}. Details: {test_explanation}") - } else { - assert!(!should_port_reuse, "Port should not have been reused, since there doesn't exist a listener in {listen_addrs:?} for {dial_addr:?}. Details: {test_explanation}"); - } - }) - } - }; - } - - const BASIC_LISTEN_ADDRS: [&str; 7] = [ - "/ip4/127.0.0.1", - "/ip6/::1", - "/ip4/127.0.0.1/tcp/800/tls/ws", - "/ip4/127.0.0.1/udp/443/quic", - "/ip4/127.0.0.1/udp/443/quic-v1", - "/dns4/bootstrap.libp2p.io/tcp/80/tls/ws/tls", - "/dns6/bootstrap.libp2p.io/tcp/443/tls/ws/tls", - ]; - - test_dial_with_listener!( - reuse_if_dial_to_self, - BASIC_LISTEN_ADDRS, - BASIC_LISTEN_ADDRS.map(|addr| (addr, true)), - "If this test fails there is likely a major problem, because we won't reuse ports to dial ourselves." - ); - - test_dial_with_listener!( - no_reuse_ip4_listener_for_ip6, - [ - "/ip4/198.51.100.30/tcp/1234", - "/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", - "/ip4/127.0.0.1/udp/1234/quic-v1", - "/ip4/12.34.56.78/udp/4321/quic", - "/ip4/102.0.2.0/udp/1234/quic/webtransport" - ], - [ - ("/ip4/198.51.100.50/tcp/42", true), - ("/ip6/2001:1380:2000:7a00::1/tcp/4001", false), - ("/ip6/fe80::883:a581:fff1:833/tcp/4002/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", false), - ("/ip6/2601:9:4f82:5fff:aefd:ecff:fe0b:7cfe/udp/2023/quic-v1", false), - ("/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/udp/4321/quic", false), - ("/ip6/1300::1/udp/300/quic/webtransport", false), - ], - "We shouldn't reuse an ip4 listener to dial to an ip6 address." - ); - - test_dial_with_listener!( - no_reuse_ip6_listener_for_ip4, - [ - "/ip6/2001:1380:2000:7a00::1/tcp/4001", - "/ip6/fe80::883:a581:fff1:833/tcp/4002/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", - "/ip6/2601:9:4f82:5fff:aefd:ecff:fe0b:7cfe/udp/2023/quic-v1", - "/ip6/2001:0db8:85a3:0000:0000:8a2e:0370:7334/udp/4321/quic", - "/ip6/1300::1/udp/300/quic/webtransport", - ], - [ - ("/ip6/2001:1380:2000:7a00::1/tcp/4001", true), - ("/ip4/198.51.100.30/tcp/1234", false), - ("/ip4/147.75.83.83/tcp/4001/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", false), - ("/ip4/127.0.0.1/udp/1234/quic-v1", false), - ("/ip4/12.34.56.78/udp/4321/quic", false), - ("/ip4/102.0.2.0/udp/1234/quic/webtransport", false) - ], - "We shouldn't reuse an ip6 listener to dial to an ip4 address." - ); - - const DNS_AND_IP_ADDRS: [&'static str; 6] = [ - "/ip4/143.244.56.51/tcp/4001", - "/ip6/2a02:2e0:3fe:1001:302::/tcp/4001", - "/dns/protocol.ai/tcp/4001", - "/dnsaddr/protocol.ai/tcp/4001", - "/dns4/protocol.ai/tcp/4001", - "/dns6/protocol.ai/tcp/4001", - ]; - - test_dial_with_listener!( - no_reuse_since_dns_is_ambigious, - ["/dns/bootstrap.libp2p.io/tcp/4001",], - DNS_AND_IP_ADDRS.map(|addr| (addr, false)), - "We can't know wheather a dns address resolves to an ip4 or ip6 address." - ); - - test_dial_with_listener!( - no_reuse_since_dnsaddr_is_ambigious, - ["/dnsaddr/bootstrap.libp2p.io/tcp/4001"], - DNS_AND_IP_ADDRS.map(|addr| (addr, false)), - "We can't know wheather a dns address resolves to an ip4 or ip6 address." - ); - - test_dial_with_listener!( - reuse_since_dns4_resolves_to_ip4, - ["/dns4/libp2p.io/tcp/80", "/ip4/185.93.3.233/udp/443/quic"], - [ - ("/ip4/185.93.3.233/tcp/80", true), - ("/ip6/2400:52e0:1e00::1080:1/tcp/80", false), - ("/dns4/libp2p.io/udp/443/quic", true), - ("/dns6/libp2p.io/udp/443/quic", false), - ], - "We know that dns4 resolves to an ip4 address and vice versa." - ); - - test_dial_with_listener!( - reuse_since_dns6_resolves_to_ip6, - [ - "/dns6/libp2p.io/tcp/80", - "/ip6/2400:52e0:1e00::1080:1/udp/443/quic", - ], - [ - ("/ip6/2400:52e0:1e00::1080:1/tcp/80", true), - ("/ip4/185.93.3.233/tcp/80", false), - ("/dns6/libp2p.io/udp/443/quic", true), - ("/dns4/libp2p.io/udp/443/quic", false), - ], - "We know that dns6 resolves to an ip6 address and vice versa." - ); - - const IP_DIFFERENT_PROTOCOLS: [&str; 4] = [ - "/ip4/1.1.1.1/tcp/80", - "/ip4/1.1.1.1/udp/443/quic", - "/ip4/1.1.1.1/udp/443/quic-v1", - "/ip4/1.1.1.1/udp/1234/webrtc-direct/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", - ]; - - macro_rules! no_reuse_since_no_protocol_listener { - ($fun_name:ident, $protocol:literal) => { - test_dial_with_listener!( - $fun_name, - IP_DIFFERENT_PROTOCOLS - .into_iter() - .filter(|e| !e.contains($protocol)), - IP_DIFFERENT_PROTOCOLS - .into_iter() - .filter(|e| e.contains($protocol)) - .map(|addr| (addr, false)), - concat!( - "We can't reuse a port since there exists no ", - $protocol, - "listener yet." - ) - ); - }; - } - - no_reuse_since_no_protocol_listener!(no_reuse_since_no_tcp_listener, "tcp"); - no_reuse_since_no_protocol_listener!(no_reuse_since_no_quic_listener, "quic"); - no_reuse_since_no_protocol_listener!(no_reuse_since_no_quicv1_listener, "quic-v1"); - no_reuse_since_no_protocol_listener!( - no_reuse_since_no_webrtc_direct_listener, - "webrtc-direct" - ); - - const LISTEN_IP4_ADDRS: [&str; 4] = [ - "/ip4/127.0.0.1", - "/ip4/1.1.1.1", - "/dns4/libp2p.io", - "/dns4/filecoin.io", - ]; - const DIAL_IP4_ADDRS: [&str; 4] = [ - "/ip4/39.13.30.9", - "/ip4/8.8.8.8", - "/dns4/bootstrap.libp2p.io", - "/dns4/filecoin.io", - ]; - - macro_rules! reuse_same_protocol_different_ip { - ($fun_name:ident, $protocol_name:literal, $($protocol:expr),+) => { - test_dial_with_listener!( - $fun_name, - LISTEN_IP4_ADDRS.map(|e| { - let mut addr = e.parse::().unwrap(); - $( - addr.push($protocol); - )+ - addr.to_string() - }), - DIAL_IP4_ADDRS.map(|e| { - let mut addr = e.parse::().unwrap(); - $( - addr.push($protocol); - )+ - (addr.to_string(), true) - }), - concat!( - "We can reuse a port, since there exists a listener with different ip but same protocol (", $protocol_name, ")." - ) - ); - }; - } - reuse_same_protocol_different_ip!(reuse_since_tcp_listener, "tcp", Protocol::Tcp(800)); - reuse_same_protocol_different_ip!( - reuse_since_quic_listener, - "quic", - Protocol::Udp(10), - Protocol::Quic - ); - reuse_same_protocol_different_ip!( - reuse_since_quicv1_listener, - "quic-v1", - Protocol::Udp(20), - Protocol::QuicV1 - ); - reuse_same_protocol_different_ip!( - reuse_since_webrtc_direct_listener, - "webrtc-direct", - Protocol::WebRTCDirect, - Protocol::Certhash(Multihash::<64>::wrap(0x12, b"umgefahren was here").unwrap()), - Protocol::P2p( - PeerId::from_str("QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb").unwrap() - ) - ); - } } From 3487ae3539fb4c8e0197acccba06abea0814f5e8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:26:06 +0200 Subject: [PATCH 020/179] Implement port use propagation --- core/src/transport/and_then.rs | 3 ++- core/src/transport/map.rs | 1 + protocols/ping/src/lib.rs | 2 ++ swarm/src/behaviour.rs | 2 ++ swarm/src/behaviour/either.rs | 4 ++++ swarm/src/behaviour/toggle.rs | 3 +++ swarm/src/connection.rs | 4 +++- swarm/src/connection/pool.rs | 8 +++++--- swarm/src/dummy.rs | 2 ++ swarm/src/keep_alive.rs | 2 ++ swarm/src/lib.rs | 3 +++ 11 files changed, 29 insertions(+), 5 deletions(-) diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index 4168176ef17..39c8bd93b8d 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -85,6 +85,7 @@ where ConnectedPoint::Dialer { address: addr, role_override: Endpoint::Dialer, + port_use: dial_opts.port_use, }, )), _marker: PhantomPinned, @@ -161,7 +162,7 @@ where Poll::Ready(Err(err)) => return Poll::Ready(Err(Either::Left(err))), Poll::Pending => return Poll::Pending, }; - let (f, mut a) = self + let (f, a) = self .args .take() .expect("AndThenFuture has already finished."); diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index 8922dc72bc3..a7b645f6d88 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -83,6 +83,7 @@ where let p = ConnectedPoint::Dialer { address: addr, role_override: Endpoint::Dialer, + port_use: dial_opts.port_use, }; Ok(MapFuture { inner: future, diff --git a/protocols/ping/src/lib.rs b/protocols/ping/src/lib.rs index d1c4a2facaf..feeb9dd0bad 100644 --- a/protocols/ping/src/lib.rs +++ b/protocols/ping/src/lib.rs @@ -51,6 +51,7 @@ mod handler; mod protocol; use handler::Handler; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -124,6 +125,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new(self.config.clone(), peer)) } diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 3662bf5c48d..b14c964ba5d 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -24,6 +24,7 @@ mod listen_addresses; pub mod toggle; pub use external_addresses::ExternalAddresses; +use libp2p_core::transport::PortUse; pub use listen_addresses::ListenAddresses; use crate::connection::ConnectionId; @@ -188,6 +189,7 @@ pub trait NetworkBehaviour: 'static { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied>; /// Informs the behaviour about an event from the [`Swarm`](crate::Swarm). diff --git a/swarm/src/behaviour/either.rs b/swarm/src/behaviour/either.rs index c6e0870d11c..3a0fd698fb8 100644 --- a/swarm/src/behaviour/either.rs +++ b/swarm/src/behaviour/either.rs @@ -22,6 +22,7 @@ use crate::behaviour::{self, NetworkBehaviour, PollParameters, ToSwarm}; use crate::connection::ConnectionId; use crate::{ConnectionDenied, THandler, THandlerInEvent, THandlerOutEvent}; use either::Either; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use std::{task::Context, task::Poll}; @@ -103,6 +104,7 @@ where peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { let handler = match self { Either::Left(inner) => Either::Left(inner.handle_established_outbound_connection( @@ -110,12 +112,14 @@ where peer, addr, role_override, + port_use, )?), Either::Right(inner) => Either::Right(inner.handle_established_outbound_connection( connection_id, peer, addr, role_override, + port_use, )?), }; diff --git a/swarm/src/behaviour/toggle.rs b/swarm/src/behaviour/toggle.rs index 92bd8963502..dbb2ece53b9 100644 --- a/swarm/src/behaviour/toggle.rs +++ b/swarm/src/behaviour/toggle.rs @@ -32,6 +32,7 @@ use crate::{ }; use either::Either; use futures::future; +use libp2p_core::transport::PortUse; use libp2p_core::{upgrade::DeniedUpgrade, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use std::{task::Context, task::Poll}; @@ -141,6 +142,7 @@ where peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { let inner = match self.inner.as_mut() { None => return Ok(ToggleConnectionHandler { inner: None }), @@ -152,6 +154,7 @@ where peer, addr, role_override, + port_use, )?; Ok(ToggleConnectionHandler { diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 8fdc39edae0..1acbe39b3ff 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -27,6 +27,7 @@ pub use error::ConnectionError; pub(crate) use error::{ PendingConnectionError, PendingInboundConnectionError, PendingOutboundConnectionError, }; +use libp2p_core::transport::PortUse; pub use supported_protocols::SupportedProtocols; use crate::handler::{ @@ -1379,6 +1380,7 @@ enum PendingPoint { Dialer { /// Same as [`ConnectedPoint::Dialer`] `role_override`. role_override: Endpoint, + port_use: PortUse, }, /// The socket comes from a listener. Listener { @@ -1392,7 +1394,7 @@ enum PendingPoint { impl From for PendingPoint { fn from(endpoint: ConnectedPoint) -> Self { match endpoint { - ConnectedPoint::Dialer { role_override, .. } => PendingPoint::Dialer { role_override }, + ConnectedPoint::Dialer { role_override, port_use, .. } => PendingPoint::Dialer { role_override, port_use }, ConnectedPoint::Listener { local_addr, send_back_addr, diff --git a/swarm/src/connection/pool.rs b/swarm/src/connection/pool.rs index c43d985f97e..378ccfe5318 100644 --- a/swarm/src/connection/pool.rs +++ b/swarm/src/connection/pool.rs @@ -40,6 +40,7 @@ use futures::{ use instant::{Duration, Instant}; use libp2p_core::connection::Endpoint; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt}; +use libp2p_core::transport::PortUse; use std::task::Waker; use std::{ collections::{hash_map, HashMap}, @@ -423,6 +424,7 @@ where >, peer: Option, role_override: Endpoint, + port_use: PortUse, dial_concurrency_factor_override: Option, connection_id: ConnectionId, ) { @@ -441,7 +443,7 @@ where self.pending_connection_events_tx.clone(), )); - let endpoint = PendingPoint::Dialer { role_override }; + let endpoint = PendingPoint::Dialer { role_override, port_use }; self.counters.inc_pending(&endpoint); self.pending.insert( @@ -642,11 +644,11 @@ where self.counters.dec_pending(&endpoint); let (endpoint, concurrent_dial_errors) = match (endpoint, outgoing) { - (PendingPoint::Dialer { role_override }, Some((address, errors))) => ( + (PendingPoint::Dialer { role_override, port_use }, Some((address, errors))) => ( ConnectedPoint::Dialer { address, role_override, - port_use, + port_use }, Some(errors), ), diff --git a/swarm/src/dummy.rs b/swarm/src/dummy.rs index 6810abec591..4166a7f0418 100644 --- a/swarm/src/dummy.rs +++ b/swarm/src/dummy.rs @@ -7,6 +7,7 @@ use crate::{ ConnectionDenied, ConnectionHandlerEvent, KeepAlive, StreamUpgradeError, SubstreamProtocol, THandler, THandlerInEvent, THandlerOutEvent, }; +use libp2p_core::transport::PortUse; use libp2p_core::upgrade::DeniedUpgrade; use libp2p_core::Endpoint; use libp2p_core::Multiaddr; @@ -37,6 +38,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(ConnectionHandler) } diff --git a/swarm/src/keep_alive.rs b/swarm/src/keep_alive.rs index 05cbcdf7b8c..bd0c08f8169 100644 --- a/swarm/src/keep_alive.rs +++ b/swarm/src/keep_alive.rs @@ -5,6 +5,7 @@ use crate::handler::{ KeepAlive, SubstreamProtocol, }; use crate::{ConnectionDenied, THandler, THandlerInEvent, THandlerOutEvent}; +use libp2p_core::transport::PortUse; use libp2p_core::upgrade::DeniedUpgrade; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -40,6 +41,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(ConnectionHandler) } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index db2834f763a..74ac02ea4d9 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -552,6 +552,7 @@ where dials, peer_id, dial_opts.role_override(), + dial_opts.port_use(), dial_opts.dial_concurrency_override(), connection_id, ); @@ -694,12 +695,14 @@ where ConnectedPoint::Dialer { address, role_override, + port_use, } => { match self.behaviour.handle_established_outbound_connection( id, peer_id, &address, role_override, + port_use, ) { Ok(handler) => handler, Err(cause) => { From d9385defa4cb84dc761eb50ac5c46176065c2486 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:43:02 +0200 Subject: [PATCH 021/179] Fix the behaviours --- protocols/identify/src/behaviour.rs | 2 ++ protocols/kad/src/behaviour.rs | 3 +++ swarm/src/test.rs | 5 ++++- swarm/tests/listener.rs | 3 ++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index f572b937d38..637deb2b697 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -20,6 +20,7 @@ use crate::handler::{self, Handler, InEvent}; use crate::protocol::{Info, UpgradeError}; +use libp2p_core::transport::PortUse; use libp2p_core::{multiaddr, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; @@ -262,6 +263,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new( self.config.initial_delay, diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 262962cbd1f..dafb1ea1399 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -36,6 +36,7 @@ use crate::record_priv::{ use crate::K_VALUE; use fnv::{FnvHashMap, FnvHashSet}; use instant::Instant; +use libp2p_core::transport::PortUse; use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ @@ -2086,10 +2087,12 @@ where peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { let connected_point = ConnectedPoint::Dialer { address: addr.clone(), role_override, + port_use, }; self.connections.insert(connection_id, peer); diff --git a/swarm/src/test.rs b/swarm/src/test.rs index 6f39d56da91..5e7f2239a6f 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -26,6 +26,7 @@ use crate::{ ConnectionDenied, ConnectionHandler, ConnectionId, NetworkBehaviour, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, }; +use libp2p_core::transport::PortUse; use libp2p_core::{multiaddr::Multiaddr, transport::ListenerId, ConnectedPoint, Endpoint}; use libp2p_identity::PeerId; use std::collections::HashMap; @@ -91,6 +92,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result { Ok(self.handler_proto.clone()) } @@ -447,6 +449,7 @@ where peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { self.handle_established_outbound_connection.push(( peer, @@ -455,7 +458,7 @@ where connection_id, )); self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/swarm/tests/listener.rs b/swarm/tests/listener.rs index 71d92cb0e1f..698d31b8d0e 100644 --- a/swarm/tests/listener.rs +++ b/swarm/tests/listener.rs @@ -3,7 +3,7 @@ use std::{ task::{Context, Poll}, }; -use libp2p_core::{multiaddr::Protocol, transport::ListenerId, Endpoint, Multiaddr}; +use libp2p_core::{multiaddr::Protocol, transport::{ListenerId, PortUse}, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ derive_prelude::NewListener, dummy, ConnectionDenied, ConnectionId, FromSwarm, ListenOpts, @@ -93,6 +93,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } From 275569652687e1e0aca6a25cc3fde43f5289eff7 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:53:01 +0200 Subject: [PATCH 022/179] Make the tests pass sometimes --- swarm-derive/src/lib.rs | 4 +++- swarm/src/lib.rs | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index e54cd058daf..911d3194373 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -92,6 +92,7 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> syn::Result syn::Result syn::Result Result<#t_handler, #connection_denied> { Ok(#handle_established_outbound_connection) } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 74ac02ea4d9..9fb60465210 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -103,7 +103,7 @@ pub mod derive_prelude { pub use crate::ToSwarm; pub use either::Either; pub use futures::prelude as futures; - pub use libp2p_core::transport::ListenerId; + pub use libp2p_core::transport::{ListenerId, PortUse}; pub use libp2p_core::ConnectedPoint; pub use libp2p_core::Endpoint; pub use libp2p_core::Multiaddr; @@ -1982,7 +1982,7 @@ mod tests { use futures::future; use libp2p_core::multiaddr::multiaddr; use libp2p_core::transport::memory::MemoryTransportError; - use libp2p_core::transport::TransportEvent; + use libp2p_core::transport::{TransportEvent, PortUse}; use libp2p_core::Endpoint; use libp2p_core::{multiaddr, transport, upgrade}; use libp2p_identity as identity; @@ -2379,6 +2379,7 @@ mod tests { ConnectedPoint::Dialer { address: other_addr, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, } ); } From c4796ac9dbb894753c52156b139951e1dc777dee Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:40:02 +0200 Subject: [PATCH 023/179] Transition TCP --- core/src/transport/and_then.rs | 2 +- core/src/transport/boxed.rs | 2 +- core/src/transport/map.rs | 2 +- swarm/src/dial_opts.rs | 15 +- transports/tcp/src/lib.rs | 256 ++++++++++----------------------- 5 files changed, 90 insertions(+), 187 deletions(-) diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index 39c8bd93b8d..eeee39bf531 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -84,7 +84,7 @@ where self.fun.clone(), ConnectedPoint::Dialer { address: addr, - role_override: Endpoint::Dialer, + role_override: dial_opts.endpoint, port_use: dial_opts.port_use, }, )), diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index 051833ccf5f..5bdc44aef05 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -61,7 +61,7 @@ trait Abstract { fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result, TransportError>; fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option; fn poll( diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index a7b645f6d88..4c9d4230be6 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -82,7 +82,7 @@ where let future = self.transport.dial(addr.clone(), dial_opts)?; let p = ConnectedPoint::Dialer { address: addr, - role_override: Endpoint::Dialer, + role_override: dial_opts.endpoint, port_use: dial_opts.port_use, }; Ok(MapFuture { diff --git a/swarm/src/dial_opts.rs b/swarm/src/dial_opts.rs index 0fb58708db1..5603d63671a 100644 --- a/swarm/src/dial_opts.rs +++ b/swarm/src/dial_opts.rs @@ -27,7 +27,7 @@ use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use std::num::NonZeroU8; -macro_rules! port_use_and_role_setter { +macro_rules! fn_override_role { () => { /// Override role of local node on connection. I.e. execute the dial _as a /// listener_. @@ -39,7 +39,11 @@ macro_rules! port_use_and_role_setter { self.role_override = Endpoint::Listener; self } + }; +} +macro_rules! fn_allocate_new_port { + () => { /// Enforce the allocation of a new port. /// Default behaviour is best effort reuse of existing ports. If there is no existing /// fitting listener, a new port is allocated. @@ -204,7 +208,8 @@ impl WithPeerId { } } - port_use_and_role_setter!(); + fn_override_role!(); + fn_allocate_new_port!(); /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { @@ -246,7 +251,8 @@ impl WithPeerIdWithAddresses { self } - port_use_and_role_setter!(); + fn_override_role!(); + fn_allocate_new_port!(); /// Override /// Number of addresses concurrently dialed for a single outbound connection attempt. @@ -292,7 +298,8 @@ pub struct WithoutPeerIdWithAddress { } impl WithoutPeerIdWithAddress { - port_use_and_role_setter!(); + fn_override_role!(); + fn_allocate_new_port!(); /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index d3fdbc2b90b..5290825ab00 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -43,11 +43,10 @@ use futures::{ }; use futures_timer::Delay; use if_watch::IfEvent; -use libp2p_core::transport::DialOpts; use libp2p_core::{ address_translation, multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, PortUse, TransportError, TransportEvent}, }; use provider::{Incoming, Provider}; use socket2::{Domain, Socket, Type}; @@ -70,27 +69,17 @@ pub struct Config { nodelay: Option, /// Size of the listen backlog for listen sockets. backlog: u32, - /// Whether port reuse should be enabled. - enable_port_reuse: bool, } type Port = u16; /// The configuration for port reuse of listening sockets. -#[derive(Debug, Clone)] -enum PortReuse { - /// Port reuse is disabled, i.e. ephemeral local ports are - /// used for outgoing TCP connections. - Disabled, - /// Port reuse when dialing is enabled, i.e. the local - /// address and port that a new socket for an outgoing - /// connection is bound to are chosen from an existing - /// listening socket, if available. - Enabled { - /// The addresses and ports of the listening sockets - /// registered as eligible for port reuse when dialing. - listen_addrs: Arc>>, - }, +#[derive(Debug, Clone, Default)] +struct PortReuse { + /// The addresses and ports of the listening sockets + /// registered as eligible for port reuse when dialing + listen_addrs: Arc>>, + dialed_as_listener: Arc>>, } impl PortReuse { @@ -98,26 +87,21 @@ impl PortReuse { /// /// Has no effect if port reuse is disabled. fn register(&mut self, ip: IpAddr, port: Port) { - if let PortReuse::Enabled { listen_addrs } = self { - log::trace!("Registering for port reuse: {}:{}", ip, port); - listen_addrs - .write() - .expect("`register()` and `unregister()` never panic while holding the lock") - .insert((ip, port)); - } + self.listen_addrs + .write() + .expect("`register()` and `unregister()` never panic while holding the lock") + .insert((ip, port)); } /// Unregisters a socket address for port reuse. /// /// Has no effect if port reuse is disabled. fn unregister(&mut self, ip: IpAddr, port: Port) { - if let PortReuse::Enabled { listen_addrs } = self { - log::trace!("Unregistering for port reuse: {}:{}", ip, port); - listen_addrs - .write() - .expect("`register()` and `unregister()` never panic while holding the lock") - .remove(&(ip, port)); - } + log::trace!("Unregistering for port reuse: {}:{}", ip, port); + self.listen_addrs + .write() + .expect("`register()` and `unregister()` never panic while holding the lock") + .remove(&(ip, port)); } /// Selects a listening socket address suitable for use @@ -130,26 +114,37 @@ impl PortReuse { /// Returns `None` if port reuse is disabled or no suitable /// listening socket address is found. fn local_dial_addr(&self, remote_ip: &IpAddr) -> Option { - if let PortReuse::Enabled { listen_addrs } = self { - for (ip, port) in listen_addrs - .read() - .expect("`local_dial_addr` never panic while holding the lock") - .iter() - { - if ip.is_ipv4() == remote_ip.is_ipv4() - && ip.is_loopback() == remote_ip.is_loopback() - { - if remote_ip.is_ipv4() { - return Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), *port)); - } else { - return Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), *port)); - } + for (ip, port) in self + .listen_addrs + .read() + .expect("`local_dial_addr` never panic while holding the lock") + .iter() + { + if ip.is_ipv4() == remote_ip.is_ipv4() && ip.is_loopback() == remote_ip.is_loopback() { + if remote_ip.is_ipv4() { + return Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), *port)); + } else { + return Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), *port)); } } } None } + + fn dialed_from_listener(&self, addr: Multiaddr) { + self.dialed_as_listener + .write() + .expect("`dialed_as_listener` never panic while holding the lock") + .insert(addr); + } + + fn already_dialed_as_listener(&self, addr: &Multiaddr) -> bool { + self.dialed_as_listener + .read() + .expect("`already_dialed_as_listener` never panic while holding the lock") + .contains(addr) + } } impl Config { @@ -168,7 +163,6 @@ impl Config { ttl: None, nodelay: None, backlog: 1024, - enable_port_reuse: false, } } @@ -189,106 +183,6 @@ impl Config { self.backlog = backlog; self } - - /// Configures port reuse for local sockets, which implies - /// reuse of listening ports for outgoing connections to - /// enhance NAT traversal capabilities. - /// - /// Please refer to e.g. [RFC 4787](https://tools.ietf.org/html/rfc4787) - /// section 4 and 5 for some of the NAT terminology used here. - /// - /// There are two main use-cases for port reuse among local - /// sockets: - /// - /// 1. Creating multiple listening sockets for the same address - /// and port to allow accepting connections on multiple threads - /// without having to synchronise access to a single listen socket. - /// - /// 2. Creating outgoing connections whose local socket is bound to - /// the same address and port as a listening socket. In the rare - /// case of simple NATs with both endpoint-independent mapping and - /// endpoint-independent filtering, this can on its own already - /// permit NAT traversal by other nodes sharing the observed - /// external address of the local node. For the common case of - /// NATs with address-dependent or address and port-dependent - /// filtering, port reuse for outgoing connections can facilitate - /// further TCP hole punching techniques for NATs that perform - /// endpoint-independent mapping. Port reuse cannot facilitate - /// NAT traversal in the presence of "symmetric" NATs that employ - /// both address/port-dependent mapping and filtering, unless - /// there is some means of port prediction. - /// - /// Both use-cases are enabled when port reuse is enabled, with port reuse - /// for outgoing connections (`2.` above) always being implied. - /// - /// > **Note**: Due to the identification of a TCP socket by a 4-tuple - /// > of source IP address, source port, destination IP address and - /// > destination port, with port reuse enabled there can be only - /// > a single outgoing connection to a particular address and port - /// > of a peer per local listening socket address. - /// - /// [`Transport`] keeps track of the listen socket addresses as they - /// are reported by polling it. It is possible to listen on multiple - /// addresses, enabling port reuse for each, knowing exactly which listen - /// address is reused when dialing with a specific [`Transport`], as in the - /// following example: - /// - /// ```no_run - /// # use futures::StreamExt; - /// # use libp2p_core::transport::{ListenerId, TransportEvent}; - /// # use libp2p_core::{Multiaddr, Transport}; - /// # use std::pin::Pin; - /// # #[cfg(not(feature = "async-io"))] - /// # fn main() {} - /// # - /// #[cfg(feature = "async-io")] - /// #[async_std::main] - /// async fn main() -> std::io::Result<()> { - /// - /// let listen_addr1: Multiaddr = "/ip4/127.0.0.1/tcp/9001".parse().unwrap(); - /// let listen_addr2: Multiaddr = "/ip4/127.0.0.1/tcp/9002".parse().unwrap(); - /// - /// let mut tcp1 = libp2p_tcp::async_io::Transport::new(libp2p_tcp::Config::new().port_reuse(true)).boxed(); - /// tcp1.listen_on(ListenerId::next(), listen_addr1.clone()).expect("listener"); - /// match tcp1.select_next_some().await { - /// TransportEvent::NewAddress { listen_addr, .. } => { - /// println!("Listening on {:?}", listen_addr); - /// let mut stream = tcp1.dial(listen_addr2.clone()).unwrap().await?; - /// // `stream` has `listen_addr1` as its local socket address. - /// } - /// _ => {} - /// } - /// - /// let mut tcp2 = libp2p_tcp::async_io::Transport::new(libp2p_tcp::Config::new().port_reuse(true)).boxed(); - /// tcp2.listen_on(ListenerId::next(), listen_addr2).expect("listener"); - /// match tcp2.select_next_some().await { - /// TransportEvent::NewAddress { listen_addr, .. } => { - /// println!("Listening on {:?}", listen_addr); - /// let mut socket = tcp2.dial(listen_addr1).unwrap().await?; - /// // `stream` has `listen_addr2` as its local socket address. - /// } - /// _ => {} - /// } - /// Ok(()) - /// } - /// ``` - /// - /// If a wildcard listen socket address is used to listen on any interface, - /// there can be multiple such addresses registered for port reuse. In this - /// case, one is chosen whose IP protocol version and loopback status is the - /// same as that of the remote address. Consequently, for maximum control of - /// the local listening addresses and ports that are used for outgoing - /// connections, a new [`Transport`] should be created for each listening - /// socket, avoiding the use of wildcard addresses which bind a socket to - /// all network interfaces. - /// - /// When this option is enabled on a unix system, the socket - /// option `SO_REUSEPORT` is set, if available, to permit - /// reuse of listening ports for multiple sockets. - pub fn port_reuse(mut self, port_reuse: bool) -> Self { - self.enable_port_reuse = port_reuse; - self - } } impl Default for Config { @@ -333,13 +227,7 @@ where /// - [`tokio::Transport::new`] /// - [`async_io::Transport::new`] pub fn new(config: Config) -> Self { - let port_reuse = if config.enable_port_reuse { - PortReuse::Enabled { - listen_addrs: Arc::new(RwLock::new(HashSet::new())), - } - } else { - PortReuse::Disabled - }; + let port_reuse = PortReuse::default(); Transport { config, port_reuse, @@ -347,7 +235,7 @@ where } } - fn create_socket(&self, socket_addr: SocketAddr) -> io::Result { + fn create_socket(&self, socket_addr: SocketAddr, port_use: PortUse) -> io::Result { let socket = Socket::new( Domain::for_address(socket_addr), Type::STREAM, @@ -363,8 +251,8 @@ where socket.set_nodelay(nodelay)?; } socket.set_reuse_address(true)?; - #[cfg(unix)] - if let PortReuse::Enabled { .. } = &self.port_reuse { + #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] + if port_use == PortUse::Reuse { socket.set_reuse_port(true)?; } Ok(socket) @@ -375,7 +263,7 @@ where id: ListenerId, socket_addr: SocketAddr, ) -> io::Result> { - let socket = self.create_socket(socket_addr)?; + let socket = self.create_socket(socket_addr, PortUse::Reuse)?; socket.bind(&socket_addr.into())?; socket.listen(self.config.backlog as _)?; socket.set_nonblocking(true)?; @@ -410,13 +298,7 @@ where /// This transport will have port-reuse disabled. fn default() -> Self { let config = Config::default(); - let port_reuse = if config.enable_port_reuse { - PortReuse::Enabled { - listen_addrs: Arc::new(RwLock::new(HashSet::new())), - } - } else { - PortReuse::Disabled - }; + let port_reuse = Default::default(); Transport { port_reuse, config, @@ -467,7 +349,7 @@ where fn dial( &mut self, addr: Multiaddr, - _opts: DialOpts, + opts: DialOpts, ) -> Result> { let socket_addr = if let Ok(socket_addr) = multiaddr_to_socketaddr(addr.clone()) { if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { @@ -480,12 +362,16 @@ where log::debug!("dialing {}", socket_addr); let socket = self - .create_socket(socket_addr) + .create_socket(socket_addr, opts.port_use) .map_err(TransportError::Other)?; - if let Some(addr) = self.port_reuse.local_dial_addr(&socket_addr.ip()) { - log::trace!("Binding dial socket to listen socket {}", addr); - socket.bind(&addr.into()).map_err(TransportError::Other)?; + match self.port_reuse.local_dial_addr(&socket_addr.ip()) { + Some(socket_addr) if opts.port_use == PortUse::Reuse => { + log::trace!("Binding dial socket to listen socket {}", socket_addr); + socket.bind(&socket_addr.into()).map_err(TransportError::Other)?; + self.port_reuse.dialed_from_listener(addr); + } + _ => {} } socket @@ -529,9 +415,10 @@ where if !is_tcp_addr(listen) || !is_tcp_addr(observed) { return None; } - match &self.port_reuse { - PortReuse::Disabled => address_translation(listen, observed), - PortReuse::Enabled { .. } => Some(observed.clone()), + if self.port_reuse.already_dialed_as_listener(&observed) { + Some(observed.clone()) + } else { + address_translation(listen, observed) } } @@ -845,6 +732,7 @@ mod tests { future::poll_fn, }; use libp2p_core::Transport as _; + use libp2p_core::Endpoint; use libp2p_identity::PeerId; #[test] @@ -926,7 +814,10 @@ mod tests { let mut tcp = Transport::::default(); // Obtain a future socket through dialing - let mut socket = tcp.dial(addr.clone()).unwrap().await.unwrap(); + let mut socket = tcp.dial(addr.clone(), DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::Reuse, + }).unwrap().await.unwrap(); socket.write_all(&[0x1, 0x2, 0x3]).await.unwrap(); let mut buf = [0u8; 3]; @@ -1000,7 +891,10 @@ mod tests { async fn dialer(mut ready_rx: mpsc::Receiver) { let dest_addr = ready_rx.next().await.unwrap(); let mut tcp = Transport::::default(); - tcp.dial(dest_addr).unwrap().await.unwrap(); + tcp.dial(dest_addr, DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New, + }).unwrap().await.unwrap(); } fn test(addr: Multiaddr) { @@ -1078,7 +972,7 @@ mod tests { port_reuse_tx: oneshot::Sender>, ) { let dest_addr = ready_rx.next().await.unwrap(); - let mut tcp = Transport::::new(Config::new().port_reuse(true)); + let mut tcp = Transport::::new(Config::new()); tcp.listen_on(ListenerId::next(), addr).unwrap(); match poll_fn(|cx| Pin::new(&mut tcp).poll(cx)).await { TransportEvent::NewAddress { .. } => { @@ -1097,7 +991,10 @@ mod tests { .ok(); // Obtain a future socket through dialing - let mut socket = tcp.dial(dest_addr).unwrap().await.unwrap(); + let mut socket = tcp.dial(dest_addr, DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::Reuse, + }).unwrap().await.unwrap(); socket.write_all(&[0x1, 0x2, 0x3]).await.unwrap(); // socket.flush().await; let mut buf = [0u8; 3]; @@ -1144,9 +1041,8 @@ mod tests { #[test] fn port_reuse_listening() { env_logger::try_init().ok(); - async fn listen_twice(addr: Multiaddr) { - let mut tcp = Transport::::new(Config::new().port_reuse(true)); + let mut tcp = Transport::::new(Config::new()); tcp.listen_on(ListenerId::next(), addr).unwrap(); match poll_fn(|cx| Pin::new(&mut tcp).poll(cx)).await { TransportEvent::NewAddress { @@ -1360,7 +1256,7 @@ mod tests { .build() .unwrap(); rt.block_on(async { - test::(); + test::(); }); } } From 2856b6f6cdb4a7683e7900a628f086a8d1be0c09 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:52:25 +0200 Subject: [PATCH 024/179] Format files --- swarm/src/connection.rs | 9 ++++++- swarm/src/connection/pool.rs | 15 ++++++++--- swarm/src/lib.rs | 2 +- swarm/src/test.rs | 9 +++++-- swarm/tests/listener.rs | 6 ++++- transports/tcp/src/lib.rs | 50 ++++++++++++++++++++++++++---------- 6 files changed, 69 insertions(+), 22 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 68211a203d2..ef5037f4158 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -1468,7 +1468,14 @@ enum PendingPoint { impl From for PendingPoint { fn from(endpoint: ConnectedPoint) -> Self { match endpoint { - ConnectedPoint::Dialer { role_override, port_use, .. } => PendingPoint::Dialer { role_override, port_use }, + ConnectedPoint::Dialer { + role_override, + port_use, + .. + } => PendingPoint::Dialer { + role_override, + port_use, + }, ConnectedPoint::Listener { local_addr, send_back_addr, diff --git a/swarm/src/connection/pool.rs b/swarm/src/connection/pool.rs index 378ccfe5318..fcfb71de8e5 100644 --- a/swarm/src/connection/pool.rs +++ b/swarm/src/connection/pool.rs @@ -443,7 +443,10 @@ where self.pending_connection_events_tx.clone(), )); - let endpoint = PendingPoint::Dialer { role_override, port_use }; + let endpoint = PendingPoint::Dialer { + role_override, + port_use, + }; self.counters.inc_pending(&endpoint); self.pending.insert( @@ -644,11 +647,17 @@ where self.counters.dec_pending(&endpoint); let (endpoint, concurrent_dial_errors) = match (endpoint, outgoing) { - (PendingPoint::Dialer { role_override, port_use }, Some((address, errors))) => ( + ( + PendingPoint::Dialer { + role_override, + port_use, + }, + Some((address, errors)), + ) => ( ConnectedPoint::Dialer { address, role_override, - port_use + port_use, }, Some(errors), ), diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 9fb60465210..e5b1f0be94c 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -1982,7 +1982,7 @@ mod tests { use futures::future; use libp2p_core::multiaddr::multiaddr; use libp2p_core::transport::memory::MemoryTransportError; - use libp2p_core::transport::{TransportEvent, PortUse}; + use libp2p_core::transport::{PortUse, TransportEvent}; use libp2p_core::Endpoint; use libp2p_core::{multiaddr, transport, upgrade}; use libp2p_identity as identity; diff --git a/swarm/src/test.rs b/swarm/src/test.rs index 5e7f2239a6f..f8b2361f4ca 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -457,8 +457,13 @@ where role_override, connection_id, )); - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/swarm/tests/listener.rs b/swarm/tests/listener.rs index 698d31b8d0e..1ef1b28239c 100644 --- a/swarm/tests/listener.rs +++ b/swarm/tests/listener.rs @@ -3,7 +3,11 @@ use std::{ task::{Context, Poll}, }; -use libp2p_core::{multiaddr::Protocol, transport::{ListenerId, PortUse}, Endpoint, Multiaddr}; +use libp2p_core::{ + multiaddr::Protocol, + transport::{ListenerId, PortUse}, + Endpoint, Multiaddr, +}; use libp2p_identity::PeerId; use libp2p_swarm::{ derive_prelude::NewListener, dummy, ConnectionDenied, ConnectionId, FromSwarm, ListenOpts, diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 5290825ab00..9b2fddb1243 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -368,7 +368,9 @@ where match self.port_reuse.local_dial_addr(&socket_addr.ip()) { Some(socket_addr) if opts.port_use == PortUse::Reuse => { log::trace!("Binding dial socket to listen socket {}", socket_addr); - socket.bind(&socket_addr.into()).map_err(TransportError::Other)?; + socket + .bind(&socket_addr.into()) + .map_err(TransportError::Other)?; self.port_reuse.dialed_from_listener(addr); } _ => {} @@ -731,8 +733,8 @@ mod tests { channel::{mpsc, oneshot}, future::poll_fn, }; - use libp2p_core::Transport as _; use libp2p_core::Endpoint; + use libp2p_core::Transport as _; use libp2p_identity::PeerId; #[test] @@ -814,10 +816,17 @@ mod tests { let mut tcp = Transport::::default(); // Obtain a future socket through dialing - let mut socket = tcp.dial(addr.clone(), DialOpts { - endpoint: Endpoint::Dialer, - port_use: PortUse::Reuse, - }).unwrap().await.unwrap(); + let mut socket = tcp + .dial( + addr.clone(), + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); socket.write_all(&[0x1, 0x2, 0x3]).await.unwrap(); let mut buf = [0u8; 3]; @@ -891,10 +900,16 @@ mod tests { async fn dialer(mut ready_rx: mpsc::Receiver) { let dest_addr = ready_rx.next().await.unwrap(); let mut tcp = Transport::::default(); - tcp.dial(dest_addr, DialOpts { - endpoint: Endpoint::Dialer, - port_use: PortUse::New, - }).unwrap().await.unwrap(); + tcp.dial( + dest_addr, + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); } fn test(addr: Multiaddr) { @@ -991,10 +1006,17 @@ mod tests { .ok(); // Obtain a future socket through dialing - let mut socket = tcp.dial(dest_addr, DialOpts { - endpoint: Endpoint::Dialer, - port_use: PortUse::Reuse, - }).unwrap().await.unwrap(); + let mut socket = tcp + .dial( + dest_addr, + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); socket.write_all(&[0x1, 0x2, 0x3]).await.unwrap(); // socket.flush().await; let mut buf = [0u8; 3]; From 99df3286ef7b2a07d9540d99af556e515fd5958a Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:18:35 +0200 Subject: [PATCH 025/179] Apply suggestions from code review Co-authored-by: Thomas Eizinger --- core/src/transport.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/transport.rs b/core/src/transport.rs index 5dc74f67c91..8ff11d35ac4 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -58,14 +58,14 @@ pub use self::upgrade::Upgrade; static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1); -/// Defines the port use policy. Decides whether to reuse an existing port of a listener -/// or to allocate a new one. +/// The port use policy for a new connection. #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)] pub enum PortUse { /// Always allocate a new port for the dial. New, - /// Best effor reusing of the port. If there is no listener present that can be used to dial, - /// a new port is allocated. + /// Best effor reusing of an existing port. + /// + /// If there is no listener present that can be used to dial, a new port is allocated. #[default] Reuse, } From 0c2000ed74f68a386c5bbdacc42f2fc0bb34ed6b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:19:13 +0200 Subject: [PATCH 026/179] Implement all suggestions --- core/src/connection.rs | 9 +++++---- core/src/either.rs | 6 +++--- core/src/transport.rs | 2 +- core/src/transport/and_then.rs | 10 +++++----- core/src/transport/boxed.rs | 8 ++++---- core/src/transport/choice.rs | 6 +++--- core/src/transport/global_only.rs | 6 +++--- core/src/transport/map.rs | 10 +++++----- protocols/kad/src/behaviour.rs | 3 +-- swarm/src/behaviour.rs | 6 ++++-- swarm/src/lib.rs | 15 +++++++-------- 11 files changed, 41 insertions(+), 40 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index ded8eb65e32..09dbfc9928b 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -1,5 +1,3 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation @@ -24,10 +22,9 @@ use crate::{ }; /// The endpoint roles associated with a peer-to-peer communication channel. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Endpoint { /// The socket comes from a dialer. - #[default] Dialer, /// The socket comes from a listener. Listener, @@ -86,6 +83,10 @@ pub enum ConnectedPoint { role_override: Endpoint, /// Whether the port for the outgoing connection was reused from a listener /// or a new port was allocated. This is useful for address translation. + /// + /// The port use is implemented on a best-effort basis. It is not guaranteed + /// that [`PortUse::Reuse`] actually reused a port. A good example is the case + /// where there is no listener available to reuse a port from. port_use: PortUse, }, /// We received the node. diff --git a/core/src/either.rs b/core/src/either.rs index 4e89c6227ee..590dcd66595 100644 --- a/core/src/either.rs +++ b/core/src/either.rs @@ -176,16 +176,16 @@ where fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result> { use TransportError::*; match self { - Either::Left(a) => match a.dial(addr, dial_opts) { + Either::Left(a) => match a.dial(addr, opts) { Ok(connec) => Ok(EitherFuture::First(connec)), Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), Err(Other(err)) => Err(Other(Either::Left(err))), }, - Either::Right(b) => match b.dial(addr, dial_opts) { + Either::Right(b) => match b.dial(addr, opts) { Ok(connec) => Ok(EitherFuture::Second(connec)), Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), Err(Other(err)) => Err(Other(Either::Right(err))), diff --git a/core/src/transport.rs b/core/src/transport.rs index 8ff11d35ac4..c3db17be83e 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -65,7 +65,7 @@ pub enum PortUse { New, /// Best effor reusing of an existing port. /// - /// If there is no listener present that can be used to dial, a new port is allocated. + /// If there is no listener present that can be used to dial, a new port is allocated. #[default] Reuse, } diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index eeee39bf531..8d2b8174665 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -20,7 +20,7 @@ use crate::transport::DialOpts; use crate::{ - connection::{ConnectedPoint, Endpoint}, + connection::ConnectedPoint, transport::{ListenerId, Transport, TransportError, TransportEvent}, }; use either::Either; @@ -72,11 +72,11 @@ where fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result> { let dialed_fut = self .transport - .dial(addr.clone(), dial_opts) + .dial(addr.clone(), opts) .map_err(|err| err.map(Either::Left))?; let future = AndThenFuture { inner: Either::Left(Box::pin(dialed_fut)), @@ -84,8 +84,8 @@ where self.fun.clone(), ConnectedPoint::Dialer { address: addr, - role_override: dial_opts.endpoint, - port_use: dial_opts.port_use, + role_override: opts.endpoint, + port_use: opts.port_use, }, )), _marker: PhantomPinned, diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index 5bdc44aef05..7b7262c977d 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -92,9 +92,9 @@ where fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result, TransportError> { - let fut = Transport::dial(self, addr, dial_opts) + let fut = Transport::dial(self, addr, opts) .map(|r| r.map_err(box_err)) .map_err(|e| e.map(box_err))?; Ok(Box::pin(fut) as Dial<_>) @@ -146,9 +146,9 @@ impl Transport for Boxed { fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result> { - self.inner.dial(addr, dial_opts) + self.inner.dial(addr, opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index 33799f8a9ce..a6dbc8bb328 100644 --- a/core/src/transport/choice.rs +++ b/core/src/transport/choice.rs @@ -96,14 +96,14 @@ where fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result> { trace!( "Attempting to dial {} using {}", addr, std::any::type_name::() ); - let addr = match self.0.dial(addr, dial_opts) { + let addr = match self.0.dial(addr, opts) { Ok(connec) => return Ok(EitherFuture::First(connec)), Err(TransportError::MultiaddrNotSupported(addr)) => { debug!( @@ -123,7 +123,7 @@ where addr, std::any::type_name::() ); - let addr = match self.1.dial(addr, dial_opts) { + let addr = match self.1.dial(addr, opts) { Ok(connec) => return Ok(EitherFuture::Second(connec)), Err(TransportError::MultiaddrNotSupported(addr)) => { debug!( diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 5aa53c91c78..b39c69b57cf 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -292,7 +292,7 @@ impl crate::Transport for Transport { fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { @@ -300,14 +300,14 @@ impl crate::Transport for Transport { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } - self.inner.dial(addr, dial_opts) + self.inner.dial(addr, opts) } Some(Protocol::Ip6(a)) => { if !ipv6_global::is_global(a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } - self.inner.dial(addr, dial_opts) + self.inner.dial(addr, opts) } _ => { debug!("Not dialing unsupported Multiaddress {:?}.", addr); diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index 4c9d4230be6..bac23f454d9 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -20,7 +20,7 @@ use crate::transport::DialOpts; use crate::{ - connection::{ConnectedPoint, Endpoint}, + connection::ConnectedPoint, transport::{Transport, TransportError, TransportEvent}, }; use futures::prelude::*; @@ -77,13 +77,13 @@ where fn dial( &mut self, addr: Multiaddr, - dial_opts: DialOpts, + opts: DialOpts, ) -> Result> { - let future = self.transport.dial(addr.clone(), dial_opts)?; + let future = self.transport.dial(addr.clone(), opts)?; let p = ConnectedPoint::Dialer { address: addr, - role_override: dial_opts.endpoint, - port_use: dial_opts.port_use, + role_override: opts.endpoint, + port_use: opts.port_use, }; Ok(MapFuture { inner: future, diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 553e59ea7ed..f6963aa53a8 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -36,8 +36,7 @@ use crate::record_priv::{ use crate::K_VALUE; use fnv::{FnvHashMap, FnvHashSet}; use instant::Instant; -use libp2p_core::transport::PortUse; -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm, diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index b14c964ba5d..2b73ae6dbdc 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -24,7 +24,6 @@ mod listen_addresses; pub mod toggle; pub use external_addresses::ExternalAddresses; -use libp2p_core::transport::PortUse; pub use listen_addresses::ListenAddresses; use crate::connection::ConnectionId; @@ -34,7 +33,10 @@ use crate::{ ConnectionDenied, ConnectionHandler, DialError, ListenError, THandler, THandlerInEvent, THandlerOutEvent, }; -use libp2p_core::{transport::ListenerId, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{ + transport::{ListenerId, PortUse}, + ConnectedPoint, Endpoint, Multiaddr, +}; use libp2p_identity::PeerId; use std::{task::Context, task::Poll}; diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 25147208483..8e992acf284 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -143,7 +143,7 @@ use libp2p_core::{ connection::ConnectedPoint, multiaddr, muxing::StreamMuxerBox, - transport::{self, DialOpts as TransportDialOpts, ListenerId, TransportError, TransportEvent}, + transport::{self, ListenerId, TransportError, TransportEvent}, Multiaddr, Transport, }; use libp2p_identity::PeerId; @@ -527,14 +527,13 @@ where .into_iter() .map(|a| match p2p_addr(peer_id, a) { Ok(address) => { - let transport_dial_opts = { - let transport_port_use = dial_opts.port_use(); - TransportDialOpts { + let dial = self.transport.dial( + address.clone(), + transport::DialOpts { endpoint: dial_opts.role_override(), - port_use: transport_port_use, - } - }; - let dial = self.transport.dial(address.clone(), transport_dial_opts); + port_use: dial_opts.port_use(), + }, + ); match dial { Ok(fut) => fut .map(|r| (address, r.map_err(TransportError::Other))) From 49a0d3e1dd179ef65e3524bfa53657389f843d76 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 21 Oct 2023 18:34:11 +0200 Subject: [PATCH 027/179] Remove some stuff, make tests pass --- core/src/transport.rs | 6 ++++ core/src/transport/memory.rs | 46 +++++++++++++++++++++++++--- core/tests/transport_upgrade.rs | 15 +++++++-- transports/tcp/src/lib.rs | 54 +++++++++++++++++++++++---------- 4 files changed, 98 insertions(+), 23 deletions(-) diff --git a/core/src/transport.rs b/core/src/transport.rs index c3db17be83e..bbbc191ce8a 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -70,9 +70,15 @@ pub enum PortUse { Reuse, } +/// Options to customize the behaviour during dialing. Through it one can specify: +/// - The role of the endpoint (dialer or listener). +/// - The port use policy. #[derive(Debug, Copy, Clone)] pub struct DialOpts { + /// The endpoint establishing a new connection. This option removes the necessity for the + /// `dial_as_listener` API. pub endpoint: Endpoint, + /// The port use policy for a new connection. pub port_use: PortUse, } diff --git a/core/src/transport/memory.rs b/core/src/transport/memory.rs index 4f6c23c7fea..fe46c85002a 100644 --- a/core/src/transport/memory.rs +++ b/core/src/transport/memory.rs @@ -413,6 +413,8 @@ impl Drop for Chan { #[cfg(test)] mod tests { + use crate::{transport::PortUse, Endpoint}; + use super::*; #[test] @@ -497,7 +499,13 @@ mod tests { fn port_not_in_use() { let mut transport = MemoryTransport::default(); assert!(transport - .dial("/memory/810172461024613".parse().unwrap()) + .dial( + "/memory/810172461024613".parse().unwrap(), + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New + } + ) .is_err()); transport .listen_on( @@ -506,7 +514,13 @@ mod tests { ) .unwrap(); assert!(transport - .dial("/memory/810172461024613".parse().unwrap()) + .dial( + "/memory/810172461024613".parse().unwrap(), + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New + } + ) .is_ok()); } @@ -573,7 +587,17 @@ mod tests { let mut t2 = MemoryTransport::default(); let dialer = async move { - let mut socket = t2.dial(cloned_t1_addr).unwrap().await.unwrap(); + let mut socket = t2 + .dial( + cloned_t1_addr, + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); socket.write_all(&msg).await.unwrap(); }; @@ -609,7 +633,13 @@ mod tests { let dialer = async move { MemoryTransport::default() - .dial(listener_addr_cloned) + .dial( + listener_addr_cloned, + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap() .await .unwrap(); @@ -660,7 +690,13 @@ mod tests { let dialer = async move { let chan = MemoryTransport::default() - .dial(listener_addr_cloned) + .dial( + listener_addr_cloned, + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap() .await .unwrap(); diff --git a/core/tests/transport_upgrade.rs b/core/tests/transport_upgrade.rs index 193ee73cbc8..21c21a8fc18 100644 --- a/core/tests/transport_upgrade.rs +++ b/core/tests/transport_upgrade.rs @@ -19,8 +19,9 @@ // DEALINGS IN THE SOFTWARE. use futures::prelude::*; -use libp2p_core::transport::{ListenerId, MemoryTransport, Transport}; +use libp2p_core::transport::{DialOpts, ListenerId, MemoryTransport, PortUse, Transport}; use libp2p_core::upgrade::{self, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; +use libp2p_core::Endpoint; use libp2p_identity as identity; use libp2p_mplex::MplexConfig; use libp2p_noise as noise; @@ -119,7 +120,17 @@ fn upgrade_pipeline() { }; let client = async move { - let (peer, _mplex) = dialer_transport.dial(listen_addr2).unwrap().await.unwrap(); + let (peer, _mplex) = dialer_transport + .dial( + listen_addr2, + DialOpts { + endpoint: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); assert_eq!(peer, listener_id); }; diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 9b2fddb1243..74daff0a517 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -79,7 +79,8 @@ struct PortReuse { /// The addresses and ports of the listening sockets /// registered as eligible for port reuse when dialing listen_addrs: Arc>>, - dialed_as_listener: Arc>>, + /// Contains a hashset of all multiaddr that where dialed from a reused port. + addr_dialed_from_reuse_port: Arc>>, } impl PortReuse { @@ -132,15 +133,15 @@ impl PortReuse { None } - fn dialed_from_listener(&self, addr: Multiaddr) { - self.dialed_as_listener + fn dialed_from_reuse_port(&self, addr: Multiaddr) { + self.addr_dialed_from_reuse_port .write() .expect("`dialed_as_listener` never panic while holding the lock") .insert(addr); } - fn already_dialed_as_listener(&self, addr: &Multiaddr) -> bool { - self.dialed_as_listener + fn was_dialed_from_reuse_port(&self, addr: &Multiaddr) -> bool { + self.addr_dialed_from_reuse_port .read() .expect("`already_dialed_as_listener` never panic while holding the lock") .contains(addr) @@ -183,6 +184,31 @@ impl Config { self.backlog = backlog; self } + + /// Configures port reuse for local sockets, which implies + /// reuse of listening ports for outgoing connections to + /// enhance NAT traversal capabilities. + /// + /// # Deprecation Notice + /// + /// The new implementation works on a per-connaction basis, defined by the behaviour. This + /// removes the necessaity to configure the transport for port reuse, instead the behaviour + /// requiring this behaviour can decide wether to use port reuse or not. + /// + /// The API to configure port reuse is part of [`Transport`] and the option can be found in + /// [`libp2p_core::transport::DialOpts`]. + /// + /// If [`PortUse::Reuse`] is enabled, the transport will try to reuse the local port of the + /// listener. If that's not possible, i.e. there is no listener or the transport doesn't allow + /// a direct control over ports, a new port (or the default behaviour) is used. If port reuse + /// is enabled for a connection, this option will be treated on a best-effor basis. + #[deprecated( + since = "0.42.0", + note = "This option does nothing now, since the port reuse policy is now decided on a per-connection basis by the behaviour. The function will be removed in a future release." + )] + pub fn port_reuse(self, _port_reuse: bool) -> Self { + self + } } impl Default for Config { @@ -227,10 +253,8 @@ where /// - [`tokio::Transport::new`] /// - [`async_io::Transport::new`] pub fn new(config: Config) -> Self { - let port_reuse = PortReuse::default(); Transport { config, - port_reuse, ..Default::default() } } @@ -297,11 +321,9 @@ where /// /// This transport will have port-reuse disabled. fn default() -> Self { - let config = Config::default(); - let port_reuse = Default::default(); Transport { - port_reuse, - config, + port_reuse: PortReuse::default(), + config: Config::default(), listeners: SelectAll::new(), pending_events: VecDeque::new(), } @@ -371,7 +393,7 @@ where socket .bind(&socket_addr.into()) .map_err(TransportError::Other)?; - self.port_reuse.dialed_from_listener(addr); + self.port_reuse.dialed_from_reuse_port(addr); } _ => {} } @@ -417,11 +439,11 @@ where if !is_tcp_addr(listen) || !is_tcp_addr(observed) { return None; } - if self.port_reuse.already_dialed_as_listener(&observed) { - Some(observed.clone()) - } else { - address_translation(listen, observed) + if self.port_reuse.was_dialed_from_reuse_port(observed) { + return Some(observed.clone()); } + + address_translation(listen, observed) } /// Poll all listeners. From 23207bdec9ea832a8db83657ed5b3f9a6be0c60d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 22 Oct 2023 15:21:21 +0200 Subject: [PATCH 028/179] Address another issue --- swarm/tests/swarm_derive.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swarm/tests/swarm_derive.rs b/swarm/tests/swarm_derive.rs index d0680591621..df214f96497 100644 --- a/swarm/tests/swarm_derive.rs +++ b/swarm/tests/swarm_derive.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use futures::StreamExt; -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{Endpoint, Multiaddr, transport::PortUse}; use libp2p_identify as identify; use libp2p_ping as ping; use libp2p_swarm::{ @@ -485,6 +485,7 @@ fn custom_out_event_no_type_parameters() { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } From 9597d5380e8198f828ca17c56b93c45513854a5a Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:27:41 +0200 Subject: [PATCH 029/179] Apply suggestions from code review Co-authored-by: Thomas Eizinger --- core/src/transport.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/transport.rs b/core/src/transport.rs index bbbc191ce8a..c5d1ecfa8ab 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -70,13 +70,13 @@ pub enum PortUse { Reuse, } -/// Options to customize the behaviour during dialing. Through it one can specify: -/// - The role of the endpoint (dialer or listener). -/// - The port use policy. +/// Options to customize the behaviour during dialing. #[derive(Debug, Copy, Clone)] pub struct DialOpts { - /// The endpoint establishing a new connection. This option removes the necessity for the - /// `dial_as_listener` API. + /// The endpoint establishing a new connection. + /// + /// When attempting a hole-punch, both parties simultaneously "dial" each other but one party has to be the "listener" on the final connection. + /// This option specifies the role of this node in the final connection. pub endpoint: Endpoint, /// The port use policy for a new connection. pub port_use: PortUse, From 6352e659eec6db9378ceb977bc651f5e225af791 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:30:59 +0200 Subject: [PATCH 030/179] Implemented really the lateset review --- core/src/connection.rs | 2 ++ core/src/transport.rs | 2 +- core/src/transport/and_then.rs | 5 ++--- core/src/transport/global_only.rs | 3 +-- core/src/transport/map.rs | 2 +- core/src/transport/memory.rs | 10 +++++----- core/tests/transport_upgrade.rs | 2 +- swarm/src/lib.rs | 2 +- swarm/tests/swarm_derive.rs | 2 +- transports/tcp/src/lib.rs | 6 +++--- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/src/connection.rs b/core/src/connection.rs index 09dbfc9928b..f03e881884d 100644 --- a/core/src/connection.rs +++ b/core/src/connection.rs @@ -1,3 +1,5 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation diff --git a/core/src/transport.rs b/core/src/transport.rs index c5d1ecfa8ab..134181a04a3 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -77,7 +77,7 @@ pub struct DialOpts { /// /// When attempting a hole-punch, both parties simultaneously "dial" each other but one party has to be the "listener" on the final connection. /// This option specifies the role of this node in the final connection. - pub endpoint: Endpoint, + pub role: Endpoint, /// The port use policy for a new connection. pub port_use: PortUse, } diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index 8d2b8174665..128f6952a1a 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -18,10 +18,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::DialOpts; use crate::{ connection::ConnectedPoint, - transport::{ListenerId, Transport, TransportError, TransportEvent}, + transport::{ListenerId, Transport, TransportError, TransportEvent, DialOpts}, }; use either::Either; use futures::prelude::*; @@ -84,7 +83,7 @@ where self.fun.clone(), ConnectedPoint::Dialer { address: addr, - role_override: opts.endpoint, + role_override: opts.role, port_use: opts.port_use, }, )), diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index b39c69b57cf..40e99c6a31b 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -18,10 +18,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::transport::DialOpts; use crate::{ multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{ListenerId, TransportError, TransportEvent, DialOpts}, }; use log::debug; use std::{ diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index bac23f454d9..a7505d36e6c 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -82,7 +82,7 @@ where let future = self.transport.dial(addr.clone(), opts)?; let p = ConnectedPoint::Dialer { address: addr, - role_override: opts.endpoint, + role_override: opts.role, port_use: opts.port_use, }; Ok(MapFuture { diff --git a/core/src/transport/memory.rs b/core/src/transport/memory.rs index fe46c85002a..b81d7149c95 100644 --- a/core/src/transport/memory.rs +++ b/core/src/transport/memory.rs @@ -502,7 +502,7 @@ mod tests { .dial( "/memory/810172461024613".parse().unwrap(), DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New } ) @@ -517,7 +517,7 @@ mod tests { .dial( "/memory/810172461024613".parse().unwrap(), DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New } ) @@ -591,7 +591,7 @@ mod tests { .dial( cloned_t1_addr, DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New, }, ) @@ -636,7 +636,7 @@ mod tests { .dial( listener_addr_cloned, DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New, }, ) @@ -693,7 +693,7 @@ mod tests { .dial( listener_addr_cloned, DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New, }, ) diff --git a/core/tests/transport_upgrade.rs b/core/tests/transport_upgrade.rs index aa63f3db7bf..31bc033bdb9 100644 --- a/core/tests/transport_upgrade.rs +++ b/core/tests/transport_upgrade.rs @@ -126,7 +126,7 @@ fn upgrade_pipeline() { .dial( listen_addr2, DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New, }, ) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 8e992acf284..a33324782c7 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -530,7 +530,7 @@ where let dial = self.transport.dial( address.clone(), transport::DialOpts { - endpoint: dial_opts.role_override(), + role: dial_opts.role_override(), port_use: dial_opts.port_use(), }, ); diff --git a/swarm/tests/swarm_derive.rs b/swarm/tests/swarm_derive.rs index df214f96497..678a5938808 100644 --- a/swarm/tests/swarm_derive.rs +++ b/swarm/tests/swarm_derive.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use futures::StreamExt; -use libp2p_core::{Endpoint, Multiaddr, transport::PortUse}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identify as identify; use libp2p_ping as ping; use libp2p_swarm::{ diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 74daff0a517..f5b276290bc 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -842,7 +842,7 @@ mod tests { .dial( addr.clone(), DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::Reuse, }, ) @@ -925,7 +925,7 @@ mod tests { tcp.dial( dest_addr, DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::New, }, ) @@ -1032,7 +1032,7 @@ mod tests { .dial( dest_addr, DialOpts { - endpoint: Endpoint::Dialer, + role: Endpoint::Dialer, port_use: PortUse::Reuse, }, ) From 6cc5d7cb5913f0828828b0cc31ca9ea15bea8c7c Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:05:54 +0100 Subject: [PATCH 031/179] Implement the request response part of AutoNATv2 --- Cargo.lock | 14 + Cargo.toml | 1 + protocols/autonatv2/Cargo.toml | 24 ++ protocols/autonatv2/src/dial_back.rs | 0 protocols/autonatv2/src/generated/mod.rs | 2 + .../autonatv2/src/generated/structs.proto | 57 +++ protocols/autonatv2/src/generated/structs.rs | 338 ++++++++++++++++++ protocols/autonatv2/src/lib.rs | 18 + protocols/autonatv2/src/request_response.rs | 309 ++++++++++++++++ 9 files changed, 763 insertions(+) create mode 100644 protocols/autonatv2/Cargo.toml create mode 100644 protocols/autonatv2/src/dial_back.rs create mode 100644 protocols/autonatv2/src/generated/mod.rs create mode 100644 protocols/autonatv2/src/generated/structs.proto create mode 100644 protocols/autonatv2/src/generated/structs.rs create mode 100644 protocols/autonatv2/src/lib.rs create mode 100644 protocols/autonatv2/src/request_response.rs diff --git a/Cargo.lock b/Cargo.lock index 0172af429ee..e04b81e11d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2443,6 +2443,20 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "libp2p-autonatv2" +version = "0.1.0" +dependencies = [ + "async-trait", + "futures", + "libp2p-core", + "libp2p-request-response", + "libp2p-swarm", + "quick-protobuf", + "rand 0.8.5", + "rand_core 0.6.4", +] + [[package]] name = "libp2p-connection-limits" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 2c823756bbe..fd3857ab685 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "muxers/test-harness", "muxers/yamux", "protocols/autonat", + "protocols/autonatv2", "protocols/dcutr", "protocols/floodsub", "protocols/gossipsub", diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml new file mode 100644 index 00000000000..319bab8e83e --- /dev/null +++ b/protocols/autonatv2/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "libp2p-autonatv2" +version = "0.1.0" +edition = "2021" +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +quick-protobuf = "0.8" +libp2p-request-response = { path = "../request-response" } +libp2p-core = { path = "../../core" } +async-trait = "0.1" +futures = "0.3" +rand_core = "0.6" +rand = { version = "0.8", optional = true } +libp2p-swarm = { version = "0.44.0", path = "../../swarm" } + +[lints] +workspace = true + +[features] +default = ["rand"] +rand = ["dep:rand"] diff --git a/protocols/autonatv2/src/dial_back.rs b/protocols/autonatv2/src/dial_back.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/protocols/autonatv2/src/generated/mod.rs b/protocols/autonatv2/src/generated/mod.rs new file mode 100644 index 00000000000..74078a88020 --- /dev/null +++ b/protocols/autonatv2/src/generated/mod.rs @@ -0,0 +1,2 @@ +// Automatically generated mod.rs +pub(crate) mod structs; diff --git a/protocols/autonatv2/src/generated/structs.proto b/protocols/autonatv2/src/generated/structs.proto new file mode 100644 index 00000000000..46f5f38f1b6 --- /dev/null +++ b/protocols/autonatv2/src/generated/structs.proto @@ -0,0 +1,57 @@ +syntax = "proto2"; + +package structs; + + +message Message { + oneof msg { + DialRequest dialRequest = 1; + DialResponse dialResponse = 2; + DialDataRequest dialDataRequest = 3; + DialDataResponse dialDataResponse = 4; + } +} + + +message DialRequest { + repeated bytes addrs = 1; + fixed64 nonce = 2; +} + + +message DialDataRequest { + uint32 addrIdx = 1; + uint64 numBytes = 2; +} + + +enum DialStatus { + UNUSED = 0; + E_DIAL_ERROR = 100; + E_DIAL_BACK_ERROR = 101; + OK = 200; +} + + +message DialResponse { + enum ResponseStatus { + E_INTERNAL_ERROR = 0; + E_REQUEST_REJECTED = 100; + E_DIAL_REFUSED = 101; + OK = 200; + } + + ResponseStatus status = 1; + uint32 addrIdx = 2; + DialStatus dialStatus = 3; +} + + +message DialDataResponse { + bytes data = 1; +} + + +message DialBack { + fixed64 nonce = 1; +} diff --git a/protocols/autonatv2/src/generated/structs.rs b/protocols/autonatv2/src/generated/structs.rs new file mode 100644 index 00000000000..3fd8956178c --- /dev/null +++ b/protocols/autonatv2/src/generated/structs.rs @@ -0,0 +1,338 @@ +// Automatically generated rust module for 'structs.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use std::borrow::Cow; +use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::*; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum DialStatus { + UNUSED = 0, + E_DIAL_ERROR = 100, + E_DIAL_BACK_ERROR = 101, + OK = 200, +} + +impl Default for DialStatus { + fn default() -> Self { + DialStatus::UNUSED + } +} + +impl From for DialStatus { + fn from(i: i32) -> Self { + match i { + 0 => DialStatus::UNUSED, + 100 => DialStatus::E_DIAL_ERROR, + 101 => DialStatus::E_DIAL_BACK_ERROR, + 200 => DialStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for DialStatus { + fn from(s: &'a str) -> Self { + match s { + "UNUSED" => DialStatus::UNUSED, + "E_DIAL_ERROR" => DialStatus::E_DIAL_ERROR, + "E_DIAL_BACK_ERROR" => DialStatus::E_DIAL_BACK_ERROR, + "OK" => DialStatus::OK, + _ => Self::default(), + } + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct Message<'a> { + pub(crate) msg: structs::mod_Message::OneOfmsg<'a>, +} + +impl<'a> MessageRead<'a> for Message<'a> { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.msg = structs::mod_Message::OneOfmsg::dialRequest(r.read_message::(bytes)?), + Ok(18) => msg.msg = structs::mod_Message::OneOfmsg::dialResponse(r.read_message::(bytes)?), + Ok(26) => msg.msg = structs::mod_Message::OneOfmsg::dialDataRequest(r.read_message::(bytes)?), + Ok(34) => msg.msg = structs::mod_Message::OneOfmsg::dialDataResponse(r.read_message::(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl<'a> MessageWrite for Message<'a> { + fn get_size(&self) -> usize { + 0 + + match self.msg { + structs::mod_Message::OneOfmsg::dialRequest(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialResponse(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::None => 0, + } } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + match self.msg { structs::mod_Message::OneOfmsg::dialRequest(ref m) => { w.write_with_tag(10, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialResponse(ref m) => { w.write_with_tag(18, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => { w.write_with_tag(26, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => { w.write_with_tag(34, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::None => {}, + } Ok(()) + } +} + +pub(crate) mod mod_Message { + +use super::*; + +#[derive(Debug, PartialEq, Clone)] +pub(crate) enum OneOfmsg<'a> { + dialRequest(structs::DialRequest<'a>), + dialResponse(structs::DialResponse), + dialDataRequest(structs::DialDataRequest), + dialDataResponse(structs::DialDataResponse<'a>), + None, +} + +impl<'a> Default for OneOfmsg<'a> { + fn default() -> Self { + OneOfmsg::None + } +} + +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialRequest<'a> { + pub(crate) addrs: Vec>, + pub(crate) nonce: Option, +} + +impl<'a> MessageRead<'a> for DialRequest<'a> { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.addrs.push(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl<'a> MessageWrite for DialRequest<'a> { + fn get_size(&self) -> usize { + 0 + + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() + + self.nonce.as_ref().map_or(0, |_| 1 + 8) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + for s in &self.addrs { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + if let Some(ref s) = self.nonce { w.write_with_tag(17, |w| w.write_fixed64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialDataRequest { + pub(crate) addrIdx: Option, + pub(crate) numBytes: Option, +} + +impl<'a> MessageRead<'a> for DialDataRequest { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.addrIdx = Some(r.read_uint32(bytes)?), + Ok(16) => msg.numBytes = Some(r.read_uint64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialDataRequest { + fn get_size(&self) -> usize { + 0 + + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.numBytes.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.addrIdx { w.write_with_tag(8, |w| w.write_uint32(*s))?; } + if let Some(ref s) = self.numBytes { w.write_with_tag(16, |w| w.write_uint64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialResponse { + pub(crate) status: Option, + pub(crate) addrIdx: Option, + pub(crate) dialStatus: Option, +} + +impl<'a> MessageRead<'a> for DialResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(16) => msg.addrIdx = Some(r.read_uint32(bytes)?), + Ok(24) => msg.dialStatus = Some(r.read_enum(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.dialStatus.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + if let Some(ref s) = self.addrIdx { w.write_with_tag(16, |w| w.write_uint32(*s))?; } + if let Some(ref s) = self.dialStatus { w.write_with_tag(24, |w| w.write_enum(*s as i32))?; } + Ok(()) + } +} + +pub(crate) mod mod_DialResponse { + + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum ResponseStatus { + E_INTERNAL_ERROR = 0, + E_REQUEST_REJECTED = 100, + E_DIAL_REFUSED = 101, + OK = 200, +} + +impl Default for ResponseStatus { + fn default() -> Self { + ResponseStatus::E_INTERNAL_ERROR + } +} + +impl From for ResponseStatus { + fn from(i: i32) -> Self { + match i { + 0 => ResponseStatus::E_INTERNAL_ERROR, + 100 => ResponseStatus::E_REQUEST_REJECTED, + 101 => ResponseStatus::E_DIAL_REFUSED, + 200 => ResponseStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for ResponseStatus { + fn from(s: &'a str) -> Self { + match s { + "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, + "E_REQUEST_REJECTED" => ResponseStatus::E_REQUEST_REJECTED, + "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, + "OK" => ResponseStatus::OK, + _ => Self::default(), + } + } +} + +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialDataResponse<'a> { + pub(crate) data: Option>, +} + +impl<'a> MessageRead<'a> for DialDataResponse<'a> { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.data = Some(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl<'a> MessageWrite for DialDataResponse<'a> { + fn get_size(&self) -> usize { + 0 + + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.data { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialBack { + pub(crate) nonce: Option, +} + +impl<'a> MessageRead<'a> for DialBack { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(9) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialBack { + fn get_size(&self) -> usize { + 0 + + self.nonce.as_ref().map_or(0, |_| 1 + 8) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.nonce { w.write_with_tag(9, |w| w.write_fixed64(*s))?; } + Ok(()) + } +} + diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs new file mode 100644 index 00000000000..c0825159cf4 --- /dev/null +++ b/protocols/autonatv2/src/lib.rs @@ -0,0 +1,18 @@ +pub(crate) mod request_response; +pub(crate) mod dial_back; +mod generated; + +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs new file mode 100644 index 00000000000..9d4a5d26a59 --- /dev/null +++ b/protocols/autonatv2/src/request_response.rs @@ -0,0 +1,309 @@ +use std::{borrow::Cow, io, sync::OnceLock}; + +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use libp2p_core::{ + upgrade::{read_length_prefixed, write_length_prefixed}, + Multiaddr, +}; +use libp2p_request_response::{Behaviour, Codec}; +use libp2p_swarm::{ConnectionId, NetworkBehaviour, StreamProtocol}; +use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; +use rand::Rng; + +use crate::generated::structs as proto; + +const REQUEST_MAX_SIZE: usize = 4104; +const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; +const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; +const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; + +pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-request"); + +macro_rules! new_io_invalid_data_err { + ($msg:expr) => { + io::Error::new(io::ErrorKind::InvalidData, $msg) + }; +} + +macro_rules! check_existence { + ($field:ident) => { + $field.ok_or_else(|| new_io_invalid_data_err!(concat!(stringify!($field), " is missing"))) + }; +} + +#[derive(Debug, Clone)] +pub(crate) enum Request { + Dial(DialRequest), + Data(DialDataResponse), +} + +#[derive(Debug, Clone)] +pub(crate) struct DialRequest { + pub(crate) addrs: Vec, + pub(crate) nonce: u64, +} + +#[derive(Debug, Clone)] +pub(crate) struct DialDataResponse { + pub(crate) data_count: usize, +} + +impl Request { + fn from_bytes(bytes: &[u8]) -> io::Result { + let mut reader = BytesReader::from_bytes(bytes); + let msg = proto::Message::from_reader(&mut reader, bytes) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + match msg.msg { + proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { + let addrs: Vec = addrs + .into_iter() + .map(|e| e.to_vec()) + .map(|e| { + Multiaddr::try_from(e).map_err(|err| { + new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + }) + }) + .collect::, io::Error>>()?; + let nonce = check_existence!(nonce)?; + Ok(Self::Dial(DialRequest { addrs, nonce })) + } + proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { + let data_count = check_existence!(data)?.len(); + Ok(Self::Data(DialDataResponse { data_count })) + } + _ => Err(new_io_invalid_data_err!( + "invalid message type, expected dialRequest or dialDataResponse" + )), + } + } + + fn into_bytes(self) -> Cow<'static, [u8]> { + fn make_message_bytes(request: Request) -> Vec { + let msg = match request { + Request::Dial(DialRequest { addrs, nonce }) => { + let addrs = addrs.iter().map(|e| e.to_vec().into()).collect(); + let nonce = Some(nonce); + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { + addrs, + nonce, + }), + } + } + Request::Data(DialDataResponse { data_count }) => { + assert!( + data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse( + proto::DialDataResponse { + data: Some(Cow::Borrowed(&DATA[..data_count])), + }, + ), + } + } + }; + let mut buf = Vec::with_capacity(msg.get_size()); + let mut writer = Writer::new(&mut buf); + msg.write_message(&mut writer).expect("encoding to succeed"); + buf + } + // little optimization: if the data is exactly 4096 bytes, we can use a static buffer. It is + // likely that this is the case, draining the most performance. + if matches!( + self, + Self::Data(DialDataResponse { + data_count: DATA_FIELD_LEN_UPPER_BOUND + }) + ) { + static CELL: OnceLock> = OnceLock::new(); + CELL.get_or_init(move || make_message_bytes(self)).into() + } else { + make_message_bytes(self).into() + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum Response { + Dial(DialResponse), + Data(DialDataRequest), +} + +#[derive(Debug, Clone)] +pub(crate) struct DialDataRequest { + pub(crate) addr_idx: usize, + pub(crate) num_bytes: usize, +} + +#[derive(Debug, Clone)] +pub(crate) struct DialResponse { + pub(crate) status: proto::mod_DialResponse::ResponseStatus, + pub(crate) addr_idx: usize, + pub(crate) dial_status: proto::DialStatus, +} + +impl Response { + fn from_bytes(bytes: &[u8]) -> std::io::Result { + let mut reader = BytesReader::from_bytes(bytes); + let msg = proto::Message::from_reader(&mut reader, bytes) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + + match msg.msg { + proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { + status, + addrIdx, + dialStatus, + }) => { + let status = check_existence!(status)?; + let addr_idx = check_existence!(addrIdx)? as usize; + let dial_status = check_existence!(dialStatus)?; + Ok(Self::Dial(DialResponse { + status, + addr_idx, + dial_status, + })) + } + proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx, + numBytes, + }) => { + let addr_idx = check_existence!(addrIdx)? as usize; + let num_bytes = check_existence!(numBytes)? as usize; + Ok(Self::Data(DialDataRequest { + addr_idx, + num_bytes, + })) + } + _ => Err(new_io_invalid_data_err!( + "invalid message type, expected dialResponse or dialDataRequest" + )), + } + } + + fn into_bytes(self) -> Vec { + let msg = match self { + Self::Dial(DialResponse { + status, + addr_idx, + dial_status, + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { + status: Some(status), + addrIdx: Some(addr_idx as u32), + dialStatus: Some(dial_status), + }), + }, + Self::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + }, + }; + let mut buf = Vec::with_capacity(msg.get_size()); + let mut writer = Writer::new(&mut buf); + msg.write_message(&mut writer).expect("encoding to succeed"); + buf + } +} + +impl DialDataRequest { + pub(crate) fn from_rng(addr_idx: usize, mut rng: R) -> Self { + let num_bytes = rng.gen_range(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND); + Self { + addr_idx, + num_bytes, + } + } + + #[cfg(any(doc, feature = "rand"))] + pub(crate) fn new(addr_idx: usize) -> Self { + Self::from_rng(addr_idx, rand::thread_rng()) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct AutoNATv2Codec { + observed_addr: Multiaddr, + connection_id: ConnectionId, +} + +#[async_trait::async_trait] +impl Codec for AutoNATv2Codec { + type Protocol = String; + type Request = Request; + type Response = Response; + + async fn read_request( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let bytes = read_length_prefixed(io, REQUEST_MAX_SIZE).await?; + Request::from_bytes(&bytes) + } + + async fn read_response( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Unpin + Send, + { + let bytes = read_length_prefixed(io, 1024).await?; + Response::from_bytes(&bytes) + } + + async fn write_request( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + write_length_prefixed(io, req.into_bytes()).await?; + io.close().await + } + + async fn write_response( + &mut self, + _protocol: &Self::Protocol, + io: &mut T, + res: Self::Response, + ) -> std::io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + write_length_prefixed(io, res.into_bytes()).await?; + io.close().await + } +} + +#[cfg(test)] +mod tests { + use crate::generated::structs::{mod_Message::OneOfmsg, DialDataResponse, Message}; + + #[test] + fn message_correct_max_size() { + let message_bytes = quick_protobuf::serialize_into_vec(&Message { + msg: OneOfmsg::dialDataResponse(DialDataResponse { + data: Some(vec![0; 4096].into()), + }), + }) + .unwrap(); + assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); + } +} From 6d44a1e0bf088986eaa20da00a7fcb48903eea72 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:56:52 +0100 Subject: [PATCH 032/179] First steps of implementing the client --- Cargo.lock | 13 +- Cargo.toml | 4 - protocols/autonatv2/Cargo.toml | 13 +- protocols/autonatv2/src/client.rs | 38 ++ protocols/autonatv2/src/client/behaviour.rs | 106 ++++++ protocols/autonatv2/src/client/handler.rs | 395 ++++++++++++++++++++ protocols/autonatv2/src/dial_back.rs | 0 protocols/autonatv2/src/lib.rs | 20 +- protocols/autonatv2/src/request_response.rs | 135 ++++--- 9 files changed, 653 insertions(+), 71 deletions(-) create mode 100644 protocols/autonatv2/src/client.rs create mode 100644 protocols/autonatv2/src/client/behaviour.rs create mode 100644 protocols/autonatv2/src/client/handler.rs delete mode 100644 protocols/autonatv2/src/dial_back.rs diff --git a/Cargo.lock b/Cargo.lock index e04b81e11d8..f649e6d236a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2448,13 +2448,18 @@ name = "libp2p-autonatv2" version = "0.1.0" dependencies = [ "async-trait", + "either", "futures", + "futures-bounded", "libp2p-core", - "libp2p-request-response", + "libp2p-identity", "libp2p-swarm", "quick-protobuf", "rand 0.8.5", "rand_core 0.6.4", + "scc", + "thiserror", + "void", ] [[package]] @@ -4872,6 +4877,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scc" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca628bbcc4be16ffaeae429444cf4538d7a23b1aa5457cc9ce9a220286befbc3" + [[package]] name = "schannel" version = "0.1.22" diff --git a/Cargo.toml b/Cargo.toml index fd3857ab685..1eb6ffc392a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,9 +88,7 @@ libp2p-mdns = { version = "0.45.0", path = "protocols/mdns" } libp2p-memory-connection-limits = { version = "0.2.0", path = "misc/memory-connection-limits" } libp2p-metrics = { version = "0.14.0", path = "misc/metrics" } libp2p-mplex = { version = "0.41.0", path = "muxers/mplex" } -libp2p-muxer-test-harness = { path = "muxers/test-harness" } libp2p-noise = { version = "0.44.0", path = "transports/noise" } -libp2p-perf = { version = "0.3.0", path = "protocols/perf" } libp2p-ping = { version = "0.44.0", path = "protocols/ping" } libp2p-plaintext = { version = "0.41.0", path = "transports/plaintext" } libp2p-pnet = { version = "0.24.0", path = "transports/pnet" } @@ -98,10 +96,8 @@ libp2p-quic = { version = "0.10.0", path = "transports/quic" } libp2p-relay = { version = "0.17.0", path = "protocols/relay" } libp2p-rendezvous = { version = "0.14.0", path = "protocols/rendezvous" } libp2p-request-response = { version = "0.26.0", path = "protocols/request-response" } -libp2p-server = { version = "0.12.3", path = "misc/server" } libp2p-swarm = { version = "0.44.0", path = "swarm" } libp2p-swarm-derive = { version = "0.34.0", path = "swarm-derive" } -libp2p-swarm-test = { version = "0.3.0", path = "swarm-test" } libp2p-tcp = { version = "0.41.0", path = "transports/tcp" } libp2p-tls = { version = "0.3.0", path = "transports/tls" } libp2p-uds = { version = "0.40.0", path = "transports/uds" } diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 319bab8e83e..7792cf97239 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -8,13 +8,18 @@ rust-version.workspace = true [dependencies] quick-protobuf = "0.8" -libp2p-request-response = { path = "../request-response" } -libp2p-core = { path = "../../core" } +libp2p-core = { workspace = true } async-trait = "0.1" -futures = "0.3" rand_core = "0.6" rand = { version = "0.8", optional = true } -libp2p-swarm = { version = "0.44.0", path = "../../swarm" } +libp2p-swarm = { workspace = true } +libp2p-identity = { workspace = true } +futures-bounded = { workspace = true } +void = "1.0.2" +either = "1.9.0" +futures = "0.3.29" +thiserror = "1.0.50" +scc = "2.0.3" [lints] workspace = true diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs new file mode 100644 index 00000000000..350afcd31cf --- /dev/null +++ b/protocols/autonatv2/src/client.rs @@ -0,0 +1,38 @@ +use libp2p_core::Multiaddr; + +use crate::request_response::{DialRequest, DialResponse}; + +mod behaviour; +mod handler; + +#[derive(Debug)] +enum ToBehaviour { + ResponseInfo(ResponseInfo), + OutboundError(E), +} + +#[derive(Debug)] +struct ResponseInfo { + response: DialResponse, + suspicious_addrs: Vec, + successfull_addr: Option, +} + +impl ResponseInfo { + fn new( + response: DialResponse, + suspicious_addrs: Vec, + successfull_addr: Option, + ) -> Self { + Self { + response, + suspicious_addrs, + successfull_addr, + } + } +} + +#[derive(Debug)] +enum FromBehaviour { + Dial(DialRequest), +} diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs new file mode 100644 index 00000000000..5351453b141 --- /dev/null +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -0,0 +1,106 @@ +use std::{ + collections::{HashSet, VecDeque}, + sync::Arc, + task::{Context, Poll}, +}; + +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, ToSwarm, +}; + +use crate::{ + client::{ResponseInfo, ToBehaviour}, + generated::structs::DialStatus, + request_response::DialResponse, +}; + +use super::handler::Handler; + +enum Command { + TestListenerReachability { + server_peer: PeerId, + local_addrs: Vec, + }, +} + +pub struct ReachabilityTestSucc { + pub server_peer: PeerId, + pub local_addr: Multiaddr, +} + +#[derive(Debug, thiserror::Error)] +pub enum ReachabilityTestError {} + +pub enum Event { + CompletedReachabilityTest(Result), +} + +pub(super) struct Behaviour { + pending_commands: VecDeque, + accepted_nonce: Arc>, +} + +impl NetworkBehaviour for Behaviour { + type ConnectionHandler = Handler; + + type ToSwarm = (); + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result { + Ok(Handler::new(self.accepted_nonce.clone(), peer)) + } + + fn handle_established_outbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result { + Ok(Handler::new(self.accepted_nonce.clone(), peer)) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + todo!() + } + + fn on_connection_handler_event( + &mut self, + _peer_id: PeerId, + _connection_id: ConnectionId, + event: ::ToBehaviour, + ) { + match event { + ToBehaviour::ResponseInfo(ResponseInfo { + response: + DialResponse { + status, + addr_idx, + dial_status, + }, + suspicious_addrs, + successfull_addr, + }) => { + todo!() + } + _ => todo!(), + } + todo!() + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll::FromBehaviour>> + { + todo!() + } +} diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs new file mode 100644 index 00000000000..6db25fe01de --- /dev/null +++ b/protocols/autonatv2/src/client/handler.rs @@ -0,0 +1,395 @@ +use std::{ + cmp::min, + collections::VecDeque, + convert::identity, + i8::MAX, + io, + iter::{once, repeat}, + sync::Arc, + task::Poll, + time::Duration, +}; + +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use libp2p_core::{upgrade::ReadyUpgrade, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use void::Void; + +use crate::{ + client::ToBehaviour, + generated::structs::mod_DialResponse::ResponseStatus, + request_response::{ + DialBack, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + }, + DIAL_BACK_PROTOCOL_NAME, REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, +}; +use crate::{DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND}; + +use super::ResponseInfo; + +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); +const MAX_CONCURRENT_REQUESTS: usize = 10; + +#[derive(Debug, thiserror::Error)] +pub(super) enum Error { + #[error("io error")] + Io(#[from] io::Error), + #[error("invalid data request index: {index} (max: {max})")] + InvalidDataRequestIndex { index: usize, max: usize }, + #[error("data request too large: {len} (max: {max})")] + DataRequestTooLarge { len: usize, max: usize }, + #[error("data request too small: {len} (min: {min})")] + DataRequestTooSmall { len: usize, min: usize }, + #[error("timeout")] + Timeout(#[from] futures_bounded::Timeout), + #[error("invalid nonce: {nonce} (provided by (by))")] + WrongNonceGiven { nonce: u64, by: PeerId }, + #[error("request protocol removed by peer")] + RequestProtocolRemoved, + #[error("dial back protocol removed by me")] + DialBackProtocolRemoved, +} + +pub(super) struct Handler { + /// Queue of events to return when polled. + queued_events: VecDeque< + ConnectionHandlerEvent< + ::OutboundProtocol, + ::OutboundOpenInfo, + ::ToBehaviour, + ::Error, + >, + >, + pending_data: VecDeque, + queued_requests: VecDeque, + inbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet>, + accepted_nonce: Arc>, + remote_peer_id: PeerId, +} + +impl Handler { + pub(super) fn new(accepted_nonce: Arc>, remote_peer_id: PeerId) -> Self { + Self { + queued_events: VecDeque::new(), + pending_data: VecDeque::new(), + queued_requests: VecDeque::new(), + inbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + outbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + accepted_nonce, + remote_peer_id, + } + } +} + +impl ConnectionHandler for Handler { + type Error = Error; + type ToBehaviour = super::ToBehaviour; + type FromBehaviour = super::FromBehaviour; + type InboundProtocol = ReadyUpgrade; + type OutboundProtocol = ReadyUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = Option; + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) + } + + fn poll( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> Poll< + ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::ToBehaviour, + Self::Error, + >, + > { + if let Some(event) = self.queued_events.pop_front() { + return Poll::Ready(event); + } + + if let Some(pending_data) = self.pending_data.pop_front() { + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(REQUEST_UPGRADE, Some(pending_data)), + }); + } + + if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { + let perform_request_res = m.map_err(Error::Timeout).and_then(identity); // necessary until flatten_error stabilized + match perform_request_res { + Ok(PerformRequestStatus::Pending(pending)) => { + self.pending_data.push_back(pending); + } + Ok(PerformRequestStatus::Finished(resp_info)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::ResponseInfo(resp_info), + )) + } + Err(e) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::OutboundError(e), + )); + } + } + } + todo!() + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + match event { + super::FromBehaviour::Dial(dial_req) => { + self.queued_requests.push_back(dial_req); + } + } + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: None, + }) => { + let request = self + .queued_requests + .pop_front() + .expect("opened a stream without a penidng request"); + let request_clone = request.clone(); + if self + .outbound + .try_push(perform_dial_request(protocol, request)) + .is_err() + { + println!( + "Dropping outbound stream because we are at capacity. {request_clone:?}" + ); + self.queued_requests.push_front(request_clone); + } + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: Some(pending_data), + }) => { + let pending_data_copy = pending_data.clone(); + if self + .outbound + .try_push(perform_data_request(protocol, pending_data)) + .is_err() + { + println!("Dropping outbound stream because we are at capacity."); + self.pending_data.push_front(pending_data_copy); + } + } + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + let remote_peer_id = self.remote_peer_id.clone(); + if self + .inbound + .try_push(perform_dial_back( + protocol, + self.accepted_nonce.clone(), + remote_peer_id, + )) + .is_err() + { + println!("Dropping inbound stream because we are at capacity."); + } + } + ConnectionEvent::DialUpgradeError(err) => { + todo!() + } + ConnectionEvent::ListenUpgradeError(err) => { + todo!() + } + ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { + if protocols.any(|e| &DIAL_BACK_PROTOCOL_NAME == e) { + self.queued_events.push_back(ConnectionHandlerEvent::Close( + Error::DialBackProtocolRemoved, + )); + } + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { + if protocols.any(|e| &REQUEST_PROTOCOL_NAME == e) { + self.queued_events + .push_back(ConnectionHandlerEvent::Close(Error::RequestProtocolRemoved)); + } + } + _ => {} + } + todo!() + } +} + +enum PerformRequestStatus { + Finished(ResponseInfo), + Pending(PendingData), +} + +#[derive(Debug, Clone)] +pub(super) struct PendingData { + data_count: usize, + suspicious_addrs: Vec, + all_addrs: Vec, +} + +async fn perform_dial_request( + mut stream: S, + dial_req: DialRequest, +) -> Result +where + S: AsyncRead + AsyncWrite + Unpin, +{ + let addrs = dial_req.addrs.clone(); + let req = Request::Dial(dial_req); + req.write_into(&mut stream).await?; + let resp = Response::read_from(&mut stream).await?; + stream.close().await?; + let DialDataRequest { + addr_idx, + num_bytes, + } = match resp { + Response::Dial(resp) => { + let successfull_addr = if resp.status == ResponseStatus::OK { + addrs.get(resp.addr_idx).cloned() + } else { + None + }; + return Ok(PerformRequestStatus::Finished(ResponseInfo::new( + resp, + Vec::new(), + successfull_addr, + ))); + } + Response::Data(data_req) => data_req, + }; + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(Error::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } else if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); + } + let suspicious_addr = addrs + .get(addr_idx) + .ok_or(Error::InvalidDataRequestIndex { + index: addr_idx, + max: addrs.len(), + })? + .clone(); + Ok(PerformRequestStatus::Pending(PendingData { + data_count: num_bytes, + suspicious_addrs: vec![suspicious_addr], + all_addrs: addrs, + })) +} + +async fn perform_data_request( + mut stream: S, + PendingData { + mut data_count, + mut suspicious_addrs, + all_addrs, + }: PendingData, +) -> Result +where + S: AsyncRead + AsyncWrite + Unpin, +{ + let data_field_len = min(DATA_FIELD_LEN_UPPER_BOUND, data_count); + data_count -= data_field_len; + let req = Request::Data(DialDataResponse { + data_count: data_field_len, + }); + req.write_into(&mut stream).await?; + if data_count != 0 { + return Ok(PerformRequestStatus::Pending(PendingData { + data_count, + suspicious_addrs, + all_addrs, + })); + } + let resp = Response::read_from(&mut stream).await?; + stream.close().await?; + match resp { + Response::Dial(dial_resp) => { + let successfull_addr = if dial_resp.status == ResponseStatus::OK { + all_addrs.get(dial_resp.addr_idx).cloned() + } else { + None + }; + Ok(PerformRequestStatus::Finished(ResponseInfo::new( + dial_resp, + suspicious_addrs, + successfull_addr, + ))) + } + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(Error::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } else if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); + } + let suspicious_addr = + all_addrs + .get(addr_idx) + .ok_or(Error::InvalidDataRequestIndex { + index: addr_idx, + max: all_addrs.len(), + })?; + if !suspicious_addrs.contains(suspicious_addr) { + suspicious_addrs.push(suspicious_addr.clone()); + } + Ok(PerformRequestStatus::Pending(PendingData { + data_count: num_bytes, + suspicious_addrs, + all_addrs, + })) + } + } +} + +async fn perform_dial_back( + mut stream: S, + accepted_nonce: Arc>, + remote_peer_id: PeerId, +) -> Result<(), Error> +where + S: AsyncRead + AsyncWrite + Unpin, +{ + let DialBack { nonce } = DialBack::read_from(&mut stream).await?; + if !accepted_nonce.contains_async(&nonce).await { + return Err(Error::WrongNonceGiven { + nonce, + by: remote_peer_id, + }); + } + stream.close().await?; + Ok(()) +} diff --git a/protocols/autonatv2/src/dial_back.rs b/protocols/autonatv2/src/dial_back.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index c0825159cf4..ad58455ca04 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -1,6 +1,22 @@ -pub(crate) mod request_response; -pub(crate) mod dial_back; +use libp2p_core::upgrade::ReadyUpgrade; +use libp2p_swarm::StreamProtocol; + +mod client; mod generated; +pub(crate) mod request_response; + +pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-request"); +pub(crate) const DIAL_BACK_PROTOCOL_NAME: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-back"); +pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = + ReadyUpgrade::new(REQUEST_PROTOCOL_NAME); +pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = + ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); + +pub(crate) use request_response::DATA_FIELD_LEN_UPPER_BOUND; +pub(crate) use request_response::DATA_LEN_LOWER_BOUND; +pub(crate) use request_response::DATA_LEN_UPPER_BOUND; pub fn add(left: usize, right: usize) -> usize { left + right diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 9d4a5d26a59..db003beed6f 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -5,7 +5,6 @@ use libp2p_core::{ upgrade::{read_length_prefixed, write_length_prefixed}, Multiaddr, }; -use libp2p_request_response::{Behaviour, Codec}; use libp2p_swarm::{ConnectionId, NetworkBehaviour, StreamProtocol}; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use rand::Rng; @@ -13,12 +12,9 @@ use rand::Rng; use crate::generated::structs as proto; const REQUEST_MAX_SIZE: usize = 4104; -const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; -const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; -const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; - -pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = - StreamProtocol::new("/libp2p/autonat/2/dial-request"); +pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; +pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; +pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; macro_rules! new_io_invalid_data_err { ($msg:expr) => { @@ -32,6 +28,30 @@ macro_rules! check_existence { }; } +macro_rules! read_from { + () => { + pub(crate) async fn read_from(mut reader: R) -> io::Result + where + R: AsyncRead + Unpin, + { + let bytes = read_length_prefixed(&mut reader, 1024).await?; + Self::from_bytes(&bytes) + } + }; +} + +macro_rules! write_into { + () => { + pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> + where + W: AsyncWrite + Unpin, + { + let bytes = self.into_bytes(); + write_length_prefixed(&mut writer, bytes).await + } + }; +} + #[derive(Debug, Clone)] pub(crate) enum Request { Dial(DialRequest), @@ -50,6 +70,8 @@ pub(crate) struct DialDataResponse { } impl Request { + read_from!(); + fn from_bytes(bytes: &[u8]) -> io::Result { let mut reader = BytesReader::from_bytes(bytes); let msg = proto::Message::from_reader(&mut reader, bytes) @@ -78,6 +100,8 @@ impl Request { } } + write_into!(); + fn into_bytes(self) -> Cow<'static, [u8]> { fn make_message_bytes(request: Request) -> Vec { let msg = match request { @@ -147,6 +171,8 @@ pub(crate) struct DialResponse { } impl Response { + read_from!(); + fn from_bytes(bytes: &[u8]) -> std::io::Result { let mut reader = BytesReader::from_bytes(bytes); let msg = proto::Message::from_reader(&mut reader, bytes) @@ -184,6 +210,8 @@ impl Response { } } + write_into!(); + fn into_bytes(self) -> Vec { let msg = match self { Self::Dial(DialResponse { @@ -229,66 +257,36 @@ impl DialDataRequest { } } -#[derive(Debug, Clone)] -pub(crate) struct AutoNATv2Codec { - observed_addr: Multiaddr, - connection_id: ConnectionId, -} +const DIAL_BACK_MAX_SIZE: usize = 10; -#[async_trait::async_trait] -impl Codec for AutoNATv2Codec { - type Protocol = String; - type Request = Request; - type Response = Response; - - async fn read_request( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let bytes = read_length_prefixed(io, REQUEST_MAX_SIZE).await?; - Request::from_bytes(&bytes) - } +pub(crate) struct DialBack { + pub nonce: u64, +} - async fn read_response( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Unpin + Send, - { - let bytes = read_length_prefixed(io, 1024).await?; - Response::from_bytes(&bytes) - } +impl DialBack { + read_from!(); - async fn write_request( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - req: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Unpin + Send, - { - write_length_prefixed(io, req.into_bytes()).await?; - io.close().await + fn from_bytes(bytes: &[u8]) -> io::Result { + let mut reader = BytesReader::from_bytes(bytes); + let proto::DialBack { nonce } = proto::DialBack::from_reader(&mut reader, bytes) + .map_err(|err| new_io_invalid_data_err!(err))?; + let nonce = check_existence!(nonce)?; + Ok(Self { nonce }) } - async fn write_response( - &mut self, - _protocol: &Self::Protocol, - io: &mut T, - res: Self::Response, - ) -> std::io::Result<()> + pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> where - T: AsyncWrite + Unpin + Send, + W: AsyncWrite + Unpin, { - write_length_prefixed(io, res.into_bytes()).await?; - io.close().await + let msg = proto::DialBack { + nonce: Some(self.nonce), + }; + let mut buf = [0u8; DIAL_BACK_MAX_SIZE]; + debug_assert!(msg.get_size() <= DIAL_BACK_MAX_SIZE); + let mut msg_writer = Writer::new(&mut buf[..]); + msg.write_message(&mut msg_writer) + .expect("encoding to succeed"); + write_length_prefixed(&mut writer, &buf[..msg.get_size()]).await } } @@ -306,4 +304,21 @@ mod tests { .unwrap(); assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); } + + #[test] + fn dial_back_correct_size() { + let dial_back = super::proto::DialBack { nonce: Some(0) }; + let buf = quick_protobuf::serialize_into_vec(&dial_back).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + + let dial_back_none = super::proto::DialBack { nonce: None }; + let buf = quick_protobuf::serialize_into_vec(&dial_back_none).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + + let dial_back_max_nonce = super::proto::DialBack { + nonce: Some(u64::MAX), + }; + let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + } } From 81cd4429782c6ef408da0421c535966df3366a1a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:23:10 +0100 Subject: [PATCH 033/179] Extract ip-global and implemented lot of behaviour --- Cargo.lock | 6 + Cargo.toml | 2 + core/Cargo.toml | 1 + core/src/transport/global_only.rs | 237 +----------------- core/src/upgrade/ready.rs | 2 +- misc/ip-global/Cargo.toml | 12 + misc/ip-global/src/lib.rs | 255 ++++++++++++++++++++ protocols/autonatv2/Cargo.toml | 3 +- protocols/autonatv2/src/client/behaviour.rs | 168 +++++++++++-- 9 files changed, 428 insertions(+), 258 deletions(-) create mode 100644 misc/ip-global/Cargo.toml create mode 100644 misc/ip-global/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f649e6d236a..90a40a8ac61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,6 +2236,10 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ip-global" +version = "0.1.0" + [[package]] name = "ipconfig" version = "0.3.2" @@ -2451,6 +2455,7 @@ dependencies = [ "either", "futures", "futures-bounded", + "ip-global", "libp2p-core", "libp2p-identity", "libp2p-swarm", @@ -2489,6 +2494,7 @@ dependencies = [ "futures", "futures-timer", "instant", + "ip-global", "libp2p-identity", "libp2p-mplex", "libp2p-noise", diff --git a/Cargo.toml b/Cargo.toml index 1eb6ffc392a..0fddc646e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "misc/allow-block-list", "misc/connection-limits", "misc/futures-bounded", + "misc/ip-global", "misc/keygen", "misc/memory-connection-limits", "misc/metrics", @@ -116,6 +117,7 @@ prometheus-client = "0.22.0" quick-protobuf-codec = { version = "0.2.0", path = "misc/quick-protobuf-codec" } quickcheck = { package = "quickcheck-ext", path = "misc/quickcheck-ext" } rw-stream-sink = { version = "0.4.0", path = "misc/rw-stream-sink" } +ip-global = { version = "0.1.0", path = "misc/ip-global" } [patch.crates-io] diff --git a/core/Cargo.toml b/core/Cargo.toml index 4cbfa827af6..0a0a42ecc2e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,6 +16,7 @@ fnv = "1.0" futures = { version = "0.3.29", features = ["executor", "thread-pool"] } futures-timer = "3" instant = "0.1.12" +ip-global = { workspace = true } libp2p-identity = { workspace = true, features = ["peerid", "ed25519"] } log = "0.4" multiaddr = { workspace = true } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 40e99c6a31b..1b4822f2cea 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -20,8 +20,9 @@ use crate::{ multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent, DialOpts}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; +use ip_global::IpExt; use log::debug; use std::{ pin::Pin, @@ -34,236 +35,6 @@ pub struct Transport { inner: T, } -/// This module contains an implementation of the `is_global` IPv4 address space. -/// -/// Credit for this implementation goes to the Rust standard library team. -/// -/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) -mod ipv4_global { - use std::net::Ipv4Addr; - - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - #[inline] - const fn is_reserved(a: Ipv4Addr) -> bool { - a.octets()[0] & 240 == 240 && !a.is_broadcast() - } - - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - #[inline] - const fn is_benchmarking(a: Ipv4Addr) -> bool { - a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - #[inline] - const fn is_shared(a: Ipv4Addr) -> bool { - a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - #[inline] - const fn is_private(a: Ipv4Addr) -> bool { - match a.octets() { - [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, - [192, 168, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[must_use] - #[inline] - pub(crate) const fn is_global(a: Ipv4Addr) -> bool { - !(a.octets()[0] == 0 // "This network" - || is_private(a) - || is_shared(a) - || a.is_loopback() - || a.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) - || a.is_documentation() - || is_benchmarking(a) - || is_reserved(a) - || a.is_broadcast()) - } -} - -/// This module contains an implementation of the `is_global` IPv6 address space. -/// -/// Credit for this implementation goes to the Rust standard library team. -/// -/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) -mod ipv6_global { - use std::net::Ipv6Addr; - - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - #[inline] - const fn is_unicast_link_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xffc0) == 0xfe80 - } - - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - #[inline] - const fn is_unique_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xfe00) == 0xfc00 - } - - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - #[inline] - const fn is_documentation(a: Ipv6Addr) -> bool { - (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - #[inline] - pub(crate) const fn is_global(a: Ipv6Addr) -> bool { - !(a.is_unspecified() - || a.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || is_documentation(a) - || is_unique_local(a) - || is_unicast_link_local(a)) - } -} - impl Transport { pub fn new(transport: T) -> Self { Transport { inner: transport } @@ -295,14 +66,14 @@ impl crate::Transport for Transport { ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if !ipv4_global::is_global(a) { + if !IpExt::is_global(&a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr, opts) } Some(Protocol::Ip6(a)) => { - if !ipv6_global::is_global(a) { + if !IpExt::is_global(&a) { debug!("Not dialing non global IP address {:?}.", a); return Err(TransportError::MultiaddrNotSupported(addr)); } diff --git a/core/src/upgrade/ready.rs b/core/src/upgrade/ready.rs index 323f1f73f32..7e235902651 100644 --- a/core/src/upgrade/ready.rs +++ b/core/src/upgrade/ready.rs @@ -31,7 +31,7 @@ pub struct ReadyUpgrade

{ } impl

ReadyUpgrade

{ - pub fn new(protocol_name: P) -> Self { + pub const fn new(protocol_name: P) -> Self { Self { protocol_name } } } diff --git a/misc/ip-global/Cargo.toml b/misc/ip-global/Cargo.toml new file mode 100644 index 00000000000..7dfb5c3dd6d --- /dev/null +++ b/misc/ip-global/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ip-global" +version = "0.1.0" +edition = "2021" +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lints] +workspace = true diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs new file mode 100644 index 00000000000..aaf7a6b2a89 --- /dev/null +++ b/misc/ip-global/src/lib.rs @@ -0,0 +1,255 @@ +//! This crate provides extension traits for [`Ipv4Addr`], [`Ipv6Addr`] and [`IpAddr`] that provide methods +//! to check if an address is reserved for special use. +//! +//! This is primarily a polyfill for `ip` feature, which is currently unstable. +//! +//! Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) + + +use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; + +pub trait Ipv4Ext { + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + fn is_reserved(&self) -> bool; + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + fn is_benchmarking(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + fn is_shared(&self) -> bool; + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + fn is_private(&self) -> bool; +} + +impl Ipv4Ext for Ipv4Addr { + #[inline] + fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + #[inline] + fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + #[inline] + fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + #[inline] + fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, + } + } +} + +pub trait Ipv6Ext { + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + fn is_unicast_link_local(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + fn is_unique_local(&self) -> bool; + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + fn is_documentation(&self) -> bool; +} + +impl Ipv6Ext for Ipv6Addr { + #[inline] + fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + #[inline] + fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + #[inline] + fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } +} + +pub trait IpExt { + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. + #[must_use] + fn is_global(&self) -> bool; +} + +impl IpExt for Ipv4Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[inline] + fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || Ipv4Ext::is_shared(self) + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || Ipv4Ext::is_benchmarking(self) + || Ipv4Ext::is_reserved(self) + || self.is_broadcast()) + } +} + +impl IpExt for Ipv6Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[inline] + fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || Ipv6Ext::is_documentation(self) + || Ipv6Ext::is_unique_local(self) + || Ipv6Ext::is_unicast_link_local(self)) + } +} + +impl IpExt for IpAddr { + #[inline] + fn is_global(&self) -> bool { + match self { + Self::V4(v4) => IpExt::is_global(v4), + Self::V6(v6) => IpExt::is_global(v6), + } + } +} diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 7792cf97239..7e3ce4bc73e 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -7,14 +7,15 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = "0.1" quick-protobuf = "0.8" libp2p-core = { workspace = true } -async-trait = "0.1" rand_core = "0.6" rand = { version = "0.8", optional = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } futures-bounded = { workspace = true } +ip-global = { workspace = true } void = "1.0.2" either = "1.9.0" futures = "0.3.29" diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 5351453b141..63eb3888cc2 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -4,19 +4,22 @@ use std::{ task::{Context, Poll}, }; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use ip_global::IpExt; +use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, ToSwarm, + dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, + NetworkBehaviour, NotifyHandler, ToSwarm, }; +use rand_core::RngCore; use crate::{ client::{ResponseInfo, ToBehaviour}, - generated::structs::DialStatus, - request_response::DialResponse, + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + request_response::{DialRequest, DialResponse}, }; -use super::handler::Handler; +use super::{handler::Handler, FromBehaviour}; enum Command { TestListenerReachability { @@ -27,25 +30,58 @@ enum Command { pub struct ReachabilityTestSucc { pub server_peer: PeerId, - pub local_addr: Multiaddr, + pub visible_addr: Option, + pub suspicious_addrs: Vec, } #[derive(Debug, thiserror::Error)] -pub enum ReachabilityTestError {} +pub enum ReachabilityTestErr { + #[error("Server chose not to dial any provided address. Server peer id: {peer_id}")] + ServerChoseNotToDialAnyAddress { peer_id: PeerId }, + #[error("Server rejected dial request. Server peer id: {peer_id}")] + ServerRejectedDialRequest { peer_id: PeerId }, + #[error("Server ran into an internal error. Server peer id: {peer_id}")] + InternalServerError { peer_id: PeerId }, + #[error("Server did not respond correctly to dial request. Server peer id: {peer_id}")] + InvalidResponse { peer_id: PeerId }, + #[error("Server was unable to connect to address: {addr:?}. Server peer id: {peer_id}")] + UnableToConnectOnSelectedAddress { + peer_id: PeerId, + addr: Option, + }, + #[error("Server experienced failure during dial back on address: {addr:?} Server peer id: {peer_id}")] + FailureDuringDialBack { + peer_id: PeerId, + addr: Option, + }, +} pub enum Event { - CompletedReachabilityTest(Result), + CompletedReachabilityTest(Result), } -pub(super) struct Behaviour { +pub(super) struct Behaviour +where + R: RngCore + 'static, +{ pending_commands: VecDeque, + pending_events: VecDeque< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + >, accepted_nonce: Arc>, + rng: R, } -impl NetworkBehaviour for Behaviour { +impl NetworkBehaviour for Behaviour +where + R: RngCore + 'static, +{ type ConnectionHandler = Handler; - type ToSwarm = (); + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -74,22 +110,16 @@ impl NetworkBehaviour for Behaviour { fn on_connection_handler_event( &mut self, - _peer_id: PeerId, + peer_id: PeerId, _connection_id: ConnectionId, event: ::ToBehaviour, ) { match event { - ToBehaviour::ResponseInfo(ResponseInfo { - response: - DialResponse { - status, - addr_idx, - dial_status, - }, - suspicious_addrs, - successfull_addr, - }) => { - todo!() + ToBehaviour::ResponseInfo(response_info) => { + let event = Event::CompletedReachabilityTest( + self.handle_response_info(peer_id, response_info), + ); + self.pending_events.push_back(ToSwarm::GenerateEvent(event)); } _ => todo!(), } @@ -101,6 +131,98 @@ impl NetworkBehaviour for Behaviour { cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + if let Some(command) = self.pending_commands.pop_front() { + self.handle_command(command); + return Poll::Pending; + } todo!() } } + +impl Behaviour +where + R: RngCore, +{ + fn handle_response_info( + &mut self, + peer_id: PeerId, + ResponseInfo { + response: + DialResponse { + status, + dial_status, + .. + }, + suspicious_addrs, + successfull_addr, + }: ResponseInfo, + ) -> Result { + match (status, dial_status) { + (ResponseStatus::E_REQUEST_REJECTED, _) => { + Err(ReachabilityTestErr::ServerRejectedDialRequest { peer_id }) + } + (ResponseStatus::E_DIAL_REFUSED, _) => { + Err(ReachabilityTestErr::ServerChoseNotToDialAnyAddress { peer_id }) + } + (ResponseStatus::E_INTERNAL_ERROR, _) => { + Err(ReachabilityTestErr::InternalServerError { peer_id }) + } + (ResponseStatus::OK, DialStatus::UNUSED) => { + Err(ReachabilityTestErr::InvalidResponse { peer_id }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { + Err(ReachabilityTestErr::UnableToConnectOnSelectedAddress { + peer_id, + addr: successfull_addr, + }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { + Err(ReachabilityTestErr::FailureDuringDialBack { + peer_id, + addr: successfull_addr, + }) + } + (ResponseStatus::OK, DialStatus::OK) => Ok(ReachabilityTestSucc { + server_peer: peer_id, + visible_addr: successfull_addr, + suspicious_addrs, + }), + } + } + + fn handle_command(&mut self, command: Command) { + match command { + Command::TestListenerReachability { + server_peer, + local_addrs, + } => { + let cleaned_local_addrs = local_addrs.iter().filter(|addr| { + !addr.iter().any(|p| match p { + Protocol::Ip4(ip) if !IpExt::is_global(&ip) => true, + Protocol::Ip6(ip) if !IpExt::is_global(&ip) => true, + Protocol::Dns(m) + | Protocol::Dns4(m) + | Protocol::Dns6(m) + | Protocol::Dnsaddr(m) => m == "localhost" || m.ends_with(".local"), + _ => false, + }) + }); + let dial_opts = DialOpts::peer_id(server_peer.clone()).build(); + let dial_event = ToSwarm::Dial { opts: dial_opts }; + self.pending_events.push_back(dial_event); + let dial_request = DialRequest { + addrs: local_addrs, + nonce: self.rng.next_u64(), + }; + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: server_peer, + handler: NotifyHandler::Any, + event: FromBehaviour::Dial(dial_request), + }) + } + } + } +} From c0fd889d4f74363ad576fa1537ee4ea48f19be0b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 18 Nov 2023 17:21:30 +0100 Subject: [PATCH 034/179] Intermediate commit --- Cargo.lock | 4 + protocols/autonatv2/Cargo.toml | 4 + protocols/autonatv2/src/client/behaviour.rs | 30 +-- protocols/autonatv2/src/client/handler.rs | 22 ++- .../autonatv2/src/client/handler/dial_back.rs | 12 ++ .../autonatv2/src/generated/structs.proto | 63 +++---- protocols/autonatv2/src/generated/structs.rs | 38 ++-- protocols/autonatv2/src/request_response.rs | 172 +++++++----------- 8 files changed, 158 insertions(+), 187 deletions(-) create mode 100644 protocols/autonatv2/src/client/handler/dial_back.rs diff --git a/Cargo.lock b/Cargo.lock index 90a40a8ac61..d1f76d9b256 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2452,6 +2452,8 @@ name = "libp2p-autonatv2" version = "0.1.0" dependencies = [ "async-trait", + "asynchronous-codec 0.6.2", + "bytes", "either", "futures", "futures-bounded", @@ -2460,9 +2462,11 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "quick-protobuf", + "quick-protobuf-codec", "rand 0.8.5", "rand_core 0.6.4", "scc", + "static_assertions", "thiserror", "void", ] diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 7e3ce4bc73e..f35a65bf77f 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -9,6 +9,8 @@ rust-version.workspace = true [dependencies] async-trait = "0.1" quick-protobuf = "0.8" +quick-protobuf-codec = { workspace = true } +asynchronous-codec = "0.6" libp2p-core = { workspace = true } rand_core = "0.6" rand = { version = "0.8", optional = true } @@ -21,6 +23,8 @@ either = "1.9.0" futures = "0.3.29" thiserror = "1.0.50" scc = "2.0.3" +bytes = "1" +static_assertions = "1.1.0" [lints] workspace = true diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 63eb3888cc2..72834301774 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashSet, VecDeque}, + collections::{VecDeque}, sync::Arc, task::{Context, Poll}, }; @@ -28,14 +28,14 @@ enum Command { }, } -pub struct ReachabilityTestSucc { - pub server_peer: PeerId, - pub visible_addr: Option, - pub suspicious_addrs: Vec, +pub(crate) struct ReachabilityTestSucc { + pub(crate) server_peer: PeerId, + pub(crate) visible_addr: Option, + pub(crate) suspicious_addrs: Vec, } #[derive(Debug, thiserror::Error)] -pub enum ReachabilityTestErr { +pub(crate) enum ReachabilityTestErr { #[error("Server chose not to dial any provided address. Server peer id: {peer_id}")] ServerChoseNotToDialAnyAddress { peer_id: PeerId }, #[error("Server rejected dial request. Server peer id: {peer_id}")] @@ -56,7 +56,7 @@ pub enum ReachabilityTestErr { }, } -pub enum Event { +pub(crate) enum Event { CompletedReachabilityTest(Result), } @@ -87,8 +87,8 @@ where &mut self, _connection_id: ConnectionId, peer: PeerId, - local_addr: &Multiaddr, - remote_addr: &Multiaddr, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, ) -> Result { Ok(Handler::new(self.accepted_nonce.clone(), peer)) } @@ -97,14 +97,14 @@ where &mut self, _connection_id: ConnectionId, peer: PeerId, - addr: &Multiaddr, - role_override: Endpoint, - port_use: PortUse, + _addr: &Multiaddr, + _role_override: Endpoint, + _port_use: PortUse, ) -> Result { Ok(Handler::new(self.accepted_nonce.clone(), peer)) } - fn on_swarm_event(&mut self, event: FromSwarm) { + fn on_swarm_event(&mut self, _event: FromSwarm) { todo!() } @@ -128,7 +128,7 @@ where fn poll( &mut self, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { if let Some(event) = self.pending_events.pop_front() { @@ -199,7 +199,7 @@ where server_peer, local_addrs, } => { - let cleaned_local_addrs = local_addrs.iter().filter(|addr| { + let _cleaned_local_addrs = local_addrs.iter().filter(|addr| { !addr.iter().any(|p| match p { Protocol::Ip4(ip) if !IpExt::is_global(&ip) => true, Protocol::Ip6(ip) if !IpExt::is_global(&ip) => true, diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 6db25fe01de..204a1ef6dee 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,10 +1,18 @@ +// two handlers, share state in behaviour +// do isolated stuff in async function +// +// write basic tests +// Take a look at rendezvous +// TODO: tests +// TODO: Handlers + +mod dial_back; + use std::{ cmp::min, collections::VecDeque, convert::identity, - i8::MAX, io, - iter::{once, repeat}, sync::Arc, task::Poll, time::Duration, @@ -17,13 +25,13 @@ use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use void::Void; + use crate::{ client::ToBehaviour, generated::structs::mod_DialResponse::ResponseStatus, request_response::{ - DialBack, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + DialBack, DialDataRequest, DialDataResponse, DialRequest, Request, Response, }, DIAL_BACK_PROTOCOL_NAME, REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, }; @@ -93,7 +101,7 @@ impl ConnectionHandler for Handler { type InboundProtocol = ReadyUpgrade; type OutboundProtocol = ReadyUpgrade; type InboundOpenInfo = (); - type OutboundOpenInfo = Option; + type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) @@ -209,10 +217,10 @@ impl ConnectionHandler for Handler { println!("Dropping inbound stream because we are at capacity."); } } - ConnectionEvent::DialUpgradeError(err) => { + ConnectionEvent::DialUpgradeError(_err) => { todo!() } - ConnectionEvent::ListenUpgradeError(err) => { + ConnectionEvent::ListenUpgradeError(_err) => { todo!() } ConnectionEvent::AddressChange(_) => {} diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs new file mode 100644 index 00000000000..edd490c2f8e --- /dev/null +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -0,0 +1,12 @@ +use libp2p_swarm::ConnectionHandler; + +#[derive(Debug)] +pub enum ToBehaviour { + GotNonce(u64), +} + +#[derive(Debug, Default)] +pub struct Handler; + +impl ConnectionHandler for Handler { +} diff --git a/protocols/autonatv2/src/generated/structs.proto b/protocols/autonatv2/src/generated/structs.proto index 46f5f38f1b6..7088e888052 100644 --- a/protocols/autonatv2/src/generated/structs.proto +++ b/protocols/autonatv2/src/generated/structs.proto @@ -2,56 +2,45 @@ syntax = "proto2"; package structs; - message Message { - oneof msg { - DialRequest dialRequest = 1; - DialResponse dialResponse = 2; - DialDataRequest dialDataRequest = 3; - DialDataResponse dialDataResponse = 4; - } + oneof msg { + DialRequest dialRequest = 1; + DialResponse dialResponse = 2; + DialDataRequest dialDataRequest = 3; + DialDataResponse dialDataResponse = 4; + } } - message DialRequest { - repeated bytes addrs = 1; - fixed64 nonce = 2; + repeated bytes addrs = 1; + fixed64 nonce = 2; } - message DialDataRequest { - uint32 addrIdx = 1; - uint64 numBytes = 2; + uint32 addrIdx = 1; + uint64 numBytes = 2; } - enum DialStatus { - UNUSED = 0; - E_DIAL_ERROR = 100; - E_DIAL_BACK_ERROR = 101; - OK = 200; + UNUSED = 0; + E_DIAL_ERROR = 100; + E_DIAL_BACK_ERROR = 101; + OK = 200; } - message DialResponse { - enum ResponseStatus { - E_INTERNAL_ERROR = 0; - E_REQUEST_REJECTED = 100; - E_DIAL_REFUSED = 101; - OK = 200; - } - - ResponseStatus status = 1; - uint32 addrIdx = 2; - DialStatus dialStatus = 3; + enum ResponseStatus { + E_INTERNAL_ERROR = 0; + E_REQUEST_REJECTED = 100; + E_DIAL_REFUSED = 101; + OK = 200; + } + + ResponseStatus status = 1; + uint32 addrIdx = 2; + DialStatus dialStatus = 3; } +message DialDataResponse { bytes data = 1; } -message DialDataResponse { - bytes data = 1; -} - - -message DialBack { - fixed64 nonce = 1; -} +message DialBack { fixed64 nonce = 1; } diff --git a/protocols/autonatv2/src/generated/structs.rs b/protocols/autonatv2/src/generated/structs.rs index 3fd8956178c..2bb613458e8 100644 --- a/protocols/autonatv2/src/generated/structs.rs +++ b/protocols/autonatv2/src/generated/structs.rs @@ -10,6 +10,7 @@ use std::borrow::Cow; + use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::*; @@ -54,11 +55,11 @@ impl<'a> From<&'a str> for DialStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct Message<'a> { - pub(crate) msg: structs::mod_Message::OneOfmsg<'a>, +pub(crate) struct Message { + pub(crate) msg: structs::mod_Message::OneOfmsg, } -impl<'a> MessageRead<'a> for Message<'a> { +impl<'a> MessageRead<'a> for Message { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { @@ -75,7 +76,7 @@ impl<'a> MessageRead<'a> for Message<'a> { } } -impl<'a> MessageWrite for Message<'a> { +impl MessageWrite for Message { fn get_size(&self) -> usize { 0 + match self.msg { @@ -101,15 +102,15 @@ pub(crate) mod mod_Message { use super::*; #[derive(Debug, PartialEq, Clone)] -pub(crate) enum OneOfmsg<'a> { - dialRequest(structs::DialRequest<'a>), +pub(crate) enum OneOfmsg { + dialRequest(structs::DialRequest), dialResponse(structs::DialResponse), dialDataRequest(structs::DialDataRequest), - dialDataResponse(structs::DialDataResponse<'a>), + dialDataResponse(structs::DialDataResponse), None, } -impl<'a> Default for OneOfmsg<'a> { +impl Default for OneOfmsg { fn default() -> Self { OneOfmsg::None } @@ -119,17 +120,17 @@ impl<'a> Default for OneOfmsg<'a> { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialRequest<'a> { - pub(crate) addrs: Vec>, +pub(crate) struct DialRequest { + pub(crate) addrs: Vec>, pub(crate) nonce: Option, } -impl<'a> MessageRead<'a> for DialRequest<'a> { +impl<'a> MessageRead<'a> for DialRequest { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.addrs.push(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(10) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), @@ -139,7 +140,7 @@ impl<'a> MessageRead<'a> for DialRequest<'a> { } } -impl<'a> MessageWrite for DialRequest<'a> { +impl MessageWrite for DialRequest { fn get_size(&self) -> usize { 0 + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() @@ -274,16 +275,16 @@ impl<'a> From<&'a str> for ResponseStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialDataResponse<'a> { - pub(crate) data: Option>, +pub(crate) struct DialDataResponse { + pub(crate) data: Option>, } -impl<'a> MessageRead<'a> for DialDataResponse<'a> { +impl<'a> MessageRead<'a> for DialDataResponse { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes).map(Cow::Borrowed)?), + Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned().into()), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -292,7 +293,7 @@ impl<'a> MessageRead<'a> for DialDataResponse<'a> { } } -impl<'a> MessageWrite for DialDataResponse<'a> { +impl MessageWrite for DialDataResponse { fn get_size(&self) -> usize { 0 + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) @@ -335,4 +336,3 @@ impl MessageWrite for DialBack { Ok(()) } } - diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index db003beed6f..0547afe603e 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -1,12 +1,17 @@ -use std::{borrow::Cow, io, sync::OnceLock}; +// change to quick-protobuf-codec -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use std::{borrow::Cow, io}; + +use asynchronous_codec::{FramedRead, FramedWrite}; + +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::{ upgrade::{read_length_prefixed, write_length_prefixed}, Multiaddr, }; -use libp2p_swarm::{ConnectionId, NetworkBehaviour, StreamProtocol}; + use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; +use quick_protobuf_codec::Codec; use rand::Rng; use crate::generated::structs as proto; @@ -28,30 +33,6 @@ macro_rules! check_existence { }; } -macro_rules! read_from { - () => { - pub(crate) async fn read_from(mut reader: R) -> io::Result - where - R: AsyncRead + Unpin, - { - let bytes = read_length_prefixed(&mut reader, 1024).await?; - Self::from_bytes(&bytes) - } - }; -} - -macro_rules! write_into { - () => { - pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> - where - W: AsyncWrite + Unpin, - { - let bytes = self.into_bytes(); - write_length_prefixed(&mut writer, bytes).await - } - }; -} - #[derive(Debug, Clone)] pub(crate) enum Request { Dial(DialRequest), @@ -70,12 +51,12 @@ pub(crate) struct DialDataResponse { } impl Request { - read_from!(); - - fn from_bytes(bytes: &[u8]) -> io::Result { - let mut reader = BytesReader::from_bytes(bytes); - let msg = proto::Message::from_reader(&mut reader, bytes) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { + let mut framed_io = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)); + let msg = framed_io + .next() + .await + .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; match msg.msg { proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { let addrs: Vec = addrs @@ -100,54 +81,35 @@ impl Request { } } - write_into!(); - - fn into_bytes(self) -> Cow<'static, [u8]> { - fn make_message_bytes(request: Request) -> Vec { - let msg = match request { - Request::Dial(DialRequest { addrs, nonce }) => { - let addrs = addrs.iter().map(|e| e.to_vec().into()).collect(); - let nonce = Some(nonce); - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { - addrs, - nonce, - }), - } + pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { + let msg = match self { + Request::Dial(DialRequest { addrs, nonce }) => { + let addrs = addrs.iter().map(|e| e.to_vec()).collect(); + let nonce = Some(nonce); + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { + addrs, + nonce, + }), } - Request::Data(DialDataResponse { data_count }) => { - assert!( - data_count <= DATA_FIELD_LEN_UPPER_BOUND, - "data_count too large" - ); - static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataResponse( - proto::DialDataResponse { - data: Some(Cow::Borrowed(&DATA[..data_count])), - }, - ), - } + } + Request::Data(DialDataResponse { data_count }) => { + debug_assert!( + data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { + data: Some(Cow::Borrowed(&DATA[..data_count])), + }), } - }; - let mut buf = Vec::with_capacity(msg.get_size()); - let mut writer = Writer::new(&mut buf); - msg.write_message(&mut writer).expect("encoding to succeed"); - buf - } - // little optimization: if the data is exactly 4096 bytes, we can use a static buffer. It is - // likely that this is the case, draining the most performance. - if matches!( - self, - Self::Data(DialDataResponse { - data_count: DATA_FIELD_LEN_UPPER_BOUND - }) - ) { - static CELL: OnceLock> = OnceLock::new(); - CELL.get_or_init(move || make_message_bytes(self)).into() - } else { - make_message_bytes(self).into() - } + } + }; + FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } } @@ -171,12 +133,11 @@ pub(crate) struct DialResponse { } impl Response { - read_from!(); - - fn from_bytes(bytes: &[u8]) -> std::io::Result { - let mut reader = BytesReader::from_bytes(bytes); - let msg = proto::Message::from_reader(&mut reader, bytes) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> std::io::Result { + let msg = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)) + .next() + .await + .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; match msg.msg { proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { @@ -210,9 +171,7 @@ impl Response { } } - write_into!(); - - fn into_bytes(self) -> Vec { + pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = match self { Self::Dial(DialResponse { status, @@ -235,10 +194,10 @@ impl Response { }), }, }; - let mut buf = Vec::with_capacity(msg.get_size()); - let mut writer = Writer::new(&mut buf); - msg.write_message(&mut writer).expect("encoding to succeed"); - buf + FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } } @@ -260,33 +219,28 @@ impl DialDataRequest { const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { - pub nonce: u64, + pub(crate) nonce: u64, } impl DialBack { - read_from!(); - - fn from_bytes(bytes: &[u8]) -> io::Result { - let mut reader = BytesReader::from_bytes(bytes); - let proto::DialBack { nonce } = proto::DialBack::from_reader(&mut reader, bytes) - .map_err(|err| new_io_invalid_data_err!(err))?; + pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { + let proto::DialBack { nonce } = + FramedRead::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) + .next() + .await + .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; let nonce = check_existence!(nonce)?; Ok(Self { nonce }) } - pub(crate) async fn write_into(self, mut writer: W) -> io::Result<()> - where - W: AsyncWrite + Unpin, - { + pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = proto::DialBack { nonce: Some(self.nonce), }; - let mut buf = [0u8; DIAL_BACK_MAX_SIZE]; - debug_assert!(msg.get_size() <= DIAL_BACK_MAX_SIZE); - let mut msg_writer = Writer::new(&mut buf[..]); - msg.write_message(&mut msg_writer) - .expect("encoding to succeed"); - write_length_prefixed(&mut writer, &buf[..msg.get_size()]).await + FramedWrite::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } } From fadf904c81faeb5c749367bc28007a17fa61ac4c Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:15:44 +0100 Subject: [PATCH 035/179] WIP client --- Cargo.lock | 1 + misc/ip-global/src/lib.rs | 2 +- protocols/autonatv2/Cargo.toml | 1 + protocols/autonatv2/src/client.rs | 35 -- protocols/autonatv2/src/client/behaviour.rs | 368 +++++++++------- protocols/autonatv2/src/client/handler.rs | 396 +----------------- .../autonatv2/src/client/handler/dial_back.rs | 92 +++- .../autonatv2/src/client/handler/request.rs | 302 +++++++++++++ protocols/autonatv2/src/global_only.rs | 247 +++++++++++ protocols/autonatv2/src/lib.rs | 1 + protocols/autonatv2/src/request_response.rs | 5 +- 11 files changed, 867 insertions(+), 583 deletions(-) create mode 100644 protocols/autonatv2/src/client/handler/request.rs create mode 100644 protocols/autonatv2/src/global_only.rs diff --git a/Cargo.lock b/Cargo.lock index ef42db7cd05..09b2320c3b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2536,6 +2536,7 @@ dependencies = [ "scc", "static_assertions", "thiserror", + "tracing", "void", ] diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs index aaf7a6b2a89..e3f1fb4f2ae 100644 --- a/misc/ip-global/src/lib.rs +++ b/misc/ip-global/src/lib.rs @@ -68,7 +68,7 @@ impl Ipv4Ext for Ipv4Addr { fn is_private(&self) -> bool { match self.octets() { [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, + [172, b, ..] if (16..=31).contains(&b) => true, [192, 168, ..] => true, _ => false, } diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index f35a65bf77f..76b2cc7ed87 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -25,6 +25,7 @@ thiserror = "1.0.50" scc = "2.0.3" bytes = "1" static_assertions = "1.1.0" +tracing = "0.1.40" [lints] workspace = true diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index 350afcd31cf..016ba931f10 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,38 +1,3 @@ -use libp2p_core::Multiaddr; - -use crate::request_response::{DialRequest, DialResponse}; - mod behaviour; mod handler; -#[derive(Debug)] -enum ToBehaviour { - ResponseInfo(ResponseInfo), - OutboundError(E), -} - -#[derive(Debug)] -struct ResponseInfo { - response: DialResponse, - suspicious_addrs: Vec, - successfull_addr: Option, -} - -impl ResponseInfo { - fn new( - response: DialResponse, - suspicious_addrs: Vec, - successfull_addr: Option, - ) -> Self { - Self { - response, - suspicious_addrs, - successfull_addr, - } - } -} - -#[derive(Debug)] -enum FromBehaviour { - Dial(DialRequest), -} diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 72834301774..7eb41a2b229 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,78 +1,48 @@ use std::{ - collections::{VecDeque}, - sync::Arc, + collections::{BTreeMap, HashMap, HashSet, VecDeque}, task::{Context, Poll}, }; -use ip_global::IpExt; -use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; +use either::Either; +use libp2p_core::{multiaddr::Protocol, transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, - NetworkBehaviour, NotifyHandler, ToSwarm, + behaviour::ConnectionEstablished, + dial_opts::{DialOpts, PeerCondition}, + ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, + NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; +use rand::{seq::SliceRandom, Rng}; use rand_core::RngCore; -use crate::{ - client::{ResponseInfo, ToBehaviour}, - generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{DialRequest, DialResponse}, -}; - -use super::{handler::Handler, FromBehaviour}; - -enum Command { - TestListenerReachability { - server_peer: PeerId, - local_addrs: Vec, - }, -} - -pub(crate) struct ReachabilityTestSucc { - pub(crate) server_peer: PeerId, - pub(crate) visible_addr: Option, - pub(crate) suspicious_addrs: Vec, -} +use crate::{global_only::IpExt, request_response::DialRequest}; -#[derive(Debug, thiserror::Error)] -pub(crate) enum ReachabilityTestErr { - #[error("Server chose not to dial any provided address. Server peer id: {peer_id}")] - ServerChoseNotToDialAnyAddress { peer_id: PeerId }, - #[error("Server rejected dial request. Server peer id: {peer_id}")] - ServerRejectedDialRequest { peer_id: PeerId }, - #[error("Server ran into an internal error. Server peer id: {peer_id}")] - InternalServerError { peer_id: PeerId }, - #[error("Server did not respond correctly to dial request. Server peer id: {peer_id}")] - InvalidResponse { peer_id: PeerId }, - #[error("Server was unable to connect to address: {addr:?}. Server peer id: {peer_id}")] - UnableToConnectOnSelectedAddress { - peer_id: PeerId, - addr: Option, - }, - #[error("Server experienced failure during dial back on address: {addr:?} Server peer id: {peer_id}")] - FailureDuringDialBack { - peer_id: PeerId, - addr: Option, - }, -} +use super::handler::{ + new_handler, Handler, RequestError, RequestFromBehaviour, RequestToBehaviour, TestEnd, +}; -pub(crate) enum Event { - CompletedReachabilityTest(Result), +pub struct Config { + pub test_server_count: usize, } -pub(super) struct Behaviour +pub struct Behaviour where R: RngCore + 'static, { - pending_commands: VecDeque, + local_peers: HashSet, + pending_nonces: HashSet, + known_servers: Vec, + rng: R, + config: Config, pending_events: VecDeque< ToSwarm< ::ToSwarm, <::ConnectionHandler as ConnectionHandler>::FromBehaviour, >, >, - accepted_nonce: Arc>, - rng: R, + peers_to_handlers: HashMap, + pending_req_for_peer: HashMap>, + pending_requests: VecDeque, } impl NetworkBehaviour for Behaviour @@ -81,148 +51,240 @@ where { type ConnectionHandler = Handler; - type ToSwarm = Event; + type ToSwarm = (); fn handle_established_inbound_connection( &mut self, - _connection_id: ConnectionId, - peer: PeerId, + connection_id: ConnectionId, + _peer: PeerId, _local_addr: &Multiaddr, - _remote_addr: &Multiaddr, - ) -> Result { - Ok(Handler::new(self.accepted_nonce.clone(), peer)) + remote_addr: &Multiaddr, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + if addr_is_local(remote_addr) { + self.local_peers.insert(connection_id); + } + Ok(new_handler()) } fn handle_established_outbound_connection( &mut self, - _connection_id: ConnectionId, - peer: PeerId, - _addr: &Multiaddr, + connection_id: ConnectionId, + _peer: PeerId, + addr: &Multiaddr, _role_override: Endpoint, _port_use: PortUse, - ) -> Result { - Ok(Handler::new(self.accepted_nonce.clone(), peer)) + ) -> Result<::ConnectionHandler, ConnectionDenied> { + if addr_is_local(addr) { + self.local_peers.insert(connection_id); + } + Ok(new_handler()) } - fn on_swarm_event(&mut self, _event: FromSwarm) { - todo!() + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { + for _ in 0..self.config.test_server_count { + let nonce = self.rng.gen(); + self.pending_requests.push_back(DialRequest { + addrs: vec![addr.clone()], + nonce, + }); + self.pending_nonces.insert(nonce); + } + } + FromSwarm::ConnectionEstablished(ConnectionEstablished { + peer_id, + connection_id, + .. + }) => { + self.peers_to_handlers + .entry(peer_id) + .or_insert(connection_id); + } + FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + .. + }) => { + tracing::trace!("connection with {peer_id:?} closed"); + self.handle_no_connection(peer_id, connection_id); + } + FromSwarm::DialFailure(DialFailure { + peer_id: Some(peer_id), + error, + connection_id, + }) => { + tracing::trace!("dialing {peer_id:?} failed: {error:?}"); + self.handle_no_connection(peer_id, connection_id); + } + _ => {} + } } fn on_connection_handler_event( &mut self, peer_id: PeerId, - _connection_id: ConnectionId, - event: ::ToBehaviour, + connection_id: ConnectionId, + event: ::ToBehaviour, ) { + self.peers_to_handlers + .entry(peer_id) + .or_insert(connection_id); match event { - ToBehaviour::ResponseInfo(response_info) => { - let event = Event::CompletedReachabilityTest( - self.handle_response_info(peer_id, response_info), + Either::Right(Ok(nonce)) => { + if self.pending_nonces.remove(&nonce) { + tracing::trace!("Received pending nonce from {peer_id:?}"); + } else { + tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); + } + } + Either::Right(Err(err)) => { + tracing::debug!("Dial back failed: {:?}", err); + } + Either::Left(RequestToBehaviour::PeerHasServerSupport) => { + if !self.known_servers.contains(&peer_id) { + self.known_servers.push(peer_id); + } + } + Either::Left(RequestToBehaviour::TestCompleted(Ok(TestEnd { + dial_request: DialRequest { nonce, addrs }, + suspicious_addr, + reachable_addr, + }))) => { + if self.pending_nonces.remove(&nonce) { + tracing::debug!( + "server reported reachbility, but didn't actually reached this node." + ); + return; + } + if !suspicious_addr.is_empty() { + tracing::trace!( + "server reported suspicious addresses: {:?}", + suspicious_addr + ); + } + self.pending_events.extend( + addrs + .into_iter() + .take_while(|addr| addr != &reachable_addr) + .map(ToSwarm::ExternalAddrExpired), ); - self.pending_events.push_back(ToSwarm::GenerateEvent(event)); + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); + } + Either::Left(RequestToBehaviour::TestCompleted(Err( + RequestError::UnableToConnectOnSelectedAddress { addr: Some(addr) }, + ))) + | Either::Left(RequestToBehaviour::TestCompleted(Err( + RequestError::FailureDuringDialBack { addr: Some(addr) }, + ))) => { + self.pending_events + .push_back(ToSwarm::ExternalAddrExpired(addr)); + } + Either::Left(RequestToBehaviour::TestCompleted(Err(err))) => { + tracing::debug!("Test failed: {:?}", err); } - _ => todo!(), } - todo!() } fn poll( &mut self, _cx: &mut Context<'_>, - ) -> Poll::FromBehaviour>> - { + ) -> Poll::FromBehaviour>> { if let Some(event) = self.pending_events.pop_front() { return Poll::Ready(event); } - if let Some(command) = self.pending_commands.pop_front() { - self.handle_command(command); - return Poll::Pending; + self.pending_req_for_peer.retain(|_, reqs| !reqs.is_empty()); + for (peer, dial_requests) in &mut self.pending_req_for_peer { + if let Some(conn_id) = self.peers_to_handlers.get(peer) { + let dial_request = dial_requests.pop_front().unwrap(); + return Poll::Ready(ToSwarm::NotifyHandler { + peer_id: *peer, + handler: NotifyHandler::One(*conn_id), + event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), + }); + } } - todo!() + if let Some(dial_request) = self.pending_requests.pop_front() { + if self.known_servers.is_empty() { + self.pending_requests.push_front(dial_request); + } else { + let peer = self.known_servers.choose(&mut self.rng).unwrap(); + + self.submit_req_for_peer(*peer, dial_request); + } + } + Poll::Pending } } impl Behaviour where - R: RngCore, + R: RngCore + 'static, { - fn handle_response_info( - &mut self, - peer_id: PeerId, - ResponseInfo { - response: - DialResponse { - status, - dial_status, - .. - }, - suspicious_addrs, - successfull_addr, - }: ResponseInfo, - ) -> Result { - match (status, dial_status) { - (ResponseStatus::E_REQUEST_REJECTED, _) => { - Err(ReachabilityTestErr::ServerRejectedDialRequest { peer_id }) - } - (ResponseStatus::E_DIAL_REFUSED, _) => { - Err(ReachabilityTestErr::ServerChoseNotToDialAnyAddress { peer_id }) - } - (ResponseStatus::E_INTERNAL_ERROR, _) => { - Err(ReachabilityTestErr::InternalServerError { peer_id }) - } - (ResponseStatus::OK, DialStatus::UNUSED) => { - Err(ReachabilityTestErr::InvalidResponse { peer_id }) - } - (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { - Err(ReachabilityTestErr::UnableToConnectOnSelectedAddress { - peer_id, - addr: successfull_addr, - }) - } - (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { - Err(ReachabilityTestErr::FailureDuringDialBack { - peer_id, - addr: successfull_addr, - }) - } - (ResponseStatus::OK, DialStatus::OK) => Ok(ReachabilityTestSucc { - server_peer: peer_id, - visible_addr: successfull_addr, - suspicious_addrs, - }), + fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest) { + if let Some(conn_id) = self.peers_to_handlers.get(&peer) { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: peer, + handler: NotifyHandler::One(*conn_id), + event: Either::Left(RequestFromBehaviour::PerformRequest(req)), + }); + } else { + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(peer) + .condition(PeerCondition::DisconnectedAndNotDialing) + .build(), + }); + self.pending_req_for_peer + .entry(peer) + .or_default() + .push_back(req); } } - fn handle_command(&mut self, command: Command) { - match command { - Command::TestListenerReachability { - server_peer, - local_addrs, - } => { - let _cleaned_local_addrs = local_addrs.iter().filter(|addr| { - !addr.iter().any(|p| match p { - Protocol::Ip4(ip) if !IpExt::is_global(&ip) => true, - Protocol::Ip6(ip) if !IpExt::is_global(&ip) => true, - Protocol::Dns(m) - | Protocol::Dns4(m) - | Protocol::Dns6(m) - | Protocol::Dnsaddr(m) => m == "localhost" || m.ends_with(".local"), - _ => false, + fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { + if matches!(self.peers_to_handlers.get(&peer_id), Some(conn_id) if *conn_id == connection_id) + { + self.peers_to_handlers.remove(&peer_id); + } + for dial_request in self + .pending_req_for_peer + .remove(&peer_id) + .unwrap_or_default() + { + if let Some(new_peer) = self.known_servers.choose(&mut self.rng) { + if let Some(conn_id) = self.peers_to_handlers.get(new_peer) { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: *new_peer, + handler: NotifyHandler::One(*conn_id), + event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), }) - }); - let dial_opts = DialOpts::peer_id(server_peer.clone()).build(); - let dial_event = ToSwarm::Dial { opts: dial_opts }; - self.pending_events.push_back(dial_event); - let dial_request = DialRequest { - addrs: local_addrs, - nonce: self.rng.next_u64(), - }; - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: server_peer, - handler: NotifyHandler::Any, - event: FromBehaviour::Dial(dial_request), - }) + } else { + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(*new_peer) + .condition(PeerCondition::DisconnectedAndNotDialing) + .build(), + }); + self.pending_req_for_peer + .entry(*new_peer) + .or_default() + .push_back(dial_request); + } + } else { + self.pending_requests.push_front(dial_request); } } } } + +fn addr_is_local(addr: &Multiaddr) -> bool { + addr.iter().any(|c| match c { + Protocol::Dns(addr) + | Protocol::Dns4(addr) + | Protocol::Dns6(addr) + | Protocol::Dnsaddr(addr) => addr.ends_with(".local"), + Protocol::Ip4(ip) => !IpExt::is_global(&ip), + Protocol::Ip6(ip) => !IpExt::is_global(&ip), + _ => false, + }) +} diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 204a1ef6dee..cbd41b9da1b 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -7,397 +7,25 @@ // TODO: Handlers mod dial_back; +mod request; -use std::{ - cmp::min, - collections::VecDeque, - convert::identity, - io, - sync::Arc, - task::Poll, - time::Duration, -}; - -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; -use libp2p_core::{upgrade::ReadyUpgrade, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - handler::{ConnectionEvent, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, -}; +use std::time::Duration; +use dial_back::Handler as DialBackHandler; +use libp2p_swarm::{ConnectionHandler, ConnectionHandlerSelect}; +use request::Handler as RequestHandler; -use crate::{ - client::ToBehaviour, - generated::structs::mod_DialResponse::ResponseStatus, - request_response::{ - DialBack, DialDataRequest, DialDataResponse, DialRequest, Request, Response, - }, - DIAL_BACK_PROTOCOL_NAME, REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, +pub use dial_back::ToBehaviour as DialBackToBehaviour; +pub use request::{ + Error as RequestError, FromBehaviour as RequestFromBehaviour, TestEnd, + ToBehaviour as RequestToBehaviour, }; -use crate::{DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND}; - -use super::ResponseInfo; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -#[derive(Debug, thiserror::Error)] -pub(super) enum Error { - #[error("io error")] - Io(#[from] io::Error), - #[error("invalid data request index: {index} (max: {max})")] - InvalidDataRequestIndex { index: usize, max: usize }, - #[error("data request too large: {len} (max: {max})")] - DataRequestTooLarge { len: usize, max: usize }, - #[error("data request too small: {len} (min: {min})")] - DataRequestTooSmall { len: usize, min: usize }, - #[error("timeout")] - Timeout(#[from] futures_bounded::Timeout), - #[error("invalid nonce: {nonce} (provided by (by))")] - WrongNonceGiven { nonce: u64, by: PeerId }, - #[error("request protocol removed by peer")] - RequestProtocolRemoved, - #[error("dial back protocol removed by me")] - DialBackProtocolRemoved, -} - -pub(super) struct Handler { - /// Queue of events to return when polled. - queued_events: VecDeque< - ConnectionHandlerEvent< - ::OutboundProtocol, - ::OutboundOpenInfo, - ::ToBehaviour, - ::Error, - >, - >, - pending_data: VecDeque, - queued_requests: VecDeque, - inbound: futures_bounded::FuturesSet>, - outbound: futures_bounded::FuturesSet>, - accepted_nonce: Arc>, - remote_peer_id: PeerId, -} - -impl Handler { - pub(super) fn new(accepted_nonce: Arc>, remote_peer_id: PeerId) -> Self { - Self { - queued_events: VecDeque::new(), - pending_data: VecDeque::new(), - queued_requests: VecDeque::new(), - inbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), - outbound: futures_bounded::FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), - accepted_nonce, - remote_peer_id, - } - } -} - -impl ConnectionHandler for Handler { - type Error = Error; - type ToBehaviour = super::ToBehaviour; - type FromBehaviour = super::FromBehaviour; - type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) - } - - fn poll( - &mut self, - cx: &mut std::task::Context<'_>, - ) -> Poll< - ConnectionHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::ToBehaviour, - Self::Error, - >, - > { - if let Some(event) = self.queued_events.pop_front() { - return Poll::Ready(event); - } - - if let Some(pending_data) = self.pending_data.pop_front() { - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(REQUEST_UPGRADE, Some(pending_data)), - }); - } - - if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { - let perform_request_res = m.map_err(Error::Timeout).and_then(identity); // necessary until flatten_error stabilized - match perform_request_res { - Ok(PerformRequestStatus::Pending(pending)) => { - self.pending_data.push_back(pending); - } - Ok(PerformRequestStatus::Finished(resp_info)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::ResponseInfo(resp_info), - )) - } - Err(e) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::OutboundError(e), - )); - } - } - } - todo!() - } - - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - match event { - super::FromBehaviour::Dial(dial_req) => { - self.queued_requests.push_back(dial_req); - } - } - } - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, - info: None, - }) => { - let request = self - .queued_requests - .pop_front() - .expect("opened a stream without a penidng request"); - let request_clone = request.clone(); - if self - .outbound - .try_push(perform_dial_request(protocol, request)) - .is_err() - { - println!( - "Dropping outbound stream because we are at capacity. {request_clone:?}" - ); - self.queued_requests.push_front(request_clone); - } - } - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, - info: Some(pending_data), - }) => { - let pending_data_copy = pending_data.clone(); - if self - .outbound - .try_push(perform_data_request(protocol, pending_data)) - .is_err() - { - println!("Dropping outbound stream because we are at capacity."); - self.pending_data.push_front(pending_data_copy); - } - } - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - let remote_peer_id = self.remote_peer_id.clone(); - if self - .inbound - .try_push(perform_dial_back( - protocol, - self.accepted_nonce.clone(), - remote_peer_id, - )) - .is_err() - { - println!("Dropping inbound stream because we are at capacity."); - } - } - ConnectionEvent::DialUpgradeError(_err) => { - todo!() - } - ConnectionEvent::ListenUpgradeError(_err) => { - todo!() - } - ConnectionEvent::AddressChange(_) => {} - ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { - if protocols.any(|e| &DIAL_BACK_PROTOCOL_NAME == e) { - self.queued_events.push_back(ConnectionHandlerEvent::Close( - Error::DialBackProtocolRemoved, - )); - } - } - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(mut protocols)) => { - if protocols.any(|e| &REQUEST_PROTOCOL_NAME == e) { - self.queued_events - .push_back(ConnectionHandlerEvent::Close(Error::RequestProtocolRemoved)); - } - } - _ => {} - } - todo!() - } -} - -enum PerformRequestStatus { - Finished(ResponseInfo), - Pending(PendingData), -} - -#[derive(Debug, Clone)] -pub(super) struct PendingData { - data_count: usize, - suspicious_addrs: Vec, - all_addrs: Vec, -} - -async fn perform_dial_request( - mut stream: S, - dial_req: DialRequest, -) -> Result -where - S: AsyncRead + AsyncWrite + Unpin, -{ - let addrs = dial_req.addrs.clone(); - let req = Request::Dial(dial_req); - req.write_into(&mut stream).await?; - let resp = Response::read_from(&mut stream).await?; - stream.close().await?; - let DialDataRequest { - addr_idx, - num_bytes, - } = match resp { - Response::Dial(resp) => { - let successfull_addr = if resp.status == ResponseStatus::OK { - addrs.get(resp.addr_idx).cloned() - } else { - None - }; - return Ok(PerformRequestStatus::Finished(ResponseInfo::new( - resp, - Vec::new(), - successfull_addr, - ))); - } - Response::Data(data_req) => data_req, - }; - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(Error::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); - } else if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(Error::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - let suspicious_addr = addrs - .get(addr_idx) - .ok_or(Error::InvalidDataRequestIndex { - index: addr_idx, - max: addrs.len(), - })? - .clone(); - Ok(PerformRequestStatus::Pending(PendingData { - data_count: num_bytes, - suspicious_addrs: vec![suspicious_addr], - all_addrs: addrs, - })) -} - -async fn perform_data_request( - mut stream: S, - PendingData { - mut data_count, - mut suspicious_addrs, - all_addrs, - }: PendingData, -) -> Result -where - S: AsyncRead + AsyncWrite + Unpin, -{ - let data_field_len = min(DATA_FIELD_LEN_UPPER_BOUND, data_count); - data_count -= data_field_len; - let req = Request::Data(DialDataResponse { - data_count: data_field_len, - }); - req.write_into(&mut stream).await?; - if data_count != 0 { - return Ok(PerformRequestStatus::Pending(PendingData { - data_count, - suspicious_addrs, - all_addrs, - })); - } - let resp = Response::read_from(&mut stream).await?; - stream.close().await?; - match resp { - Response::Dial(dial_resp) => { - let successfull_addr = if dial_resp.status == ResponseStatus::OK { - all_addrs.get(dial_resp.addr_idx).cloned() - } else { - None - }; - Ok(PerformRequestStatus::Finished(ResponseInfo::new( - dial_resp, - suspicious_addrs, - successfull_addr, - ))) - } - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => { - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(Error::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); - } else if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(Error::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - let suspicious_addr = - all_addrs - .get(addr_idx) - .ok_or(Error::InvalidDataRequestIndex { - index: addr_idx, - max: all_addrs.len(), - })?; - if !suspicious_addrs.contains(suspicious_addr) { - suspicious_addrs.push(suspicious_addr.clone()); - } - Ok(PerformRequestStatus::Pending(PendingData { - data_count: num_bytes, - suspicious_addrs, - all_addrs, - })) - } - } -} +pub type Handler = ConnectionHandlerSelect; -async fn perform_dial_back( - mut stream: S, - accepted_nonce: Arc>, - remote_peer_id: PeerId, -) -> Result<(), Error> -where - S: AsyncRead + AsyncWrite + Unpin, -{ - let DialBack { nonce } = DialBack::read_from(&mut stream).await?; - if !accepted_nonce.contains_async(&nonce).await { - return Err(Error::WrongNonceGiven { - nonce, - by: remote_peer_id, - }); - } - stream.close().await?; - Ok(()) +pub fn new_handler() -> Handler { + RequestHandler::new().select(DialBackHandler::new()) } diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index edd490c2f8e..7d4075b7fa9 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,12 +1,92 @@ -use libp2p_swarm::ConnectionHandler; +use std::{ + io, + task::{Context, Poll}, +}; -#[derive(Debug)] -pub enum ToBehaviour { - GotNonce(u64), +use either::Either; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use futures_bounded::{FuturesSet, Timeout}; +use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; + +use crate::request_response::DialBack; + +use super::DEFAULT_TIMEOUT; + +pub type ToBehaviour = Result>; + +pub struct Handler { + inbound: FuturesSet>, } -#[derive(Debug, Default)] -pub struct Handler; +impl Handler { + pub fn new() -> Self { + Self { + inbound: FuturesSet::new(DEFAULT_TIMEOUT, 2), + } + } +} impl ConnectionHandler for Handler { + type FromBehaviour = (); + type ToBehaviour = ToBehaviour; + type InboundProtocol = ReadyUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + result + .map_err(Either::Right) + .and_then(|e| e.map_err(Either::Left)), + )); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + if self.inbound.try_push(perform_dial_back(protocol)).is_err() { + tracing::warn!("Dial back request dropped, too many requests in flight"); + } + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { + tracing::debug!("Dial back request failed: {:?}", error); + } + _ => {} + } + } +} + +async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { + let DialBack { nonce } = DialBack::read_from(&mut stream).await?; + stream.close().await?; + Ok(nonce) } diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/request.rs new file mode 100644 index 00000000000..c08f64878c1 --- /dev/null +++ b/protocols/autonatv2/src/client/handler/request.rs @@ -0,0 +1,302 @@ +use asynchronous_codec::FramedWrite; +use futures::{ + channel::mpsc, pin_mut, select, AsyncRead, AsyncWrite, FutureExt, SinkExt, StreamExt, +}; +use futures_bounded::{FuturesSet, Timeout}; +use libp2p_core::{ + upgrade::{DeniedUpgrade, ReadyUpgrade}, + Multiaddr, +}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsAdded, ProtocolsChange, + }, + AddressChange, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; +use std::{ + collections::VecDeque, + convert::identity, + io, + iter::{once, repeat}, + pin::Pin, + task::{Context, Poll}, +}; + +use crate::{ + add, + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + request_response::{ + DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, + }, + REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, +}; + +use super::DEFAULT_TIMEOUT; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("io error")] + Io(#[from] io::Error), + #[error("invalid referenced address index: {index} (max number of addr: {max})")] + InvalidReferencedAddress { index: usize, max: usize }, + #[error("data request too large: {len} (max: {max})")] + DataRequestTooLarge { len: usize, max: usize }, + #[error("data request too small: {len} (min: {min})")] + DataRequestTooSmall { len: usize, min: usize }, + #[error("timeout")] + Timeout(#[from] futures_bounded::Timeout), + #[error("server rejected dial request")] + ServerRejectedDialRequest, + #[error("server chose not to dial any provided address")] + ServerChoseNotToDialAnyAddress, + #[error("server ran into an internal error")] + InternalServerError, + #[error("server did not respond correctly to dial request")] + InvalidResponse, + #[error("server was unable to connect to address: {addr:?}")] + UnableToConnectOnSelectedAddress { addr: Option }, + #[error("server experienced failure during dial back on address: {addr:?}")] + FailureDuringDialBack { addr: Option }, +} + + +#[derive(Debug)] +pub struct TestEnd { + pub dial_request: DialRequest, + pub suspicious_addr: Vec, + pub reachable_addr: Multiaddr, +} + +#[derive(Debug)] +pub enum ToBehaviour { + TestCompleted(Result), + PeerHasServerSupport, +} + +#[derive(Debug)] +pub enum FromBehaviour { + PerformRequest(DialRequest), +} + +pub struct Handler { + queued_events: VecDeque< + ConnectionHandlerEvent< + ::OutboundProtocol, + ::OutboundOpenInfo, + ::ToBehaviour, + >, + >, + outbound: futures_bounded::FuturesSet>, + queued_requests: VecDeque, +} + +impl Handler { + pub fn new() -> Self { + Self { + queued_events: VecDeque::new(), + outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), + queued_requests: VecDeque::new(), + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = FromBehaviour; + + type ToBehaviour = ToBehaviour; + + type InboundProtocol = DeniedUpgrade; + + type OutboundProtocol = ReadyUpgrade; + + type InboundOpenInfo = (); + + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Some(event) = self.queued_events.pop_front() { + return Poll::Ready(event); + } + if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), + )); + } + if !self.queued_requests.is_empty() { + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), + }); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + match event { + FromBehaviour::PerformRequest(req) => { + self.queued_requests.push_back(req); + } + } + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + tracing::debug!("Dial request failed: {}", error); + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => match self.queued_requests.pop_front() { + Some(dial_req) => { + if self + .outbound + .try_push(handle_substream(dial_req.clone(), protocol)) + .is_err() + { + tracing::warn!("Dial request dropped, too many requests in flight"); + self.queued_requests.push_front(dial_req); + } + } + None => { + tracing::warn!("Opened unexpected substream without a pending dial request"); + } + }, + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { + if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { + self.queued_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::PeerHasServerSupport, + )); + } + } + _ => {} + } + } +} + +async fn handle_substream( + dial_request: DialRequest, + mut substream: impl AsyncRead + AsyncWrite + Unpin, +) -> Result { + Request::Dial(dial_request.clone()) + .write_into(&mut substream) + .await?; + let mut suspicious_addr = Vec::new(); + loop { + match Response::read_from(&mut substream).await? { + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if addr_idx >= dial_request.addrs.len() { + return Err(Error::InvalidReferencedAddress { + index: addr_idx, + max: dial_request.addrs.len(), + }); + } + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(Error::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } + if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); + } + match dial_request.addrs.get(addr_idx) { + Some(addr) => { + tracing::trace!("the address {addr} is suspicious to the server, sending {num_bytes} bytes of data"); + suspicious_addr.push(addr.clone()); + } + None => { + return Err(Error::InvalidReferencedAddress { + index: addr_idx, + max: dial_request.addrs.len(), + }); + } + } + + send_aap_data(&mut substream, num_bytes).await?; + } + Response::Dial(dial_response) => { + return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); + } + } + } +} + +fn test_end_from_dial_response( + req: DialRequest, + resp: DialResponse, + suspicious_addr: Vec, +) -> Result { + if resp.addr_idx >= req.addrs.len() { + return Err(Error::InvalidReferencedAddress { + index: resp.addr_idx, + max: req.addrs.len(), + }); + } + match (resp.status, resp.dial_status) { + (ResponseStatus::E_REQUEST_REJECTED, _) => Err(Error::ServerRejectedDialRequest), + (ResponseStatus::E_DIAL_REFUSED, _) => Err(Error::ServerChoseNotToDialAnyAddress), + (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServerError), + (ResponseStatus::OK, DialStatus::UNUSED) => Err(Error::InvalidResponse), + (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { + Err(Error::UnableToConnectOnSelectedAddress { + addr: req.addrs.get(resp.addr_idx).cloned(), + }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => Err(Error::FailureDuringDialBack { + addr: req.addrs.get(resp.addr_idx).cloned(), + }), + (ResponseStatus::OK, DialStatus::OK) => req + .addrs + .get(resp.addr_idx) + .ok_or(Error::InvalidReferencedAddress { + index: resp.addr_idx, + max: req.addrs.len(), + }) + .cloned() + .map(|reachable_addr| TestEnd { + dial_request: req, + suspicious_addr, + reachable_addr, + }), + } +} + +async fn send_aap_data(mut substream: impl AsyncWrite + Unpin, num_bytes: usize) -> io::Result<()> { + let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; + let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; + for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) + .take(count_full) + .chain(once(partial_len)) + .filter(|e| *e > 0) + .map(|data_count| Request::Data(DialDataResponse { data_count })) + { + req.write_into(&mut substream).await?; + } + Ok(()) +} diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonatv2/src/global_only.rs new file mode 100644 index 00000000000..4a9b4d7846a --- /dev/null +++ b/protocols/autonatv2/src/global_only.rs @@ -0,0 +1,247 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +pub trait Ipv4Ext { + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + fn is_reserved(&self) -> bool; + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + fn is_benchmarking(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + fn is_shared(&self) -> bool; + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + fn is_private(&self) -> bool; +} + +impl Ipv4Ext for Ipv4Addr { + #[inline] + fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + #[inline] + fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + #[inline] + fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + #[inline] + fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if (16..=31).contains(&b) => true, + [192, 168, ..] => true, + _ => false, + } + } +} + +pub trait Ipv6Ext { + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + fn is_unicast_link_local(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + fn is_unique_local(&self) -> bool; + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + fn is_documentation(&self) -> bool; +} + +impl Ipv6Ext for Ipv6Addr { + #[inline] + fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + #[inline] + fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + #[inline] + fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } +} + +pub trait IpExt { + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. + #[must_use] + fn is_global(&self) -> bool; +} + +impl IpExt for Ipv4Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[inline] + fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || Ipv4Ext::is_shared(self) + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || Ipv4Ext::is_benchmarking(self) + || Ipv4Ext::is_reserved(self) + || self.is_broadcast()) + } +} + +impl IpExt for Ipv6Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[inline] + fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || Ipv6Ext::is_documentation(self) + || Ipv6Ext::is_unique_local(self) + || Ipv6Ext::is_unicast_link_local(self)) + } +} + +impl IpExt for IpAddr { + #[inline] + fn is_global(&self) -> bool { + match self { + Self::V4(v4) => IpExt::is_global(v4), + Self::V6(v6) => IpExt::is_global(v6), + } + } +} diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index ad58455ca04..ae7b1ccbccd 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -3,6 +3,7 @@ use libp2p_swarm::StreamProtocol; mod client; mod generated; +mod global_only; pub(crate) mod request_response; pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 0547afe603e..03205df440e 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -5,10 +5,7 @@ use std::{borrow::Cow, io}; use asynchronous_codec::{FramedRead, FramedWrite}; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; -use libp2p_core::{ - upgrade::{read_length_prefixed, write_length_prefixed}, - Multiaddr, -}; +use libp2p_core::Multiaddr; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use quick_protobuf_codec::Codec; From 60b4cccd9d6d4ef95583f519b86f933350f95052 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:16:20 +0100 Subject: [PATCH 036/179] Run cargo fix --- core/src/transport/and_then.rs | 2 +- misc/ip-global/src/lib.rs | 3 +- protocols/autonatv2/src/client.rs | 1 - protocols/autonatv2/src/client/behaviour.rs | 10 +++--- protocols/autonatv2/src/client/handler.rs | 7 ++-- .../autonatv2/src/client/handler/dial_back.rs | 6 ++-- .../autonatv2/src/client/handler/request.rs | 36 ++++++++----------- protocols/autonatv2/src/global_only.rs | 6 ++-- protocols/autonatv2/src/lib.rs | 4 --- protocols/autonatv2/src/request_response.rs | 1 - 10 files changed, 30 insertions(+), 46 deletions(-) diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index 128f6952a1a..fb6ed2949a4 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -20,7 +20,7 @@ use crate::{ connection::ConnectedPoint, - transport::{ListenerId, Transport, TransportError, TransportEvent, DialOpts}, + transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}, }; use either::Either; use futures::prelude::*; diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs index e3f1fb4f2ae..c469fbb7e31 100644 --- a/misc/ip-global/src/lib.rs +++ b/misc/ip-global/src/lib.rs @@ -5,8 +5,7 @@ //! //! Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) - -use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; pub trait Ipv4Ext { /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index 016ba931f10..dff2397252c 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,3 +1,2 @@ mod behaviour; mod handler; - diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 7eb41a2b229..655c6666b09 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,10 +1,10 @@ use std::{ - collections::{BTreeMap, HashMap, HashSet, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, task::{Context, Poll}, }; use either::Either; -use libp2p_core::{multiaddr::Protocol, transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::ConnectionEstablished, @@ -21,11 +21,11 @@ use super::handler::{ new_handler, Handler, RequestError, RequestFromBehaviour, RequestToBehaviour, TestEnd, }; -pub struct Config { - pub test_server_count: usize, +pub(crate) struct Config { + pub(crate) test_server_count: usize, } -pub struct Behaviour +pub(crate) struct Behaviour where R: RngCore + 'static, { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index cbd41b9da1b..03b9e77eb2c 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -15,8 +15,7 @@ use dial_back::Handler as DialBackHandler; use libp2p_swarm::{ConnectionHandler, ConnectionHandlerSelect}; use request::Handler as RequestHandler; -pub use dial_back::ToBehaviour as DialBackToBehaviour; -pub use request::{ +pub(crate) use request::{ Error as RequestError, FromBehaviour as RequestFromBehaviour, TestEnd, ToBehaviour as RequestToBehaviour, }; @@ -24,8 +23,8 @@ pub use request::{ const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub type Handler = ConnectionHandlerSelect; +pub(crate) type Handler = ConnectionHandlerSelect; -pub fn new_handler() -> Handler { +pub(crate) fn new_handler() -> Handler { RequestHandler::new().select(DialBackHandler::new()) } diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 7d4075b7fa9..260cf28c369 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -16,14 +16,14 @@ use crate::request_response::DialBack; use super::DEFAULT_TIMEOUT; -pub type ToBehaviour = Result>; +pub(crate) type ToBehaviour = Result>; -pub struct Handler { +pub(crate) struct Handler { inbound: FuturesSet>, } impl Handler { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { inbound: FuturesSet::new(DEFAULT_TIMEOUT, 2), } diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/request.rs index c08f64878c1..21b8879b19a 100644 --- a/protocols/autonatv2/src/client/handler/request.rs +++ b/protocols/autonatv2/src/client/handler/request.rs @@ -1,18 +1,13 @@ -use asynchronous_codec::FramedWrite; -use futures::{ - channel::mpsc, pin_mut, select, AsyncRead, AsyncWrite, FutureExt, SinkExt, StreamExt, -}; -use futures_bounded::{FuturesSet, Timeout}; +use futures::{AsyncRead, AsyncWrite}; +use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, }; -use libp2p_identity::PeerId; + use libp2p_swarm::{ - handler::{ - ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsAdded, ProtocolsChange, - }, - AddressChange, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; use std::{ @@ -20,12 +15,10 @@ use std::{ convert::identity, io, iter::{once, repeat}, - pin::Pin, task::{Context, Poll}, }; use crate::{ - add, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, @@ -37,7 +30,7 @@ use crate::{ use super::DEFAULT_TIMEOUT; #[derive(Debug, thiserror::Error)] -pub enum Error { +pub(crate) enum Error { #[error("io error")] Io(#[from] io::Error), #[error("invalid referenced address index: {index} (max number of addr: {max})")] @@ -62,26 +55,25 @@ pub enum Error { FailureDuringDialBack { addr: Option }, } - #[derive(Debug)] -pub struct TestEnd { - pub dial_request: DialRequest, - pub suspicious_addr: Vec, - pub reachable_addr: Multiaddr, +pub(crate) struct TestEnd { + pub(crate) dial_request: DialRequest, + pub(crate) suspicious_addr: Vec, + pub(crate) reachable_addr: Multiaddr, } #[derive(Debug)] -pub enum ToBehaviour { +pub(crate) enum ToBehaviour { TestCompleted(Result), PeerHasServerSupport, } #[derive(Debug)] -pub enum FromBehaviour { +pub(crate) enum FromBehaviour { PerformRequest(DialRequest), } -pub struct Handler { +pub(crate) struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< ::OutboundProtocol, @@ -94,7 +86,7 @@ pub struct Handler { } impl Handler { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonatv2/src/global_only.rs index 4a9b4d7846a..e549030eff9 100644 --- a/protocols/autonatv2/src/global_only.rs +++ b/protocols/autonatv2/src/global_only.rs @@ -1,6 +1,6 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -pub trait Ipv4Ext { +pub(crate) trait Ipv4Ext { /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since @@ -67,7 +67,7 @@ impl Ipv4Ext for Ipv4Addr { } } -pub trait Ipv6Ext { +pub(crate) trait Ipv6Ext { /// Returns `true` if the address is a unicast address with link-local scope, /// as defined in [RFC 4291]. /// @@ -129,7 +129,7 @@ impl Ipv6Ext for Ipv6Addr { } } -pub trait IpExt { +pub(crate) trait IpExt { /// Returns [`true`] if the address appears to be globally routable. /// /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index ae7b1ccbccd..db7605757ee 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -15,10 +15,6 @@ pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); -pub(crate) use request_response::DATA_FIELD_LEN_UPPER_BOUND; -pub(crate) use request_response::DATA_LEN_LOWER_BOUND; -pub(crate) use request_response::DATA_LEN_UPPER_BOUND; - pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 03205df440e..5551d9ab4a9 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -7,7 +7,6 @@ use asynchronous_codec::{FramedRead, FramedWrite}; use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::Multiaddr; -use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use quick_protobuf_codec::Codec; use rand::Rng; From 71487fcf5bdc6b9b2ef59d69ff5889a3023cec05 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:04:13 +0100 Subject: [PATCH 037/179] Correct minor things --- protocols/autonatv2/src/client/behaviour.rs | 36 +++++++++---------- .../autonatv2/src/client/handler/request.rs | 4 +-- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 655c6666b09..d6835507fd9 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -191,8 +191,9 @@ where &mut self, _cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); + let pending_event = self.poll_pending_events(); + if pending_event.is_ready() { + return pending_event; } self.pending_req_for_peer.retain(|_, reqs| !reqs.is_empty()); for (peer, dial_requests) in &mut self.pending_req_for_peer { @@ -210,8 +211,8 @@ where self.pending_requests.push_front(dial_request); } else { let peer = self.known_servers.choose(&mut self.rng).unwrap(); - self.submit_req_for_peer(*peer, dial_request); + return self.poll_pending_events(); } } Poll::Pending @@ -253,28 +254,23 @@ where .unwrap_or_default() { if let Some(new_peer) = self.known_servers.choose(&mut self.rng) { - if let Some(conn_id) = self.peers_to_handlers.get(new_peer) { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: *new_peer, - handler: NotifyHandler::One(*conn_id), - event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), - }) - } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(*new_peer) - .condition(PeerCondition::DisconnectedAndNotDialing) - .build(), - }); - self.pending_req_for_peer - .entry(*new_peer) - .or_default() - .push_back(dial_request); - } + self.submit_req_for_peer(*new_peer, dial_request); } else { self.pending_requests.push_front(dial_request); } } } + + fn poll_pending_events( + &mut self, + ) -> Poll< + ToSwarm<::ToSwarm, ::FromBehaviour>, + > { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + Poll::Pending + } } fn addr_is_local(addr: &Multiaddr) -> bool { diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/request.rs index 21b8879b19a..6a543c31ab6 100644 --- a/protocols/autonatv2/src/client/handler/request.rs +++ b/protocols/autonatv2/src/client/handler/request.rs @@ -46,7 +46,7 @@ pub(crate) enum Error { #[error("server chose not to dial any provided address")] ServerChoseNotToDialAnyAddress, #[error("server ran into an internal error")] - InternalServerError, + InternalServer, #[error("server did not respond correctly to dial request")] InvalidResponse, #[error("server was unable to connect to address: {addr:?}")] @@ -253,7 +253,7 @@ fn test_end_from_dial_response( match (resp.status, resp.dial_status) { (ResponseStatus::E_REQUEST_REJECTED, _) => Err(Error::ServerRejectedDialRequest), (ResponseStatus::E_DIAL_REFUSED, _) => Err(Error::ServerChoseNotToDialAnyAddress), - (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServerError), + (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServer), (ResponseStatus::OK, DialStatus::UNUSED) => Err(Error::InvalidResponse), (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { Err(Error::UnableToConnectOnSelectedAddress { From 63b31e966432ebf440fe0d5273b6ea437ec75330 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:17:57 +0100 Subject: [PATCH 038/179] Implement suggestions --- protocols/autonatv2/src/client/behaviour.rs | 136 +++++++++--------- protocols/autonatv2/src/client/handler.rs | 20 +-- .../autonatv2/src/client/handler/dial_back.rs | 7 +- .../handler/{request.rs => dial_request.rs} | 96 +++++++++---- protocols/autonatv2/src/request_response.rs | 34 ++--- 5 files changed, 170 insertions(+), 123 deletions(-) rename protocols/autonatv2/src/client/handler/{request.rs => dial_request.rs} (77%) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index d6835507fd9..d82e4eac17b 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,28 +1,44 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, task::{Context, Poll}, + time::{Duration, Instant}, }; use either::Either; use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - behaviour::ConnectionEstablished, + behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, dial_opts::{DialOpts, PeerCondition}, ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; -use rand::{seq::SliceRandom, Rng}; +use rand::{distributions::Standard, seq::SliceRandom, Rng}; use rand_core::RngCore; use crate::{global_only::IpExt, request_response::DialRequest}; -use super::handler::{ - new_handler, Handler, RequestError, RequestFromBehaviour, RequestToBehaviour, TestEnd, -}; +use super::handler::{dial_back, dial_request, Handler, TestEnd}; + +struct IntervalTicker { + interval: Duration, + last_tick: Instant, +} + +impl IntervalTicker { + fn ready(&mut self) -> bool { + if self.last_tick.elapsed() >= self.interval { + self.last_tick = Instant::now(); + true + } else { + false + } + } +} pub(crate) struct Config { pub(crate) test_server_count: usize, + pub(crate) max_addrs_count: usize, } pub(crate) struct Behaviour @@ -40,9 +56,9 @@ where <::ConnectionHandler as ConnectionHandler>::FromBehaviour, >, >, + address_candidates: HashMap, peers_to_handlers: HashMap, - pending_req_for_peer: HashMap>, - pending_requests: VecDeque, + ticker: IntervalTicker, } impl NetworkBehaviour for Behaviour @@ -63,7 +79,7 @@ where if addr_is_local(remote_addr) { self.local_peers.insert(connection_id); } - Ok(new_handler()) + Ok(Either::Left(dial_request::Handler::new())) } fn handle_established_outbound_connection( @@ -77,20 +93,16 @@ where if addr_is_local(addr) { self.local_peers.insert(connection_id); } - Ok(new_handler()) + Ok(Either::Right(dial_back::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - for _ in 0..self.config.test_server_count { - let nonce = self.rng.gen(); - self.pending_requests.push_back(DialRequest { - addrs: vec![addr.clone()], - nonce, - }); - self.pending_nonces.insert(nonce); - } + *self.address_candidates.entry(addr.clone()).or_default() += 1; + } + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { + self.address_candidates.remove(addr); } FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, @@ -141,12 +153,12 @@ where Either::Right(Err(err)) => { tracing::debug!("Dial back failed: {:?}", err); } - Either::Left(RequestToBehaviour::PeerHasServerSupport) => { + Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { if !self.known_servers.contains(&peer_id) { self.known_servers.push(peer_id); } } - Either::Left(RequestToBehaviour::TestCompleted(Ok(TestEnd { + Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { dial_request: DialRequest { nonce, addrs }, suspicious_addr, reachable_addr, @@ -172,16 +184,16 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } - Either::Left(RequestToBehaviour::TestCompleted(Err( - RequestError::UnableToConnectOnSelectedAddress { addr: Some(addr) }, - ))) - | Either::Left(RequestToBehaviour::TestCompleted(Err( - RequestError::FailureDuringDialBack { addr: Some(addr) }, - ))) => { + Either::Left(dial_request::ToBehaviour::TestCompleted( + Err(dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }) + )) + | Either::Left(dial_request::ToBehaviour::TestCompleted( + Err(dial_request::Error::FailureDuringDialBack { addr: Some(addr) }) + )) => { self.pending_events .push_back(ToSwarm::ExternalAddrExpired(addr)); } - Either::Left(RequestToBehaviour::TestCompleted(Err(err))) => { + Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { tracing::debug!("Test failed: {:?}", err); } } @@ -195,24 +207,35 @@ where if pending_event.is_ready() { return pending_event; } - self.pending_req_for_peer.retain(|_, reqs| !reqs.is_empty()); - for (peer, dial_requests) in &mut self.pending_req_for_peer { - if let Some(conn_id) = self.peers_to_handlers.get(peer) { - let dial_request = dial_requests.pop_front().unwrap(); - return Poll::Ready(ToSwarm::NotifyHandler { - peer_id: *peer, - handler: NotifyHandler::One(*conn_id), - event: Either::Left(RequestFromBehaviour::PerformRequest(dial_request)), - }); - } - } - if let Some(dial_request) = self.pending_requests.pop_front() { - if self.known_servers.is_empty() { - self.pending_requests.push_front(dial_request); + if self.ticker.ready() && !self.known_servers.is_empty() { + let mut entries = self.address_candidates.drain().collect::>(); + entries.sort_unstable_by_key(|(_, count)| *count); + let addrs = entries + .into_iter() + .rev() + .map(|(addr, _)| addr) + .take(self.config.max_addrs_count) + .collect::>(); + let peers = if self.known_servers.len() < self.config.test_server_count { + self.known_servers.clone() } else { - let peer = self.known_servers.choose(&mut self.rng).unwrap(); - self.submit_req_for_peer(*peer, dial_request); - return self.poll_pending_events(); + self.known_servers + .choose_multiple(&mut self.rng, self.config.test_server_count) + .copied() + .collect() + }; + for peer in peers { + let nonce = self.rng.gen(); + let req = DialRequest { + nonce, + addrs: addrs.clone(), + }; + self.pending_nonces.insert(nonce); + self.submit_req_for_peer(peer, req); + } + let pending_event = self.poll_pending_events(); + if pending_event.is_ready() { + return pending_event; } } Poll::Pending @@ -228,18 +251,13 @@ where self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, handler: NotifyHandler::One(*conn_id), - event: Either::Left(RequestFromBehaviour::PerformRequest(req)), + event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), }); } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer) - .condition(PeerCondition::DisconnectedAndNotDialing) - .build(), - }); - self.pending_req_for_peer - .entry(peer) - .or_default() - .push_back(req); + tracing::debug!( + "There should be a connection to {:?}, but there isn't", + peer + ); } } @@ -248,17 +266,7 @@ where { self.peers_to_handlers.remove(&peer_id); } - for dial_request in self - .pending_req_for_peer - .remove(&peer_id) - .unwrap_or_default() - { - if let Some(new_peer) = self.known_servers.choose(&mut self.rng) { - self.submit_req_for_peer(*new_peer, dial_request); - } else { - self.pending_requests.push_front(dial_request); - } - } + self.known_servers.retain(|p| p != &peer_id); } fn poll_pending_events( diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 03b9e77eb2c..9e8389bb630 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -6,25 +6,15 @@ // TODO: tests // TODO: Handlers -mod dial_back; -mod request; +pub(super) mod dial_back; +pub(super) mod dial_request; +use either::Either; use std::time::Duration; -use dial_back::Handler as DialBackHandler; -use libp2p_swarm::{ConnectionHandler, ConnectionHandlerSelect}; -use request::Handler as RequestHandler; - -pub(crate) use request::{ - Error as RequestError, FromBehaviour as RequestFromBehaviour, TestEnd, - ToBehaviour as RequestToBehaviour, -}; +pub(crate) use dial_request::TestEnd; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub(crate) type Handler = ConnectionHandlerSelect; - -pub(crate) fn new_handler() -> Handler { - RequestHandler::new().select(DialBackHandler::new()) -} +pub(crate) type Handler = Either; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 260cf28c369..f1a8a618916 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,4 +1,5 @@ use std::{ + convert::identity, io, task::{Context, Poll}, }; @@ -16,7 +17,7 @@ use crate::request_response::DialBack; use super::DEFAULT_TIMEOUT; -pub(crate) type ToBehaviour = Result>; +pub(crate) type ToBehaviour = Result; pub(crate) struct Handler { inbound: FuturesSet>, @@ -51,8 +52,8 @@ impl ConnectionHandler for Handler { if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( result - .map_err(Either::Right) - .and_then(|e| e.map_err(Either::Left)), + .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) + .and_then(identity), )); } Poll::Pending diff --git a/protocols/autonatv2/src/client/handler/request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs similarity index 77% rename from protocols/autonatv2/src/client/handler/request.rs rename to protocols/autonatv2/src/client/handler/dial_request.rs index 6a543c31ab6..a26fe4e458d 100644 --- a/protocols/autonatv2/src/client/handler/request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,4 +1,4 @@ -use futures::{AsyncRead, AsyncWrite}; +use futures::{channel::oneshot, AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -6,8 +6,12 @@ use libp2p_core::{ }; use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, + handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, + ProtocolsChange, + }, + ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, + SubstreamProtocol, }; use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; use std::{ @@ -53,6 +57,10 @@ pub(crate) enum Error { UnableToConnectOnSelectedAddress { addr: Option }, #[error("server experienced failure during dial back on address: {addr:?}")] FailureDuringDialBack { addr: Option }, + #[error("error during substream upgrad")] + SubstreamError( + #[from] StreamUpgradeError< as OutboundUpgradeSend>::Error>, + ), } #[derive(Debug)] @@ -82,7 +90,14 @@ pub(crate) struct Handler { >, >, outbound: futures_bounded::FuturesSet>, - queued_requests: VecDeque, + queued_streams: VecDeque< + oneshot::Sender< + Result< + Stream, + StreamUpgradeError< as OutboundUpgradeSend>::Error>, + >, + >, + >, } impl Handler { @@ -90,7 +105,23 @@ impl Handler { Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), - queued_requests: VecDeque::new(), + queued_streams: VecDeque::default(), + } + } + + fn perform_request(&mut self, req: DialRequest) { + let (tx, rx) = oneshot::channel(); + self.queued_streams.push_back(tx); + self.queued_events + .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), + }); + if self + .outbound + .try_push(start_substream_handle(req, rx)) + .is_err() + { + tracing::debug!("Dial request dropped, too many requests in flight"); } } } @@ -126,18 +157,13 @@ impl ConnectionHandler for Handler { ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), )); } - if !self.queued_requests.is_empty() { - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), - }); - } Poll::Pending } fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { match event { FromBehaviour::PerformRequest(req) => { - self.queued_requests.push_back(req); + self.perform_request(req); } } } @@ -154,18 +180,25 @@ impl ConnectionHandler for Handler { match event { ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { tracing::debug!("Dial request failed: {}", error); + match self.queued_streams.pop_front() { + Some(stream_tx) => { + if let Err(_) = stream_tx.send(Err(error)) { + tracing::warn!("Failed to send stream to dead handler"); + } + } + None => { + tracing::warn!( + "Opened unexpected substream without a pending dial request" + ); + } + } } ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { protocol, .. - }) => match self.queued_requests.pop_front() { - Some(dial_req) => { - if self - .outbound - .try_push(handle_substream(dial_req.clone(), protocol)) - .is_err() - { - tracing::warn!("Dial request dropped, too many requests in flight"); - self.queued_requests.push_front(dial_req); + }) => match self.queued_streams.pop_front() { + Some(stream_tx) => { + if let Err(_) = stream_tx.send(Ok(protocol)) { + tracing::warn!("Failed to send stream to dead handler"); } } None => { @@ -174,10 +207,9 @@ impl ConnectionHandler for Handler { }, ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { - self.queued_events - .push_back(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::PeerHasServerSupport, - )); + self.queued_events.push_back( + ConnectionHandlerEvent::NotifyBehaviour(ToBehaviour::PeerHasServerSupport) + ); } } _ => {} @@ -185,6 +217,22 @@ impl ConnectionHandler for Handler { } } +async fn start_substream_handle( + dial_request: DialRequest, + substream_recv: oneshot::Receiver< + Result< + Stream, + StreamUpgradeError< as OutboundUpgradeSend>::Error>, + >, + >, +) -> Result { + match substream_recv.await { + Ok(Ok(substream)) => handle_substream(dial_request, substream).await, + Ok(Err(err)) => Err(Error::from(err)), + Err(_) => Err(Error::InternalServer), + } +} + async fn handle_substream( dial_request: DialRequest, mut substream: impl AsyncRead + AsyncWrite + Unpin, diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 5551d9ab4a9..63bf8451350 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -59,9 +59,9 @@ impl Request { .into_iter() .map(|e| e.to_vec()) .map(|e| { - Multiaddr::try_from(e).map_err(|err| { - new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) - }) + Multiaddr::try_from(e).map_err( + |err| new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + ) }) .collect::, io::Error>>()?; let nonce = check_existence!(nonce)?; @@ -83,10 +83,9 @@ impl Request { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); let nonce = Some(nonce); proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { - addrs, - nonce, - }), + msg: proto::mod_Message::OneOfmsg::dialRequest( + proto::DialRequest { addrs, nonce } + ), } } Request::Data(DialDataResponse { data_count }) => { @@ -150,10 +149,9 @@ impl Response { dial_status, })) } - proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx, - numBytes, - }) => { + proto::mod_Message::OneOfmsg::dialDataRequest( + proto::DialDataRequest { addrIdx, numBytes } + ) => { let addr_idx = check_existence!(addrIdx)? as usize; let num_bytes = check_existence!(numBytes)? as usize; Ok(Self::Data(DialDataRequest { @@ -183,12 +181,14 @@ impl Response { Self::Data(DialDataRequest { addr_idx, num_bytes, - }) => proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), - }), - }, + }) => { + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + } + } }; FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) .send(msg) From c16235fe79fe32f0cd09811c4bcb54ba04bd5246 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:20:00 +0100 Subject: [PATCH 039/179] Remove gloabl only --- Cargo.lock | 8 +- Cargo.toml | 1 - core/Cargo.toml | 1 - core/src/transport/global_only.rs | 252 ++++++++++++++++++++++++++++- misc/ip-global/Cargo.toml | 12 -- misc/ip-global/src/lib.rs | 254 ------------------------------ protocols/autonatv2/Cargo.toml | 3 +- 7 files changed, 253 insertions(+), 278 deletions(-) delete mode 100644 misc/ip-global/Cargo.toml delete mode 100644 misc/ip-global/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 007e3147b05..66efb5211f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2297,10 +2297,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "ip-global" -version = "0.1.0" - [[package]] name = "ipconfig" version = "0.3.2" @@ -2518,12 +2514,11 @@ name = "libp2p-autonatv2" version = "0.1.0" dependencies = [ "async-trait", - "asynchronous-codec 0.6.2", + "asynchronous-codec", "bytes", "either", "futures", "futures-bounded", - "ip-global", "libp2p-core", "libp2p-identity", "libp2p-swarm", @@ -2565,7 +2560,6 @@ dependencies = [ "futures", "futures-timer", "instant", - "ip-global", "libp2p-identity", "libp2p-mplex", "libp2p-noise", diff --git a/Cargo.toml b/Cargo.toml index f419b6351a6..900e0e2bc46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ members = [ "misc/allow-block-list", "misc/connection-limits", "misc/futures-bounded", - "misc/ip-global", "misc/keygen", "misc/memory-connection-limits", "misc/metrics", diff --git a/core/Cargo.toml b/core/Cargo.toml index 722ea9fb611..7380695023b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,7 +16,6 @@ fnv = "1.0" futures = { version = "0.3.29", features = ["executor", "thread-pool"] } futures-timer = "3" instant = "0.1.12" -ip-global = { workspace = true } libp2p-identity = { workspace = true, features = ["peerid", "ed25519"] } multiaddr = { workspace = true } multihash = { workspace = true } diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 0a1779b2375..2963cc955d0 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -17,12 +17,262 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +// +mod ip { + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + + trait Ipv4Ext { + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + fn is_reserved(&self) -> bool; + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + fn is_benchmarking(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + fn is_shared(&self) -> bool; + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + fn is_private(&self) -> bool; + } + + impl Ipv4Ext for Ipv4Addr { + #[inline] + fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + #[inline] + fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + #[inline] + fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + #[inline] + fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if (16..=31).contains(&b) => true, + [192, 168, ..] => true, + _ => false, + } + } + } + + trait Ipv6Ext { + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + fn is_unicast_link_local(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + fn is_unique_local(&self) -> bool; + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + fn is_documentation(&self) -> bool; + } + + impl Ipv6Ext for Ipv6Addr { + #[inline] + fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + #[inline] + fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + #[inline] + fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } + } + + pub(super) trait IpExt { + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. + #[must_use] + fn is_global(&self) -> bool; + } + + impl IpExt for Ipv4Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[inline] + fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || Ipv4Ext::is_shared(self) + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || Ipv4Ext::is_benchmarking(self) + || Ipv4Ext::is_reserved(self) + || self.is_broadcast()) + } + } + + impl IpExt for Ipv6Addr { + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[inline] + fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || Ipv6Ext::is_documentation(self) + || Ipv6Ext::is_unique_local(self) + || Ipv6Ext::is_unicast_link_local(self)) + } + } + + impl IpExt for IpAddr { + #[inline] + fn is_global(&self) -> bool { + match self { + Self::V4(v4) => IpExt::is_global(v4), + Self::V6(v6) => IpExt::is_global(v6), + } + } + } +} use crate::{ multiaddr::{Multiaddr, Protocol}, transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; -use ip_global::*; +use ip::IpExt; use std::{ pin::Pin, task::{Context, Poll}, diff --git a/misc/ip-global/Cargo.toml b/misc/ip-global/Cargo.toml deleted file mode 100644 index 7dfb5c3dd6d..00000000000 --- a/misc/ip-global/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "ip-global" -version = "0.1.0" -edition = "2021" -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[lints] -workspace = true diff --git a/misc/ip-global/src/lib.rs b/misc/ip-global/src/lib.rs deleted file mode 100644 index c469fbb7e31..00000000000 --- a/misc/ip-global/src/lib.rs +++ /dev/null @@ -1,254 +0,0 @@ -//! This crate provides extension traits for [`Ipv4Addr`], [`Ipv6Addr`] and [`IpAddr`] that provide methods -//! to check if an address is reserved for special use. -//! -//! This is primarily a polyfill for `ip` feature, which is currently unstable. -//! -//! Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) - -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -pub trait Ipv4Ext { - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - fn is_reserved(&self) -> bool; - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - fn is_benchmarking(&self) -> bool; - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - fn is_shared(&self) -> bool; - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - fn is_private(&self) -> bool; -} - -impl Ipv4Ext for Ipv4Addr { - #[inline] - fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - #[inline] - fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - #[inline] - fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - #[inline] - fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if (16..=31).contains(&b) => true, - [192, 168, ..] => true, - _ => false, - } - } -} - -pub trait Ipv6Ext { - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - fn is_unicast_link_local(&self) -> bool; - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - fn is_unique_local(&self) -> bool; - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - fn is_documentation(&self) -> bool; -} - -impl Ipv6Ext for Ipv6Addr { - #[inline] - fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - #[inline] - fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - #[inline] - fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } -} - -pub trait IpExt { - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. - #[must_use] - fn is_global(&self) -> bool; -} - -impl IpExt for Ipv4Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[inline] - fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || Ipv4Ext::is_shared(self) - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || Ipv4Ext::is_benchmarking(self) - || Ipv4Ext::is_reserved(self) - || self.is_broadcast()) - } -} - -impl IpExt for Ipv6Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[inline] - fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || Ipv6Ext::is_documentation(self) - || Ipv6Ext::is_unique_local(self) - || Ipv6Ext::is_unicast_link_local(self)) - } -} - -impl IpExt for IpAddr { - #[inline] - fn is_global(&self) -> bool { - match self { - Self::V4(v4) => IpExt::is_global(v4), - Self::V6(v6) => IpExt::is_global(v6), - } - } -} diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 76b2cc7ed87..8729108ab08 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -10,14 +10,13 @@ rust-version.workspace = true async-trait = "0.1" quick-protobuf = "0.8" quick-protobuf-codec = { workspace = true } -asynchronous-codec = "0.6" +asynchronous-codec = "0.7" libp2p-core = { workspace = true } rand_core = "0.6" rand = { version = "0.8", optional = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } futures-bounded = { workspace = true } -ip-global = { workspace = true } void = "1.0.2" either = "1.9.0" futures = "0.3.29" From 3bc107e38f7123a40b3625a687bd6d96b5147252 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:31:24 +0100 Subject: [PATCH 040/179] Initial server implementation --- .../autonatv2/src/client/handler/dial_back.rs | 6 +- protocols/autonatv2/src/lib.rs | 3 + protocols/autonatv2/src/request_response.rs | 38 +-- protocols/autonatv2/src/server.rs | 2 + protocols/autonatv2/src/server/behaviour.rs | 179 ++++++++++++ protocols/autonatv2/src/server/handler.rs | 6 + .../autonatv2/src/server/handler/dial_back.rs | 139 +++++++++ .../src/server/handler/dial_request.rs | 264 ++++++++++++++++++ 8 files changed, 615 insertions(+), 22 deletions(-) create mode 100644 protocols/autonatv2/src/server.rs create mode 100644 protocols/autonatv2/src/server/behaviour.rs create mode 100644 protocols/autonatv2/src/server/handler.rs create mode 100644 protocols/autonatv2/src/server/handler/dial_back.rs create mode 100644 protocols/autonatv2/src/server/handler/dial_request.rs diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index f1a8a618916..f4505335f2c 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -13,14 +13,14 @@ use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use crate::request_response::DialBack; +use crate::{request_response::DialBack, Nonce}; use super::DEFAULT_TIMEOUT; -pub(crate) type ToBehaviour = Result; +pub(crate) type ToBehaviour = io::Result; pub(crate) struct Handler { - inbound: FuturesSet>, + inbound: FuturesSet>, } impl Handler { diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index db7605757ee..5e357b5cf0e 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -2,6 +2,7 @@ use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; mod client; +mod server; mod generated; mod global_only; pub(crate) mod request_response; @@ -15,6 +16,8 @@ pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); +pub type Nonce = u64; + pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 63bf8451350..b87852c7302 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -10,7 +10,7 @@ use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; use rand::Rng; -use crate::generated::structs as proto; +use crate::{generated::structs as proto, Nonce}; const REQUEST_MAX_SIZE: usize = 4104; pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; @@ -59,9 +59,9 @@ impl Request { .into_iter() .map(|e| e.to_vec()) .map(|e| { - Multiaddr::try_from(e).map_err( - |err| new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) - ) + Multiaddr::try_from(e).map_err(|err| { + new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + }) }) .collect::, io::Error>>()?; let nonce = check_existence!(nonce)?; @@ -83,9 +83,10 @@ impl Request { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); let nonce = Some(nonce); proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest( - proto::DialRequest { addrs, nonce } - ), + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { + addrs, + nonce, + }), } } Request::Data(DialDataResponse { data_count }) => { @@ -149,9 +150,10 @@ impl Response { dial_status, })) } - proto::mod_Message::OneOfmsg::dialDataRequest( - proto::DialDataRequest { addrIdx, numBytes } - ) => { + proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx, + numBytes, + }) => { let addr_idx = check_existence!(addrIdx)? as usize; let num_bytes = check_existence!(numBytes)? as usize; Ok(Self::Data(DialDataRequest { @@ -181,14 +183,12 @@ impl Response { Self::Data(DialDataRequest { addr_idx, num_bytes, - }) => { - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), - }), - } - } + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + }, }; FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) .send(msg) @@ -215,7 +215,7 @@ impl DialDataRequest { const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { - pub(crate) nonce: u64, + pub(crate) nonce: Nonce, } impl DialBack { diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs new file mode 100644 index 00000000000..dff2397252c --- /dev/null +++ b/protocols/autonatv2/src/server.rs @@ -0,0 +1,2 @@ +mod behaviour; +mod handler; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs new file mode 100644 index 00000000000..a96d412a0b7 --- /dev/null +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -0,0 +1,179 @@ +use std::{ + collections::{HashMap, VecDeque}, + task::{Context, Poll}, +}; + +use either::Either; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + dial_opts::{DialOpts, PeerCondition}, + ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NotifyHandler, + ToSwarm, +}; +use rand_core::RngCore; + +use super::handler::{ + dial_back, + dial_request::{self, DialBackCommand}, + Handler, +}; + +pub struct Behaviour +where + R: Clone + Send + RngCore + 'static, +{ + handlers: HashMap<(Multiaddr, PeerId), ConnectionId>, + pending_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, + dialing_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, + pending_events: VecDeque< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + >, + rng: R, +} + +impl Behaviour +where + R: RngCore + Send + Clone + 'static, +{ + pub(crate) fn new(rng: R) -> Self { + Self { + handlers: HashMap::new(), + pending_dial_back: HashMap::new(), + dialing_dial_back: HashMap::new(), + pending_events: VecDeque::new(), + rng, + } + } + + fn poll_pending_events( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + > { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + Poll::Pending + } +} + +impl NetworkBehaviour for Behaviour +where + R: RngCore + Send + Clone + 'static, +{ + type ConnectionHandler = Handler; + + type ToSwarm = (); + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _peer: PeerId, + _local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + Ok(Either::Right(dial_request::Handler::new( + remote_addr.clone(), + self.rng.clone(), + ))) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + if port_use == PortUse::New { + self.handlers.insert((addr.clone(), peer), connection_id); + } + Ok(Either::Left(dial_back::Handler::new())) + } + + fn on_swarm_event(&mut self, event: FromSwarm) {} + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + _connection_id: ConnectionId, + event: as ConnectionHandler>::ToBehaviour, + ) { + if let Either::Right(m) = event { + match m { + Ok(cmd) => { + let addr = cmd.addr.clone(); + if let Some(connection_id) = self.handlers.get(&(addr.clone(), peer_id)) { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(*connection_id), + event: Either::Left(cmd), + }); + } else { + if let Some(pending) = + self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) + { + pending.push_back(cmd); + } else { + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(peer_id.clone()) + .condition(PeerCondition::Always) + .addresses(vec![addr.clone()]) + .allocate_new_port() + .build(), + }); + self.dialing_dial_back + .insert((addr, peer_id), VecDeque::from([cmd])); + } + } + } + Err(e) => { + tracing::warn!("incoming dial request failed: {}", e); + } + } + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll as ConnectionHandler>::FromBehaviour>> { + let pending_event = self.poll_pending_events(cx); + if pending_event.is_ready() { + return pending_event; + } + if let Some((addr, peer)) = self + .dialing_dial_back + .keys() + .filter(|k| self.handlers.contains_key(*k)) + .next() + .cloned() + { + let cmds = self + .dialing_dial_back + .remove(&(addr.clone(), peer)) + .unwrap(); + let cmd_n = cmds.len(); + for cmd in cmds { + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id: peer.clone(), + handler: NotifyHandler::One(self.handlers[&(addr.clone(), peer)]), + event: Either::Left(cmd), + }); + } + if cmd_n > 0 { + return self.poll_pending_events(cx); + } + } + Poll::Pending + } +} diff --git a/protocols/autonatv2/src/server/handler.rs b/protocols/autonatv2/src/server/handler.rs new file mode 100644 index 00000000000..7beb8f00ec3 --- /dev/null +++ b/protocols/autonatv2/src/server/handler.rs @@ -0,0 +1,6 @@ +use either::Either; + +pub(crate) mod dial_back; +pub(crate) mod dial_request; + +pub type Handler = Either>; diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs new file mode 100644 index 00000000000..4f8d7915063 --- /dev/null +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -0,0 +1,139 @@ +use std::{ + collections::VecDeque, + convert::identity, + io, + task::{Context, Poll}, + time::Duration, +}; + +use futures::{AsyncWrite, AsyncWriteExt}; +use futures_bounded::FuturesSet; +use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; +use libp2p_swarm::{ + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; + +use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; + +use super::dial_request::{DialBack as DialBackRes, DialBackCommand}; + +pub type ToBehaviour = io::Result<()>; +pub type FromBehaviour = DialBackCommand; + +pub struct Handler { + pending_nonce: VecDeque, + requested_substream_nonce: VecDeque, + outbound: FuturesSet, +} + +impl Handler { + pub(crate) fn new() -> Self { + Self { + pending_nonce: VecDeque::new(), + requested_substream_nonce: VecDeque::new(), + outbound: FuturesSet::new(Duration::from_secs(10), 2), + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = FromBehaviour; + type ToBehaviour = ToBehaviour; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = ReadyUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Poll::Ready(result) = self.outbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + result + .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) + .and_then(identity), + )); + } + if let Some(cmd) = self.pending_nonce.pop_front() { + self.requested_substream_nonce.push_back(cmd); + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(DIAL_BACK_UPGRADE, ()), + }); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + self.pending_nonce.push_back(event); + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => { + if let Some(cmd) = self.requested_substream_nonce.pop_front() { + if self + .outbound + .try_push(perform_dial_back(protocol, cmd)) + .is_err() + { + tracing::warn!("Dial back dropped, too many requests in flight"); + } + } else { + tracing::warn!("received dial back substream without nonce"); + } + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + tracing::debug!("Dial back failed: {:?}", error); + } + _ => {} + } + todo!() + } +} + +async fn perform_dial_back( + mut stream: impl AsyncWrite + Unpin, + DialBackCommand { + nonce, + back_channel, + .. + }: DialBackCommand, +) -> io::Result<()> { + let res = perform_dial_back_inner(&mut stream, nonce) + .await + .map_err(|_| DialBackRes::DialBack) + .map(|_| DialBackRes::Ok) + .unwrap_or_else(|e| e); + back_channel + .send(res) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; + Ok(()) +} + +async fn perform_dial_back_inner( + mut stream: impl AsyncWrite + Unpin, + nonce: Nonce, +) -> io::Result<()> { + let dial_back = DialBack { nonce }; + dial_back.write_into(&mut stream).await?; + stream.close().await?; + Ok(()) +} diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs new file mode 100644 index 00000000000..f80a1385a6a --- /dev/null +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -0,0 +1,264 @@ +use std::{ + convert::identity, + io, + task::{Context, Poll}, + time::Duration, +}; + +use futures::{ + channel::{mpsc, oneshot}, + AsyncRead, AsyncWrite, SinkExt, StreamExt, +}; +use futures_bounded::FuturesSet; +use libp2p_core::{ + upgrade::{DeniedUpgrade, ReadyUpgrade}, + Multiaddr, +}; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use rand_core::RngCore; + +use crate::{ + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + request_response::{ + DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + }, + Nonce, REQUEST_UPGRADE, +}; + +#[derive(Debug, PartialEq)] +pub(crate) enum DialBack { + Dial, + DialBack, + Ok, +} + +#[derive(Debug)] +pub struct DialBackCommand { + pub(crate) addr: Multiaddr, + pub(crate) nonce: Nonce, + pub(crate) back_channel: oneshot::Sender, +} + +pub struct Handler { + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + dial_back_cmd_receiver: mpsc::Receiver, + inbound: FuturesSet>, + rng: R, +} + +impl Handler +where + R: RngCore, +{ + pub(crate) fn new(observed_multiaddr: Multiaddr, rng: R) -> Self { + let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); + Self { + observed_multiaddr, + dial_back_cmd_sender, + dial_back_cmd_receiver, + inbound: FuturesSet::new(Duration::from_secs(10), 2), + rng, + } + } +} + +impl ConnectionHandler for Handler +where + R: RngCore + Send + Clone + 'static, +{ + type FromBehaviour = (); + + type ToBehaviour = io::Result; + + type InboundProtocol = ReadyUpgrade; + + type OutboundProtocol = DeniedUpgrade; + + type InboundOpenInfo = (); + + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(REQUEST_UPGRADE, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(Err(e))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err(e))); + } + Poll::Ready(Err(e)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err( + io::Error::new(io::ErrorKind::TimedOut, e), + ))); + } + Poll::Ready(Ok(Ok(_))) => {} + Poll::Pending => {} + } + if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + if self + .inbound + .try_push(handle_request( + protocol, + self.observed_multiaddr.clone(), + self.dial_back_cmd_sender.clone(), + self.rng.clone(), + )) + .is_err() + { + tracing::warn!( + "failed to push inbound request handler, too many requests in flight" + ); + } + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { + tracing::debug!("inbound request failed: {:?}", error); + } + _ => {} + } + } +} + +enum HandleFail { + InternalError(usize), + RequestRejected, + DialRefused, + DialBack { idx: usize, err: DialBack }, +} + +impl From for DialResponse { + fn from(value: HandleFail) -> Self { + match value { + HandleFail::InternalError(addr_idx) => Self { + status: ResponseStatus::E_INTERNAL_ERROR, + addr_idx, + dial_status: DialStatus::UNUSED, + }, + HandleFail::RequestRejected => Self { + status: ResponseStatus::E_REQUEST_REJECTED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + }, + HandleFail::DialRefused => Self { + status: ResponseStatus::E_DIAL_REFUSED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + }, + HandleFail::DialBack { idx, err } => Self { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: match err { + DialBack::Dial => DialStatus::E_DIAL_ERROR, + DialBack::DialBack => DialStatus::E_DIAL_BACK_ERROR, + DialBack::Ok => DialStatus::OK, + }, + }, + } + } +} + +async fn handle_request_internal( + mut stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + mut rng: impl RngCore, +) -> Result { + let DialRequest { addrs, nonce } = match Request::read_from(&mut stream) + .await + .map_err(|_| HandleFail::InternalError(0))? + { + Request::Dial(dial_request) => dial_request, + Request::Data(_) => { + return Err(HandleFail::RequestRejected); + } + }; + for (idx, addr) in addrs.into_iter().enumerate() { + if addr != observed_multiaddr { + let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); + let mut rem_data = dial_data_request.num_bytes; + Response::Data(dial_data_request) + .write_into(&mut stream) + .await + .map_err(|_| HandleFail::InternalError(idx))?; + while rem_data > 0 { + let DialDataResponse { data_count } = match Request::read_from(&mut stream) + .await + .map_err(|_| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response, + }; + rem_data = rem_data.saturating_sub(data_count); + } + } + let (back_channel, rx) = oneshot::channel(); + let dial_back_cmd = DialBackCommand { + addr, + nonce, + back_channel, + }; + dial_back_cmd_sender + .clone() + .send(dial_back_cmd) + .await + .map_err(|_| HandleFail::InternalError(idx))?; + let dial_back = rx.await.map_err(|_| HandleFail::InternalError(idx))?; + if dial_back != DialBack::Ok { + return Err(HandleFail::DialBack { + idx, + err: dial_back, + }); + } + return Ok(DialResponse { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: DialStatus::OK, + }); + } + Err(HandleFail::DialRefused) +} + +async fn handle_request( + mut stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + rng: impl RngCore, +) -> io::Result<()> { + let response = + handle_request_internal(&mut stream, observed_multiaddr, dial_back_cmd_sender, rng) + .await + .unwrap_or_else(|e| e.into()); + Response::Dial(response).write_into(&mut stream).await?; + Ok(()) +} From 50fb5e4ab8cf06335fdc19dd285622c9acd3f2e8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:29:55 +0100 Subject: [PATCH 041/179] Pass the basic test for the first time. --- Cargo.lock | 11 +- protocols/autonatv2/Cargo.toml | 8 +- protocols/autonatv2/src/client.rs | 2 + protocols/autonatv2/src/client/behaviour.rs | 67 +++++++++--- protocols/autonatv2/src/client/handler.rs | 8 +- .../autonatv2/src/client/handler/dial_back.rs | 2 +- .../src/client/handler/dial_request.rs | 21 ++-- protocols/autonatv2/src/lib.rs | 4 +- protocols/autonatv2/src/server.rs | 2 + protocols/autonatv2/src/server/behaviour.rs | 15 ++- .../autonatv2/src/server/handler/dial_back.rs | 3 +- .../src/server/handler/dial_request.rs | 18 ++- protocols/autonatv2/tests/autonatv2.rs | 103 ++++++++++++++++++ 13 files changed, 215 insertions(+), 49 deletions(-) create mode 100644 protocols/autonatv2/tests/autonatv2.rs diff --git a/Cargo.lock b/Cargo.lock index 66efb5211f2..4f5d739fca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2520,16 +2520,19 @@ dependencies = [ "futures", "futures-bounded", "libp2p-core", + "libp2p-identify", "libp2p-identity", "libp2p-swarm", + "libp2p-swarm-test", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", "rand_core 0.6.4", - "scc", "static_assertions", "thiserror", + "tokio", "tracing", + "tracing-subscriber", "void", ] @@ -5072,12 +5075,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scc" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca628bbcc4be16ffaeae429444cf4538d7a23b1aa5457cc9ce9a220286befbc3" - [[package]] name = "schannel" version = "0.1.22" diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 8729108ab08..7a38ec38313 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -21,11 +21,17 @@ void = "1.0.2" either = "1.9.0" futures = "0.3.29" thiserror = "1.0.50" -scc = "2.0.3" bytes = "1" static_assertions = "1.1.0" tracing = "0.1.40" +[dev-dependencies] +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +libp2p-swarm-test = { workspace = true } +libp2p-identify = { workspace = true } +libp2p-swarm = { workspace = true, features = ["macros"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"]} + [lints] workspace = true diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index dff2397252c..be1f6fd8df0 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,2 +1,4 @@ mod behaviour; mod handler; + +pub use behaviour::Behaviour; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index d82e4eac17b..9dae7b31ea6 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; use rand::{distributions::Standard, seq::SliceRandom, Rng}; -use rand_core::RngCore; +use rand_core::{OsRng, RngCore}; use crate::{global_only::IpExt, request_response::DialRequest}; @@ -36,12 +36,21 @@ impl IntervalTicker { } } -pub(crate) struct Config { +pub struct Config { pub(crate) test_server_count: usize, pub(crate) max_addrs_count: usize, } -pub(crate) struct Behaviour +impl Default for Config { + fn default() -> Self { + Self { + test_server_count: 3, + max_addrs_count: 10, + } + } +} + +pub struct Behaviour where R: RngCore + 'static, { @@ -79,7 +88,7 @@ where if addr_is_local(remote_addr) { self.local_peers.insert(connection_id); } - Ok(Either::Left(dial_request::Handler::new())) + Ok(Either::Right(dial_back::Handler::new())) } fn handle_established_outbound_connection( @@ -93,7 +102,7 @@ where if addr_is_local(addr) { self.local_peers.insert(connection_id); } - Ok(Either::Right(dial_back::Handler::new())) + Ok(Either::Left(dial_request::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -139,9 +148,11 @@ where connection_id: ConnectionId, event: ::ToBehaviour, ) { - self.peers_to_handlers - .entry(peer_id) - .or_insert(connection_id); + if matches!(event, Either::Left(_)) { + self.peers_to_handlers + .entry(peer_id) + .or_insert(connection_id); + } match event { Either::Right(Ok(nonce)) => { if self.pending_nonces.remove(&nonce) { @@ -184,12 +195,12 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } - Either::Left(dial_request::ToBehaviour::TestCompleted( - Err(dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }) - )) - | Either::Left(dial_request::ToBehaviour::TestCompleted( - Err(dial_request::Error::FailureDuringDialBack { addr: Some(addr) }) - )) => { + Either::Left(dial_request::ToBehaviour::TestCompleted(Err( + dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }, + ))) + | Either::Left(dial_request::ToBehaviour::TestCompleted(Err( + dial_request::Error::FailureDuringDialBack { addr: Some(addr) }, + ))) => { self.pending_events .push_back(ToSwarm::ExternalAddrExpired(addr)); } @@ -207,7 +218,7 @@ where if pending_event.is_ready() { return pending_event; } - if self.ticker.ready() && !self.known_servers.is_empty() { + if self.ticker.ready() && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { let mut entries = self.address_candidates.drain().collect::>(); entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries @@ -246,6 +257,23 @@ impl Behaviour where R: RngCore + 'static, { + pub fn new(rng: R, config: Config) -> Self { + Self { + local_peers: HashSet::new(), + pending_nonces: HashSet::new(), + known_servers: Vec::new(), + rng, + config, + pending_events: VecDeque::new(), + address_candidates: HashMap::new(), + peers_to_handlers: HashMap::new(), + ticker: IntervalTicker { + interval: Duration::from_secs(0), + last_tick: Instant::now(), + }, + } + } + fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest) { if let Some(conn_id) = self.peers_to_handlers.get(&peer) { self.pending_events.push_back(ToSwarm::NotifyHandler { @@ -253,6 +281,9 @@ where handler: NotifyHandler::One(*conn_id), event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), }); + if self.pending_events.is_empty() { + println!("is empty") + } } else { tracing::debug!( "There should be a connection to {:?}, but there isn't", @@ -281,6 +312,12 @@ where } } +impl Default for Behaviour { + fn default() -> Self { + Self::new(OsRng, Config::default()) + } +} + fn addr_is_local(addr: &Multiaddr) -> bool { addr.iter().any(|c| match c { Protocol::Dns(addr) diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 9e8389bb630..f156d7b30c7 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -6,15 +6,15 @@ // TODO: tests // TODO: Handlers -pub(super) mod dial_back; -pub(super) mod dial_request; +pub mod dial_back; +pub mod dial_request; use either::Either; use std::time::Duration; pub(crate) use dial_request::TestEnd; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10000); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub(crate) type Handler = Either; +pub type Handler = Either; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index f4505335f2c..e883a45715e 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -19,7 +19,7 @@ use super::DEFAULT_TIMEOUT; pub(crate) type ToBehaviour = io::Result; -pub(crate) struct Handler { +pub struct Handler { inbound: FuturesSet>, } diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index a26fe4e458d..9a0ceecec12 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,4 +1,4 @@ -use futures::{channel::oneshot, AsyncRead, AsyncWrite}; +use futures::{channel::oneshot, AsyncRead, AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -13,7 +13,6 @@ use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, SubstreamProtocol, }; -use scc::hash_cache::DEFAULT_MAXIMUM_CAPACITY; use std::{ collections::VecDeque, convert::identity, @@ -64,24 +63,24 @@ pub(crate) enum Error { } #[derive(Debug)] -pub(crate) struct TestEnd { +pub struct TestEnd { pub(crate) dial_request: DialRequest, pub(crate) suspicious_addr: Vec, pub(crate) reachable_addr: Multiaddr, } #[derive(Debug)] -pub(crate) enum ToBehaviour { +pub enum ToBehaviour { TestCompleted(Result), PeerHasServerSupport, } #[derive(Debug)] -pub(crate) enum FromBehaviour { +pub enum FromBehaviour { PerformRequest(DialRequest), } -pub(crate) struct Handler { +pub struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< ::OutboundProtocol, @@ -104,7 +103,7 @@ impl Handler { pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(DEFAULT_TIMEOUT, DEFAULT_MAXIMUM_CAPACITY), + outbound: FuturesSet::new(DEFAULT_TIMEOUT, 10), queued_streams: VecDeque::default(), } } @@ -207,9 +206,10 @@ impl ConnectionHandler for Handler { }, ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { - self.queued_events.push_back( - ConnectionHandlerEvent::NotifyBehaviour(ToBehaviour::PeerHasServerSupport) - ); + self.queued_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::PeerHasServerSupport, + )); } } _ => {} @@ -281,6 +281,7 @@ async fn handle_substream( send_aap_data(&mut substream, num_bytes).await?; } Response::Dial(dial_response) => { + substream.close().await?; return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); } } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index 5e357b5cf0e..e64b9d480d4 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -1,8 +1,8 @@ use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; -mod client; -mod server; +pub mod client; +pub mod server; mod generated; mod global_only; pub(crate) mod request_response; diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs index dff2397252c..be1f6fd8df0 100644 --- a/protocols/autonatv2/src/server.rs +++ b/protocols/autonatv2/src/server.rs @@ -1,2 +1,4 @@ mod behaviour; mod handler; + +pub use behaviour::Behaviour; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index a96d412a0b7..0771e83c13f 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -11,7 +11,8 @@ use libp2p_swarm::{ ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, }; -use rand_core::RngCore; +use rand_core::{OsRng, RngCore}; +use libp2p_core::multiaddr::Protocol; use super::handler::{ dial_back, @@ -19,7 +20,7 @@ use super::handler::{ Handler, }; -pub struct Behaviour +pub struct Behaviour where R: Clone + Send + RngCore + 'static, { @@ -35,11 +36,17 @@ where rng: R, } +impl Default for Behaviour { + fn default() -> Self { + Self::new(OsRng) + } +} + impl Behaviour where R: RngCore + Send + Clone + 'static, { - pub(crate) fn new(rng: R) -> Self { + pub fn new(rng: R) -> Self { Self { handlers: HashMap::new(), pending_dial_back: HashMap::new(), @@ -95,7 +102,7 @@ where port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { if port_use == PortUse::New { - self.handlers.insert((addr.clone(), peer), connection_id); + self.handlers.insert((addr.iter().filter(|e| !matches!(e, Protocol::P2p(_))).collect(), peer), connection_id); } Ok(Either::Left(dial_back::Handler::new())) } diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 4f8d7915063..45a3fba8480 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -32,7 +32,7 @@ impl Handler { Self { pending_nonce: VecDeque::new(), requested_substream_nonce: VecDeque::new(), - outbound: FuturesSet::new(Duration::from_secs(10), 2), + outbound: FuturesSet::new(Duration::from_secs(10000), 2), } } } @@ -105,7 +105,6 @@ impl ConnectionHandler for Handler { } _ => {} } - todo!() } } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index f80a1385a6a..554675f5b20 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -7,8 +7,9 @@ use std::{ use futures::{ channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, SinkExt, StreamExt, + AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, }; +use futures::future::FusedFuture; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -60,7 +61,7 @@ where observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, - inbound: FuturesSet::new(Duration::from_secs(10), 2), + inbound: FuturesSet::new(Duration::from_secs(1000), 2), rng, } } @@ -105,6 +106,7 @@ where Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); } Poll::Pending @@ -146,6 +148,10 @@ where _ => {} } } + + fn connection_keep_alive(&self) -> bool { + true + } } enum HandleFail { @@ -233,7 +239,12 @@ async fn handle_request_internal( .send(dial_back_cmd) .await .map_err(|_| HandleFail::InternalError(idx))?; - let dial_back = rx.await.map_err(|_| HandleFail::InternalError(idx))?; + if rx.is_terminated() { + println!("is terminated"); + } + let dial_back = rx.await.map_err(|e| { + HandleFail::InternalError(idx) + })?; if dial_back != DialBack::Ok { return Err(HandleFail::DialBack { idx, @@ -260,5 +271,6 @@ async fn handle_request( .await .unwrap_or_else(|e| e.into()); Response::Dial(response).write_into(&mut stream).await?; + stream.close().await?; Ok(()) } diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs new file mode 100644 index 00000000000..c3acdd3c462 --- /dev/null +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -0,0 +1,103 @@ +use std::task::Poll; + +use futures::StreamExt; +use libp2p_swarm::{Swarm, SwarmEvent, ToSwarm}; +use libp2p_swarm_test::SwarmExt; +use tracing_subscriber::EnvFilter; + +#[tokio::test] +async fn foo() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let mut alice = new_server().await; + let cor_server_peer = alice.local_peer_id().clone(); + let mut bob = new_client().await; + let cor_client_peer = bob.local_peer_id().clone(); + bob.connect(&mut alice).await; + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + peer_id: client_peer_sent, + }), CombinedServerEvent::Identify(libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + })], + [CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + peer_id: server_peer_sent, + }), CombinedClientEvent::Identify(libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + })], + ) => { + assert_eq!(server_peer_sent, cor_server_peer); + assert_eq!(client_peer_sent, cor_client_peer); + assert_eq!(server_peer_recv, cor_server_peer); + assert_eq!(client_peer_recv, cor_client_peer); + } + e => panic!("unexpected event: {:?}", e), + } + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [ + SwarmEvent::Dialing { .. }, + SwarmEvent::ConnectionEstablished { .. }, + SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), + SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), + SwarmEvent::NewExternalAddrCandidate { ..}, + ],[ + SwarmEvent::NewExternalAddrCandidate { address: addr_new }, + SwarmEvent::IncomingConnection { .. }, + SwarmEvent::ConnectionEstablished { .. }, + SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), + SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), + SwarmEvent::NewExternalAddrCandidate { address: addr_snd}, + SwarmEvent::ExternalAddrConfirmed { address: addr_ok} + ] + ) => { + assert_eq!(addr_new, addr_snd); + assert_eq!(addr_snd, addr_ok); + } + _ => todo!() + } + +} + +async fn new_server() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| CombinedServer { + autonat: libp2p_autonatv2::server::Behaviour::default(), + identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )), + }); + node.listen().with_tcp_addr_external().await; + + node +} + +async fn new_client() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| CombinedClient { + autonat: libp2p_autonatv2::client::Behaviour::default(), + identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )), + }); + node.listen().with_tcp_addr_external().await; + node +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct CombinedServer { + autonat: libp2p_autonatv2::server::Behaviour, + identify: libp2p_identify::Behaviour, +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct CombinedClient { + autonat: libp2p_autonatv2::client::Behaviour, + identify: libp2p_identify::Behaviour, +} From d34782e91999993759f640a8e6f2e4406649bb35 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 6 Dec 2023 21:22:48 +0100 Subject: [PATCH 042/179] Move forwards to fewer bug fixes --- Cargo.lock | 3 + protocols/autonatv2/Cargo.toml | 1 + protocols/autonatv2/src/client/behaviour.rs | 20 ++- protocols/autonatv2/src/client/handler.rs | 8 +- .../autonatv2/src/client/handler/dial_back.rs | 11 +- .../src/client/handler/dial_request.rs | 30 ++-- protocols/autonatv2/src/lib.rs | 2 +- protocols/autonatv2/src/request_response.rs | 140 ++++++++++++----- protocols/autonatv2/src/server/behaviour.rs | 60 ++++++-- protocols/autonatv2/src/server/handler.rs | 2 +- .../autonatv2/src/server/handler/dial_back.rs | 17 ++- .../src/server/handler/dial_request.rs | 61 ++++---- protocols/autonatv2/tests/autonatv2.rs | 141 ++++++++++++++---- 13 files changed, 356 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f5d739fca6..b67b765eff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2533,6 +2533,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "unsigned-varint 0.8.0", "void", ] @@ -6119,6 +6120,8 @@ checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" dependencies = [ "asynchronous-codec", "bytes", + "futures-io", + "futures-util", ] [[package]] diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 7a38ec38313..3f5ccf0b68e 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -24,6 +24,7 @@ thiserror = "1.0.50" bytes = "1" static_assertions = "1.1.0" tracing = "0.1.40" +unsigned-varint = { workspace = true, features = ["futures"] } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 9dae7b31ea6..4557d4da548 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -9,11 +9,10 @@ use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, - dial_opts::{DialOpts, PeerCondition}, ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; -use rand::{distributions::Standard, seq::SliceRandom, Rng}; +use rand::{seq::SliceRandom, Rng}; use rand_core::{OsRng, RngCore}; use crate::{global_only::IpExt, request_response::DialRequest}; @@ -66,6 +65,7 @@ where >, >, address_candidates: HashMap, + already_tested: HashSet, peers_to_handlers: HashMap, ticker: IntervalTicker, } @@ -108,7 +108,10 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - *self.address_candidates.entry(addr.clone()).or_default() += 1; + if !self.already_tested.contains(addr) { + println!("external addr: {addr}"); + *self.address_candidates.entry(addr.clone()).or_default() += 1; + } } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { self.address_candidates.remove(addr); @@ -218,7 +221,10 @@ where if pending_event.is_ready() { return pending_event; } - if self.ticker.ready() && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { + if self.ticker.ready() + && !self.known_servers.is_empty() + && !self.address_candidates.is_empty() + { let mut entries = self.address_candidates.drain().collect::>(); entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries @@ -227,6 +233,7 @@ where .map(|(addr, _)| addr) .take(self.config.max_addrs_count) .collect::>(); + self.already_tested.extend(addrs.iter().cloned()); let peers = if self.known_servers.len() < self.config.test_server_count { self.known_servers.clone() } else { @@ -267,6 +274,7 @@ where pending_events: VecDeque::new(), address_candidates: HashMap::new(), peers_to_handlers: HashMap::new(), + already_tested: HashSet::new(), ticker: IntervalTicker { interval: Duration::from_secs(0), last_tick: Instant::now(), @@ -310,6 +318,10 @@ where } Poll::Pending } + + pub fn inject_test_addr(&mut self, addr: Multiaddr) { + *self.address_candidates.entry(addr).or_default() += 1; + } } impl Default for Behaviour { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index f156d7b30c7..e4a48992727 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -6,15 +6,15 @@ // TODO: tests // TODO: Handlers -pub mod dial_back; -pub mod dial_request; +pub(crate) mod dial_back; +pub(crate) mod dial_request; use either::Either; use std::time::Duration; pub(crate) use dial_request::TestEnd; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10000); +const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub type Handler = Either; +pub(crate) type Handler = Either; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index e883a45715e..906ba6a35e8 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -4,9 +4,8 @@ use std::{ task::{Context, Poll}, }; -use either::Either; use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; -use futures_bounded::{FuturesSet, Timeout}; +use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, @@ -15,7 +14,7 @@ use libp2p_swarm::{ use crate::{request_response::DialBack, Nonce}; -use super::DEFAULT_TIMEOUT; +use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; pub(crate) type ToBehaviour = io::Result; @@ -26,7 +25,7 @@ pub struct Handler { impl Handler { pub(crate) fn new() -> Self { Self { - inbound: FuturesSet::new(DEFAULT_TIMEOUT, 2), + inbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), } } } @@ -84,6 +83,10 @@ impl ConnectionHandler for Handler { _ => {} } } + + fn connection_keep_alive(&self) -> bool { + false + } } async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index 9a0ceecec12..2545f92f063 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -21,6 +21,7 @@ use std::{ task::{Context, Poll}, }; +use crate::request_response::Coder; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ @@ -30,10 +31,10 @@ use crate::{ REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, }; -use super::DEFAULT_TIMEOUT; +use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; #[derive(Debug, thiserror::Error)] -pub(crate) enum Error { +pub enum Error { #[error("io error")] Io(#[from] io::Error), #[error("invalid referenced address index: {index} (max number of addr: {max})")] @@ -103,12 +104,13 @@ impl Handler { pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(DEFAULT_TIMEOUT, 10), + outbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), queued_streams: VecDeque::default(), } } fn perform_request(&mut self, req: DialRequest) { + println!("{req:?}"); let (tx, rx) = oneshot::channel(); self.queued_streams.push_back(tx); self.queued_events @@ -235,14 +237,15 @@ async fn start_substream_handle( async fn handle_substream( dial_request: DialRequest, - mut substream: impl AsyncRead + AsyncWrite + Unpin, + substream: impl AsyncRead + AsyncWrite + Unpin, ) -> Result { - Request::Dial(dial_request.clone()) - .write_into(&mut substream) + let mut coder = Coder::new(substream); + coder + .send_request(Request::Dial(dial_request.clone())) .await?; let mut suspicious_addr = Vec::new(); loop { - match Response::read_from(&mut substream).await? { + match coder.next_response().await? { Response::Data(DialDataRequest { addr_idx, num_bytes, @@ -278,10 +281,11 @@ async fn handle_substream( } } - send_aap_data(&mut substream, num_bytes).await?; + println!("Time to bpay the tribute"); + send_aap_data(&mut coder, num_bytes).await?; } Response::Dial(dial_response) => { - substream.close().await?; + coder.close().await?; return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); } } @@ -328,7 +332,10 @@ fn test_end_from_dial_response( } } -async fn send_aap_data(mut substream: impl AsyncWrite + Unpin, num_bytes: usize) -> io::Result<()> { +async fn send_aap_data(substream: &mut Coder, num_bytes: usize) -> io::Result<()> +where + I: AsyncWrite + Unpin, +{ let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) @@ -337,7 +344,8 @@ async fn send_aap_data(mut substream: impl AsyncWrite + Unpin, num_bytes: usize) .filter(|e| *e > 0) .map(|data_count| Request::Data(DialDataResponse { data_count })) { - req.write_into(&mut substream).await?; + println!("Data req: {req:?}"); + substream.send_request(req).await?; } Ok(()) } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index e64b9d480d4..2b4db326485 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -2,10 +2,10 @@ use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; pub mod client; -pub mod server; mod generated; mod global_only; pub(crate) mod request_response; +pub mod server; pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index b87852c7302..5716863dee6 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -1,10 +1,11 @@ // change to quick-protobuf-codec +use std::io::ErrorKind; use std::{borrow::Cow, io}; -use asynchronous_codec::{FramedRead, FramedWrite}; +use asynchronous_codec::{Framed, FramedRead, FramedWrite}; -use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; @@ -29,30 +30,82 @@ macro_rules! check_existence { }; } -#[derive(Debug, Clone)] +pub(crate) struct Coder { + inner: Framed>, +} + +impl Coder +where + I: AsyncWrite + AsyncRead + Unpin, +{ + pub(crate) fn new(io: I) -> Self { + Self { + inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), + } + } + pub(crate) async fn close(self) -> io::Result<()> { + let mut stream = self.inner.into_inner(); + stream.close().await?; + Ok(()) + } +} + +impl Coder +where + I: AsyncRead + Unpin, +{ + async fn next_msg(&mut self) -> io::Result { + self.inner + .next() + .await + .ok_or(io::Error::new(ErrorKind::Other, "no request to read"))? + .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) + } + pub(crate) async fn next_request(&mut self) -> io::Result { + Request::from_proto(self.next_msg().await?) + } + pub(crate) async fn next_response(&mut self) -> io::Result { + Response::from_proto(self.next_msg().await?) + } +} + +impl Coder +where + I: AsyncWrite + Unpin, +{ + async fn send_msg(&mut self, msg: proto::Message) -> io::Result<()> { + self.inner.send(msg).await?; + Ok(()) + } + + pub(crate) async fn send_request(&mut self, request: Request) -> io::Result<()> { + self.send_msg(request.into_proto()).await + } + + pub(crate) async fn send_response(&mut self, response: Response) -> io::Result<()> { + self.send_msg(response.into_proto()).await + } +} + +#[derive(Debug, Clone, PartialEq)] pub(crate) enum Request { Dial(DialRequest), Data(DialDataResponse), } -#[derive(Debug, Clone)] -pub(crate) struct DialRequest { +#[derive(Debug, Clone, PartialEq)] +pub struct DialRequest { pub(crate) addrs: Vec, pub(crate) nonce: u64, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct DialDataResponse { pub(crate) data_count: usize, } impl Request { - pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { - let mut framed_io = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)); - let msg = framed_io - .next() - .await - .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; + pub(crate) fn from_proto(msg: proto::Message) -> io::Result { match msg.msg { proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { let addrs: Vec = addrs @@ -77,8 +130,8 @@ impl Request { } } - pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = match self { + pub(crate) fn into_proto(self) -> proto::Message { + match self { Request::Dial(DialRequest { addrs, nonce }) => { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); let nonce = Some(nonce); @@ -101,11 +154,7 @@ impl Request { }), } } - }; - FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + } } } @@ -129,12 +178,7 @@ pub(crate) struct DialResponse { } impl Response { - pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> std::io::Result { - let msg = FramedRead::new(io, Codec::::new(REQUEST_MAX_SIZE)) - .next() - .await - .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; - + pub(crate) fn from_proto(msg: proto::Message) -> std::io::Result { match msg.msg { proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { status, @@ -167,8 +211,8 @@ impl Response { } } - pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = match self { + pub(crate) fn into_proto(self) -> proto::Message { + match self { Self::Dial(DialResponse { status, addr_idx, @@ -189,11 +233,7 @@ impl Response { numBytes: Some(num_bytes as u64), }), }, - }; - FramedWrite::new(io, Codec::::new(REQUEST_MAX_SIZE)) - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + } } } @@ -205,11 +245,6 @@ impl DialDataRequest { num_bytes, } } - - #[cfg(any(doc, feature = "rand"))] - pub(crate) fn new(addr_idx: usize) -> Self { - Self::from_rng(addr_idx, rand::thread_rng()) - } } const DIAL_BACK_MAX_SIZE: usize = 10; @@ -242,12 +277,18 @@ impl DialBack { #[cfg(test)] mod tests { - use crate::generated::structs::{mod_Message::OneOfmsg, DialDataResponse, Message}; + use crate::generated::structs::{ + mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, + }; + use crate::request_response::{Coder, DialDataResponse, Request}; + use futures::io::Cursor; + + use rand::{thread_rng, Rng}; #[test] fn message_correct_max_size() { let message_bytes = quick_protobuf::serialize_into_vec(&Message { - msg: OneOfmsg::dialDataResponse(DialDataResponse { + msg: OneOfmsg::dialDataResponse(GenDialDataResponse { data: Some(vec![0; 4096].into()), }), }) @@ -271,4 +312,25 @@ mod tests { let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); } + + #[tokio::test] + async fn write_read_request() { + let mut buf = Cursor::new(Vec::new()); + let mut coder = Coder::new(&mut buf); + let mut all_req = Vec::with_capacity(100); + for _ in 0..100 { + let data_request: Request = Request::Data(DialDataResponse { + data_count: thread_rng().gen_range(0..4000), + }); + all_req.push(data_request.clone()); + coder.send_request(data_request.clone()).await.unwrap(); + } + let inner = coder.inner.into_inner(); + inner.set_position(0); + let mut coder = Coder::new(inner); + for i in 0..100 { + let read_data_request = coder.next_request().await.unwrap(); + assert_eq!(read_data_request, all_req[i]); + } + } } diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 0771e83c13f..e07f3cd29d3 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -3,16 +3,16 @@ use std::{ task::{Context, Poll}, }; +use crate::server::handler::dial_request::DialBack; use either::Either; +use libp2p_core::multiaddr::Protocol; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - dial_opts::{DialOpts, PeerCondition}, - ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NotifyHandler, - ToSwarm, + dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialError, DialFailure, + FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, }; use rand_core::{OsRng, RngCore}; -use libp2p_core::multiaddr::Protocol; use super::handler::{ dial_back, @@ -25,7 +25,6 @@ where R: Clone + Send + RngCore + 'static, { handlers: HashMap<(Multiaddr, PeerId), ConnectionId>, - pending_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, dialing_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, pending_events: VecDeque< ToSwarm< @@ -49,7 +48,6 @@ where pub fn new(rng: R) -> Self { Self { handlers: HashMap::new(), - pending_dial_back: HashMap::new(), dialing_dial_back: HashMap::new(), pending_events: VecDeque::new(), rng, @@ -58,7 +56,7 @@ where fn poll_pending_events( &mut self, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll< ToSwarm< ::ToSwarm, @@ -98,16 +96,47 @@ where connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, - role_override: Endpoint, + _role_override: Endpoint, port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { if port_use == PortUse::New { - self.handlers.insert((addr.iter().filter(|e| !matches!(e, Protocol::P2p(_))).collect(), peer), connection_id); + self.handlers.insert( + ( + addr.iter() + .filter(|e| !matches!(e, Protocol::P2p(_))) + .collect(), + peer, + ), + connection_id, + ); + println!("Found handler"); } Ok(Either::Left(dial_back::Handler::new())) } - fn on_swarm_event(&mut self, event: FromSwarm) {} + fn on_swarm_event(&mut self, event: FromSwarm) { + println!("EVENT: {event:?}"); + match event { + FromSwarm::DialFailure(DialFailure { + error: DialError::Transport(pairs), + .. + }) => { + println!("Failed to dial"); + for (addr, _) in pairs.iter() { + if let Some((_, p)) = self.dialing_dial_back.keys().find(|(a, _)| a == addr) { + let cmds = self.dialing_dial_back.remove(&(addr.clone(), *p)).unwrap(); + for cmd in cmds { + let _ = cmd.back_channel.send(DialBack::Dial); + } + } + } + } + FromSwarm::DialFailure(m) => { + println!("{m:?}"); + } + _ => {} + } + } fn on_connection_handler_event( &mut self, @@ -132,9 +161,14 @@ where pending.push_back(cmd); } else { self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer_id.clone()) - .condition(PeerCondition::Always) - .addresses(vec![addr.clone()]) + /* opts: DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr.clone()])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(), + */ + opts: DialOpts::unknown_peer_id() + .address(addr.clone()) .allocate_new_port() .build(), }); diff --git a/protocols/autonatv2/src/server/handler.rs b/protocols/autonatv2/src/server/handler.rs index 7beb8f00ec3..764e1a71f1f 100644 --- a/protocols/autonatv2/src/server/handler.rs +++ b/protocols/autonatv2/src/server/handler.rs @@ -3,4 +3,4 @@ use either::Either; pub(crate) mod dial_back; pub(crate) mod dial_request; -pub type Handler = Either>; +pub(crate) type Handler = Either>; diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 45a3fba8480..8a1d06a74a2 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -10,16 +10,16 @@ use futures::{AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; +use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME, DIAL_BACK_UPGRADE}; use super::dial_request::{DialBack as DialBackRes, DialBackCommand}; -pub type ToBehaviour = io::Result<()>; -pub type FromBehaviour = DialBackCommand; +pub(crate) type ToBehaviour = io::Result<()>; +pub(crate) type FromBehaviour = DialBackCommand; pub struct Handler { pending_nonce: VecDeque, @@ -103,7 +103,14 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { tracing::debug!("Dial back failed: {:?}", error); } - _ => {} + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut protocols)) => { + if !protocols.any(|p| p == &DIAL_BACK_PROTOCOL_NAME) { + todo!("handle when dialed back but not supporting protocol"); + } + } + e => { + println!("e: {:?}", e); + } } } } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 554675f5b20..4405e8ce7c0 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -1,15 +1,14 @@ use std::{ - convert::identity, io, task::{Context, Poll}, time::Duration, }; +use futures::future::FusedFuture; use futures::{ channel::{mpsc, oneshot}, AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, }; -use futures::future::FusedFuture; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -21,6 +20,7 @@ use libp2p_swarm::{ }; use rand_core::RngCore; +use crate::request_response::Coder; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ @@ -31,6 +31,7 @@ use crate::{ #[derive(Debug, PartialEq)] pub(crate) enum DialBack { + #[allow(unused)] Dial, DialBack, Ok, @@ -61,7 +62,7 @@ where observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, - inbound: FuturesSet::new(Duration::from_secs(1000), 2), + inbound: FuturesSet::new(Duration::from_secs(10), 10), rng, } } @@ -106,7 +107,6 @@ where Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); } Poll::Pending @@ -192,13 +192,17 @@ impl From for DialResponse { } } -async fn handle_request_internal( - mut stream: impl AsyncRead + AsyncWrite + Unpin, +async fn handle_request_internal( + coder: &mut Coder, observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, mut rng: impl RngCore, -) -> Result { - let DialRequest { addrs, nonce } = match Request::read_from(&mut stream) +) -> Result +where + I: AsyncRead + AsyncWrite + Unpin, +{ + let DialRequest { addrs, nonce } = match coder + .next_request() .await .map_err(|_| HandleFail::InternalError(0))? { @@ -207,24 +211,29 @@ async fn handle_request_internal( return Err(HandleFail::RequestRejected); } }; + println!("incoming addr: {addrs:?}"); for (idx, addr) in addrs.into_iter().enumerate() { if addr != observed_multiaddr { let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); let mut rem_data = dial_data_request.num_bytes; - Response::Data(dial_data_request) - .write_into(&mut stream) + coder + .send_response(Response::Data(dial_data_request)) .await .map_err(|_| HandleFail::InternalError(idx))?; while rem_data > 0 { - let DialDataResponse { data_count } = match Request::read_from(&mut stream) - .await - .map_err(|_| HandleFail::InternalError(idx))? - { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => dial_data_response, - }; + let DialDataResponse { data_count } = + match coder.next_request().await.map_err(|e| { + println!("err: {e:?}"); + HandleFail::InternalError(idx) + })? { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => { + println!("Dial data response: {dial_data_response:?}"); + dial_data_response + } + }; rem_data = rem_data.saturating_sub(data_count); } } @@ -242,9 +251,7 @@ async fn handle_request_internal( if rx.is_terminated() { println!("is terminated"); } - let dial_back = rx.await.map_err(|e| { - HandleFail::InternalError(idx) - })?; + let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; if dial_back != DialBack::Ok { return Err(HandleFail::DialBack { idx, @@ -261,16 +268,18 @@ async fn handle_request_internal( } async fn handle_request( - mut stream: impl AsyncRead + AsyncWrite + Unpin, + stream: impl AsyncRead + AsyncWrite + Unpin, observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, rng: impl RngCore, ) -> io::Result<()> { + let mut coder = Coder::new(stream); let response = - handle_request_internal(&mut stream, observed_multiaddr, dial_back_cmd_sender, rng) + handle_request_internal(&mut coder, observed_multiaddr, dial_back_cmd_sender, rng) .await .unwrap_or_else(|e| e.into()); - Response::Dial(response).write_into(&mut stream).await?; - stream.close().await?; + println!("Response: {response:?}"); + coder.send_response(Response::Dial(response)).await?; + coder.close().await?; Ok(()) } diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index c3acdd3c462..dbc5e2d1819 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -1,12 +1,59 @@ -use std::task::Poll; - -use futures::StreamExt; -use libp2p_swarm::{Swarm, SwarmEvent, ToSwarm}; +use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt; use tracing_subscriber::EnvFilter; #[tokio::test] -async fn foo() { +async fn confirm_successful() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let mut alice = new_server().await; + let cor_server_peer = alice.local_peer_id().clone(); + let mut bob = new_client().await; + let cor_client_peer = bob.local_peer_id().clone(); + bob.connect(&mut alice).await; + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + peer_id: client_peer_sent, + })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( + libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + }, + )), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + peer_id: server_peer_sent, + })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( + libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + }, + ))], + ) => { + assert_eq!(server_peer_sent, cor_server_peer); + assert_eq!(client_peer_sent, cor_client_peer); + assert_eq!(server_peer_recv, cor_server_peer); + assert_eq!(client_peer_recv, cor_client_peer); + } + e => panic!("unexpected events: {e:#?}"), + } + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], + ) => { + assert_eq!(addr_new, addr_snd); + assert_eq!(addr_snd, addr_ok); + } + e => panic!("unknown events {e:#?}"), + } +} + +#[tokio::test] +async fn dial_back_to_not_supporting() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); @@ -15,52 +62,71 @@ async fn foo() { let mut bob = new_client().await; let cor_client_peer = bob.local_peer_id().clone(); bob.connect(&mut alice).await; + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( - [CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { peer_id: client_peer_sent, - }), CombinedServerEvent::Identify(libp2p_identify::Event::Received { - peer_id: client_peer_recv, - .. - })], - [CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( + libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + }, + )), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { peer_id: server_peer_sent, - }), CombinedClientEvent::Identify(libp2p_identify::Event::Received { - peer_id: server_peer_recv, - .. - })], + })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( + libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + }, + ))], ) => { assert_eq!(server_peer_sent, cor_server_peer); assert_eq!(client_peer_sent, cor_client_peer); assert_eq!(server_peer_recv, cor_server_peer); assert_eq!(client_peer_recv, cor_client_peer); } - e => panic!("unexpected event: {:?}", e), + e => panic!("unexpected events: {e:#?}"), } + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( - [ - SwarmEvent::Dialing { .. }, - SwarmEvent::ConnectionEstablished { .. }, - SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), - SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), - SwarmEvent::NewExternalAddrCandidate { ..}, - ],[ - SwarmEvent::NewExternalAddrCandidate { address: addr_new }, - SwarmEvent::IncomingConnection { .. }, - SwarmEvent::ConnectionEstablished { .. }, - SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), - SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), - SwarmEvent::NewExternalAddrCandidate { address: addr_snd}, - SwarmEvent::ExternalAddrConfirmed { address: addr_ok} - ] + [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], ) => { assert_eq!(addr_new, addr_snd); assert_eq!(addr_snd, addr_ok); } - _ => todo!() + e => panic!("unknown events {e:#?}"), } + println!("Standard succeed"); + + let mut hannes = new_dummy().await; + let unreachable_address = hannes.external_addresses().next().unwrap().clone(); + bob.behaviour_mut() + .autonat + .inject_test_addr(unreachable_address.clone()); + + tokio::spawn(async move { + loop { + let hannes_event = hannes.next_swarm_event().await; + println!("Hannes: {hannes_event:?}"); + } + }); + + tokio::spawn(async move { + loop { + let bob_event = bob.next_swarm_event().await; + println!("Bob: {bob_event:?}"); + } + }); + + loop { + let alice_event = alice.next_swarm_event().await; + println!("Alice: {alice_event:?}"); + } } async fn new_server() -> Swarm { @@ -101,3 +167,14 @@ struct CombinedClient { autonat: libp2p_autonatv2::client::Behaviour, identify: libp2p_identify::Behaviour, } + +async fn new_dummy() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| { + libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )) + }); + node.listen().with_tcp_addr_external().await; + node +} From fee9de17017d40f0b34c00d3de873e8e032e9145 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:01:52 +0100 Subject: [PATCH 043/179] Make the things actually expire --- protocols/autonatv2/src/client/behaviour.rs | 4 -- .../src/client/handler/dial_request.rs | 3 -- protocols/autonatv2/src/server/behaviour.rs | 39 ++++++++++++------- .../autonatv2/src/server/handler/dial_back.rs | 23 ++++++----- .../src/server/handler/dial_request.rs | 28 +++++-------- protocols/autonatv2/tests/autonatv2.rs | 24 +++++------- 6 files changed, 58 insertions(+), 63 deletions(-) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 4557d4da548..70ecb4b3284 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -109,7 +109,6 @@ where match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { if !self.already_tested.contains(addr) { - println!("external addr: {addr}"); *self.address_candidates.entry(addr.clone()).or_default() += 1; } } @@ -289,9 +288,6 @@ where handler: NotifyHandler::One(*conn_id), event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), }); - if self.pending_events.is_empty() { - println!("is empty") - } } else { tracing::debug!( "There should be a connection to {:?}, but there isn't", diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index 2545f92f063..a4700f679d7 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -110,7 +110,6 @@ impl Handler { } fn perform_request(&mut self, req: DialRequest) { - println!("{req:?}"); let (tx, rx) = oneshot::channel(); self.queued_streams.push_back(tx); self.queued_events @@ -281,7 +280,6 @@ async fn handle_substream( } } - println!("Time to bpay the tribute"); send_aap_data(&mut coder, num_bytes).await?; } Response::Dial(dial_response) => { @@ -344,7 +342,6 @@ where .filter(|e| *e > 0) .map(|data_count| Request::Data(DialDataResponse { data_count })) { - println!("Data req: {req:?}"); substream.send_request(req).await?; } Ok(()) diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index e07f3cd29d3..3f20fbf1bfb 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::{ collections::{HashMap, VecDeque}, task::{Context, Poll}, @@ -8,6 +9,7 @@ use either::Either; use libp2p_core::multiaddr::Protocol; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialError, DialFailure, FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, @@ -109,19 +111,33 @@ where ), connection_id, ); - println!("Found handler"); } Ok(Either::Left(dial_back::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { - println!("EVENT: {event:?}"); match event { + FromSwarm::DialFailure(DialFailure { + error: DialError::WrongPeerId { .. }, + peer_id: Some(peer_id), + .. + }) => { + if let Some(key) = self + .dialing_dial_back + .keys() + .find(|(_, p)| *p == peer_id) + .cloned() + { + let cmds = self.dialing_dial_back.remove(&key).unwrap(); + for cmd in cmds { + let _ = cmd.back_channel.send(DialBack::Dial); + } + } + } FromSwarm::DialFailure(DialFailure { error: DialError::Transport(pairs), .. }) => { - println!("Failed to dial"); for (addr, _) in pairs.iter() { if let Some((_, p)) = self.dialing_dial_back.keys().find(|(a, _)| a == addr) { let cmds = self.dialing_dial_back.remove(&(addr.clone(), *p)).unwrap(); @@ -131,9 +147,6 @@ where } } } - FromSwarm::DialFailure(m) => { - println!("{m:?}"); - } _ => {} } } @@ -161,16 +174,16 @@ where pending.push_back(cmd); } else { self.pending_events.push_back(ToSwarm::Dial { - /* opts: DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr.clone()])) - .condition(PeerCondition::Always) + opts: DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr.clone()])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(), + /* opts: DialOpts::unknown_peer_id() + .address(addr.clone()) .allocate_new_port() .build(), */ - opts: DialOpts::unknown_peer_id() - .address(addr.clone()) - .allocate_new_port() - .build(), }); self.dialing_dial_back .insert((addr, peer_id), VecDeque::from([cmd])); diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 8a1d06a74a2..c282ff522dc 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -11,7 +11,8 @@ use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, + SubstreamProtocol, }; use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME, DIAL_BACK_UPGRADE}; @@ -100,17 +101,19 @@ impl ConnectionHandler for Handler { tracing::warn!("received dial back substream without nonce"); } } - ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { - tracing::debug!("Dial back failed: {:?}", error); - } - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut protocols)) => { - if !protocols.any(|p| p == &DIAL_BACK_PROTOCOL_NAME) { - todo!("handle when dialed back but not supporting protocol"); + ConnectionEvent::DialUpgradeError(DialUpgradeError { + error: StreamUpgradeError::NegotiationFailed, + .. + }) + | ConnectionEvent::DialUpgradeError(DialUpgradeError { + error: StreamUpgradeError::Timeout, + .. + }) => { + if let Some(cmd) = self.requested_substream_nonce.pop_front() { + let _ = cmd.back_channel.send(DialBackRes::DialBack); } } - e => { - println!("e: {:?}", e); - } + _ => {} } } } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 4405e8ce7c0..6a7cf89de0c 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -211,7 +211,6 @@ where return Err(HandleFail::RequestRejected); } }; - println!("incoming addr: {addrs:?}"); for (idx, addr) in addrs.into_iter().enumerate() { if addr != observed_multiaddr { let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); @@ -221,19 +220,16 @@ where .await .map_err(|_| HandleFail::InternalError(idx))?; while rem_data > 0 { - let DialDataResponse { data_count } = - match coder.next_request().await.map_err(|e| { - println!("err: {e:?}"); - HandleFail::InternalError(idx) - })? { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => { - println!("Dial data response: {dial_data_response:?}"); - dial_data_response - } - }; + let DialDataResponse { data_count } = match coder + .next_request() + .await + .map_err(|e| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response, + }; rem_data = rem_data.saturating_sub(data_count); } } @@ -248,9 +244,6 @@ where .send(dial_back_cmd) .await .map_err(|_| HandleFail::InternalError(idx))?; - if rx.is_terminated() { - println!("is terminated"); - } let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; if dial_back != DialBack::Ok { return Err(HandleFail::DialBack { @@ -278,7 +271,6 @@ async fn handle_request( handle_request_internal(&mut coder, observed_multiaddr, dial_back_cmd_sender, rng) .await .unwrap_or_else(|e| e.into()); - println!("Response: {response:?}"); coder.send_response(Response::Dial(response)).await?; coder.close().await?; Ok(()) diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index dbc5e2d1819..332918f249d 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -101,32 +101,26 @@ async fn dial_back_to_not_supporting() { e => panic!("unknown events {e:#?}"), } - println!("Standard succeed"); - let mut hannes = new_dummy().await; let unreachable_address = hannes.external_addresses().next().unwrap().clone(); bob.behaviour_mut() .autonat .inject_test_addr(unreachable_address.clone()); - tokio::spawn(async move { - loop { - let hannes_event = hannes.next_swarm_event().await; - println!("Hannes: {hannes_event:?}"); - } - }); - - tokio::spawn(async move { + let handler = tokio::spawn(async move { loop { - let bob_event = bob.next_swarm_event().await; - println!("Bob: {bob_event:?}"); + hannes.next_swarm_event().await; } }); - loop { - let alice_event = alice.next_swarm_event().await; - println!("Alice: {alice_event:?}"); + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { .. }], + [SwarmEvent::ExternalAddrExpired { .. }], + ) => {} + e => panic!("unknown events {e:#?}"), } + handler.abort(); } async fn new_server() -> Swarm { From faa694abde2031d7efefdc19d9ae25f8b51c2e64 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:58:18 +0100 Subject: [PATCH 044/179] Tests are working --- protocols/autonatv2/src/client.rs | 3 +- protocols/autonatv2/src/client/behaviour.rs | 17 +- .../src/client/handler/dial_request.rs | 47 +++- protocols/autonatv2/src/lib.rs | 15 -- protocols/autonatv2/src/server/behaviour.rs | 50 ++-- protocols/autonatv2/tests/autonatv2.rs | 232 ++++++++++++------ 6 files changed, 240 insertions(+), 124 deletions(-) diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index be1f6fd8df0..0e26a51499e 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,4 +1,5 @@ mod behaviour; mod handler; -pub use behaviour::Behaviour; +pub use behaviour::{Behaviour, Report}; +pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 70ecb4b3284..756e7f6815e 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -17,7 +17,11 @@ use rand_core::{OsRng, RngCore}; use crate::{global_only::IpExt, request_response::DialRequest}; -use super::handler::{dial_back, dial_request, Handler, TestEnd}; +use super::handler::{ + dial_back, + dial_request::{self, StatusUpdate}, + Handler, TestEnd, +}; struct IntervalTicker { interval: Duration, @@ -49,6 +53,12 @@ impl Default for Config { } } +#[derive(Debug)] +pub struct Report { + pub update: StatusUpdate, + pub peer_id: PeerId, +} + pub struct Behaviour where R: RngCore + 'static, @@ -76,7 +86,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = (); + type ToSwarm = Report; fn handle_established_inbound_connection( &mut self, @@ -209,6 +219,9 @@ where Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { tracing::debug!("Test failed: {:?}", err); } + Either::Left(dial_request::ToBehaviour::StatusUpdate(update)) => self + .pending_events + .push_back(ToSwarm::GenerateEvent(Report { update, peer_id })), } } diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index a4700f679d7..439d0148ffd 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,4 +1,7 @@ -use futures::{channel::oneshot, AsyncRead, AsyncWrite, AsyncWriteExt}; +use futures::{ + channel::{mpsc, oneshot}, + AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, +}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -58,7 +61,7 @@ pub enum Error { #[error("server experienced failure during dial back on address: {addr:?}")] FailureDuringDialBack { addr: Option }, #[error("error during substream upgrad")] - SubstreamError( + Substream( #[from] StreamUpgradeError< as OutboundUpgradeSend>::Error>, ), } @@ -73,9 +76,16 @@ pub struct TestEnd { #[derive(Debug)] pub enum ToBehaviour { TestCompleted(Result), + StatusUpdate(StatusUpdate), PeerHasServerSupport, } +#[derive(Debug)] +pub enum StatusUpdate { + GotDialDataReq { addr: Multiaddr, num_bytes: usize }, + CompletedDialData { addr: Multiaddr, num_bytes: usize }, +} + #[derive(Debug)] pub enum FromBehaviour { PerformRequest(DialRequest), @@ -98,14 +108,19 @@ pub struct Handler { >, >, >, + status_update_rx: mpsc::Receiver, + status_update_tx: mpsc::Sender, } impl Handler { pub(crate) fn new() -> Self { + let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), queued_streams: VecDeque::default(), + status_update_tx, + status_update_rx, } } @@ -118,7 +133,11 @@ impl Handler { }); if self .outbound - .try_push(start_substream_handle(req, rx)) + .try_push(start_substream_handle( + req, + rx, + self.status_update_tx.clone(), + )) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -157,6 +176,11 @@ impl ConnectionHandler for Handler { ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), )); } + if let Poll::Ready(Some(status_update)) = self.status_update_rx.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::StatusUpdate(status_update), + )); + } Poll::Pending } @@ -226,9 +250,10 @@ async fn start_substream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, + status_update_tx: mpsc::Sender, ) -> Result { match substream_recv.await { - Ok(Ok(substream)) => handle_substream(dial_request, substream).await, + Ok(Ok(substream)) => handle_substream(dial_request, substream, status_update_tx).await, Ok(Err(err)) => Err(Error::from(err)), Err(_) => Err(Error::InternalServer), } @@ -237,6 +262,7 @@ async fn start_substream_handle( async fn handle_substream( dial_request: DialRequest, substream: impl AsyncRead + AsyncWrite + Unpin, + mut status_update_tx: mpsc::Sender, ) -> Result { let mut coder = Coder::new(substream); coder @@ -267,10 +293,11 @@ async fn handle_substream( min: DATA_LEN_LOWER_BOUND, }); } - match dial_request.addrs.get(addr_idx) { + let addr = match dial_request.addrs.get(addr_idx).cloned() { Some(addr) => { tracing::trace!("the address {addr} is suspicious to the server, sending {num_bytes} bytes of data"); suspicious_addr.push(addr.clone()); + addr } None => { return Err(Error::InvalidReferencedAddress { @@ -278,9 +305,17 @@ async fn handle_substream( max: dial_request.addrs.len(), }); } - } + }; + let _ = status_update_tx + .send(StatusUpdate::GotDialDataReq { + addr: addr.clone(), + num_bytes, + }) + .await; + let status_update = StatusUpdate::CompletedDialData { addr, num_bytes }; send_aap_data(&mut coder, num_bytes).await?; + let _ = status_update_tx.send(status_update).await; } Response::Dial(dial_response) => { coder.close().await?; diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index 2b4db326485..00023a8b9c3 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -17,18 +17,3 @@ pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); pub type Nonce = u64; - -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 3f20fbf1bfb..c2592af17e2 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -139,10 +139,19 @@ where .. }) => { for (addr, _) in pairs.iter() { - if let Some((_, p)) = self.dialing_dial_back.keys().find(|(a, _)| a == addr) { - let cmds = self.dialing_dial_back.remove(&(addr.clone(), *p)).unwrap(); - for cmd in cmds { - let _ = cmd.back_channel.send(DialBack::Dial); + let cleaned_addr: Multiaddr = addr + .iter() + .filter(|p| !matches!(p, Protocol::P2p(_))) + .collect(); + let peer_id_opt = addr.iter().find_map(|p| match p { + Protocol::P2p(peer) => Some(peer), + _ => None, + }); + if let Some(peer_id) = peer_id_opt { + if let Some(cmd) = self.dialing_dial_back.remove(&(cleaned_addr, peer_id)) { + for cmd in cmd { + let _ = cmd.back_channel.send(DialBack::Dial); + } } } } @@ -167,27 +176,20 @@ where handler: NotifyHandler::One(*connection_id), event: Either::Left(cmd), }); + } else if let Some(pending) = + self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) + { + pending.push_back(cmd); } else { - if let Some(pending) = - self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) - { - pending.push_back(cmd); - } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr.clone()])) - .condition(PeerCondition::Always) - .allocate_new_port() - .build(), - /* opts: DialOpts::unknown_peer_id() - .address(addr.clone()) - .allocate_new_port() - .build(), - */ - }); - self.dialing_dial_back - .insert((addr, peer_id), VecDeque::from([cmd])); - } + self.pending_events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr.clone()])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(), + }); + self.dialing_dial_back + .insert((addr, peer_id), VecDeque::from([cmd])); } } Err(e) => { diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index 332918f249d..0e947ecdbdd 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -1,4 +1,7 @@ -use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmEvent}; +use futures::StreamExt; +use libp2p_autonatv2::client::{Report, StatusUpdate}; +use libp2p_core::{multiaddr::Protocol, Multiaddr}; +use libp2p_swarm::{DialError, NetworkBehaviour, Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt; use tracing_subscriber::EnvFilter; @@ -7,99 +10,111 @@ async fn confirm_successful() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); - let mut alice = new_server().await; - let cor_server_peer = alice.local_peer_id().clone(); - let mut bob = new_client().await; - let cor_client_peer = bob.local_peer_id().clone(); - bob.connect(&mut alice).await; + let (_alice, _bob) = bootstrap().await; +} - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { - peer_id: client_peer_sent, - })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( - libp2p_identify::Event::Received { - peer_id: client_peer_recv, - .. - }, - )), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { - peer_id: server_peer_sent, - })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( - libp2p_identify::Event::Received { - peer_id: server_peer_recv, - .. - }, - ))], - ) => { - assert_eq!(server_peer_sent, cor_server_peer); - assert_eq!(client_peer_sent, cor_client_peer); - assert_eq!(server_peer_recv, cor_server_peer); - assert_eq!(client_peer_recv, cor_client_peer); - } - e => panic!("unexpected events: {e:#?}"), - } +#[tokio::test] +async fn dial_back_to_unsupported_protocol() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let (mut alice, mut bob) = bootstrap().await; + + let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + + bob.behaviour_mut() + .autonat + .inject_test_addr(test_addr.clone()); match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( - [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], + [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(_), + .. + }], + [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::GotDialDataReq { + addr: addr_0, + num_bytes: req_num, + }, + .. + })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::CompletedDialData { + addr: addr_1, + num_bytes: done_num, + }, + .. + })), SwarmEvent::ExternalAddrExpired { .. }], ) => { - assert_eq!(addr_new, addr_snd); - assert_eq!(addr_snd, addr_ok); + assert_eq!(addr_0, test_addr); + assert_eq!(addr_1, test_addr); + assert_eq!(req_num, done_num); } e => panic!("unknown events {e:#?}"), } } #[tokio::test] -async fn dial_back_to_not_supporting() { +async fn dial_back_to_non_libp2p() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); - let mut alice = new_server().await; - let cor_server_peer = alice.local_peer_id().clone(); - let mut bob = new_client().await; - let cor_client_peer = bob.local_peer_id().clone(); - bob.connect(&mut alice).await; + let (mut alice, mut bob) = bootstrap().await; - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { - peer_id: client_peer_sent, - })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( - libp2p_identify::Event::Received { - peer_id: client_peer_recv, + for addr_str in [ + "/dns4/umgefahren.xyz/tcp/809", + "/ip4/169.150.247.38/tcp/32", + "/ip6/2400:52e0:1e02::1187:1/tcp/1000", + ] { + let addr: Multiaddr = addr_str.parse().unwrap(); + bob.behaviour_mut().autonat.inject_test_addr(addr.clone()); + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { + error: DialError::Transport(mut peers), .. - }, - )), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { - peer_id: server_peer_sent, - })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( - libp2p_identify::Event::Received { - peer_id: server_peer_recv, + }], + [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::GotDialDataReq { + addr: addr_0, + num_bytes: req_num, + }, .. - }, - ))], - ) => { - assert_eq!(server_peer_sent, cor_server_peer); - assert_eq!(client_peer_sent, cor_client_peer); - assert_eq!(server_peer_recv, cor_server_peer); - assert_eq!(client_peer_recv, cor_client_peer); + })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::CompletedDialData { + addr: addr_1, + num_bytes: done_num, + }, + .. + })), SwarmEvent::ExternalAddrExpired { .. }], + ) => { + let (peer_addr, _) = peers.pop().unwrap(); + let cleaned_addr: Multiaddr = peer_addr + .iter() + .filter(|p| !matches!(p, Protocol::P2p(_))) + .collect(); + assert_eq!(addr, cleaned_addr); + assert_eq!(addr_0, cleaned_addr); + assert_eq!(addr_1, cleaned_addr); + assert_eq!(req_num, done_num); + } + e => panic!("unknown events {e:#?}"), } - e => panic!("unexpected events: {e:#?}"), } +} - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], - ) => { - assert_eq!(addr_new, addr_snd); - assert_eq!(addr_snd, addr_ok); - } - e => panic!("unknown events {e:#?}"), - } +#[tokio::test] +async fn dial_back_to_not_supporting() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let (mut alice, mut bob) = bootstrap().await; let mut hannes = new_dummy().await; let unreachable_address = hannes.external_addresses().next().unwrap().clone(); @@ -116,8 +131,26 @@ async fn dial_back_to_not_supporting() { match libp2p_swarm_test::drive(&mut alice, &mut bob).await { ( [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { .. }], - [SwarmEvent::ExternalAddrExpired { .. }], - ) => {} + [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::GotDialDataReq { + addr: addr_0, + num_bytes: req_num, + }, + .. + })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { + update: + StatusUpdate::CompletedDialData { + addr: addr_1, + num_bytes: done_num, + }, + .. + })), SwarmEvent::ExternalAddrExpired { .. }], + ) => { + assert_eq!(addr_0, unreachable_address); + assert_eq!(addr_1, unreachable_address); + assert_eq!(req_num, done_num); + } e => panic!("unknown events {e:#?}"), } handler.abort(); @@ -172,3 +205,50 @@ async fn new_dummy() -> Swarm { node.listen().with_tcp_addr_external().await; node } + +async fn bootstrap() -> (Swarm, Swarm) { + let mut alice = new_server().await; + let cor_server_peer = alice.local_peer_id().clone(); + let mut bob = new_client().await; + let cor_client_peer = bob.local_peer_id().clone(); + bob.connect(&mut alice).await; + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { + peer_id: client_peer_sent, + })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( + libp2p_identify::Event::Received { + peer_id: client_peer_recv, + .. + }, + )), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { + peer_id: server_peer_sent, + })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( + libp2p_identify::Event::Received { + peer_id: server_peer_recv, + .. + }, + ))], + ) => { + assert_eq!(server_peer_sent, cor_server_peer); + assert_eq!(client_peer_sent, cor_client_peer); + assert_eq!(server_peer_recv, cor_server_peer); + assert_eq!(client_peer_recv, cor_client_peer); + } + e => panic!("unexpected events: {e:#?}"), + } + + match libp2p_swarm_test::drive(&mut alice, &mut bob).await { + ( + [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], + [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], + ) => { + assert_eq!(addr_new, addr_snd); + assert_eq!(addr_snd, addr_ok); + } + e => panic!("unknown events {e:#?}"), + } + (alice, bob) +} From 6282cae0e81235a1610aa37cb078aea7bf651f46 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 22 Dec 2023 15:42:18 +0100 Subject: [PATCH 045/179] Implement in call suggested changes --- Cargo.lock | 1 + core/src/transport/global_only.rs | 2 +- protocols/autonatv2/Cargo.toml | 3 +- protocols/autonatv2/src/client.rs | 2 +- protocols/autonatv2/src/client/behaviour.rs | 101 ++-- protocols/autonatv2/src/client/handler.rs | 49 +- .../src/client/handler/dial_request.rs | 196 ++++--- protocols/autonatv2/src/global_only.rs | 2 +- protocols/autonatv2/src/server.rs | 1 + protocols/autonatv2/src/server/behaviour.rs | 158 ++---- .../autonatv2/src/server/handler/dial_back.rs | 42 +- .../src/server/handler/dial_request.rs | 218 ++++--- protocols/autonatv2/tests/autonatv2.rs | 532 +++++++++++++----- swarm/src/lib.rs | 31 +- transports/tcp/src/lib.rs | 22 +- 15 files changed, 847 insertions(+), 513 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d17406cbae8..9ca6c813c3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2659,6 +2659,7 @@ dependencies = [ "either", "futures", "futures-bounded", + "futures-timer", "libp2p-core", "libp2p-identify", "libp2p-identity", diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index 2963cc955d0..a40f99054d0 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -249,7 +249,7 @@ mod ip { // AS112-v6 (`2001:4:112::/48`) || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) )) || Ipv6Ext::is_documentation(self) || Ipv6Ext::is_unique_local(self) diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 3f5ccf0b68e..55b778c6ef9 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -25,9 +25,10 @@ bytes = "1" static_assertions = "1.1.0" tracing = "0.1.40" unsigned-varint = { workspace = true, features = ["futures"] } +futures-timer = "3.0.2" [dev-dependencies] -tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] } libp2p-swarm-test = { workspace = true } libp2p-identify = { workspace = true } libp2p-swarm = { workspace = true, features = ["macros"] } diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index 0e26a51499e..fc15af146f3 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,5 +1,5 @@ mod behaviour; mod handler; -pub use behaviour::{Behaviour, Report}; +pub use behaviour::{Behaviour, Config}; pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 756e7f6815e..47f6ece4d07 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -1,10 +1,12 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, task::{Context, Poll}, - time::{Duration, Instant}, + time::Duration, }; use either::Either; +use futures::FutureExt; +use futures_timer::Delay; use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -23,25 +25,34 @@ use super::handler::{ Handler, TestEnd, }; -struct IntervalTicker { - interval: Duration, - last_tick: Instant, +#[derive(Debug, Clone, Copy)] +pub struct Config { + pub(crate) test_server_count: usize, + pub(crate) max_addrs_count: usize, + pub(crate) recheck_interval: Duration, } -impl IntervalTicker { - fn ready(&mut self) -> bool { - if self.last_tick.elapsed() >= self.interval { - self.last_tick = Instant::now(); - true - } else { - false +impl Config { + pub fn with_test_server_count(self, test_server_count: usize) -> Self { + Self { + test_server_count, + ..self } } -} -pub struct Config { - pub(crate) test_server_count: usize, - pub(crate) max_addrs_count: usize, + pub fn with_max_addrs_count(self, max_addrs_count: usize) -> Self { + Self { + max_addrs_count, + ..self + } + } + + pub fn with_recheck_interval(self, recheck_interval: Duration) -> Self { + Self { + recheck_interval, + ..self + } + } } impl Default for Config { @@ -49,16 +60,10 @@ impl Default for Config { Self { test_server_count: 3, max_addrs_count: 10, + recheck_interval: Duration::from_secs(5), } } } - -#[derive(Debug)] -pub struct Report { - pub update: StatusUpdate, - pub peer_id: PeerId, -} - pub struct Behaviour where R: RngCore + 'static, @@ -77,7 +82,7 @@ where address_candidates: HashMap, already_tested: HashSet, peers_to_handlers: HashMap, - ticker: IntervalTicker, + next_tick: Delay, } impl NetworkBehaviour for Behaviour @@ -86,7 +91,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = Report; + type ToSwarm = StatusUpdate; fn handle_established_inbound_connection( &mut self, @@ -104,7 +109,7 @@ where fn handle_established_outbound_connection( &mut self, connection_id: ConnectionId, - _peer: PeerId, + peer: PeerId, addr: &Multiaddr, _role_override: Endpoint, _port_use: PortUse, @@ -112,7 +117,7 @@ where if addr_is_local(addr) { self.local_peers.insert(connection_id); } - Ok(Either::Left(dial_request::Handler::new())) + Ok(Either::Left(dial_request::Handler::new(peer))) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -183,7 +188,6 @@ where } Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { dial_request: DialRequest { nonce, addrs }, - suspicious_addr, reachable_addr, }))) => { if self.pending_nonces.remove(&nonce) { @@ -192,12 +196,6 @@ where ); return; } - if !suspicious_addr.is_empty() { - tracing::trace!( - "server reported suspicious addresses: {:?}", - suspicious_addr - ); - } self.pending_events.extend( addrs .into_iter() @@ -207,33 +205,35 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } - Either::Left(dial_request::ToBehaviour::TestCompleted(Err( - dial_request::Error::UnableToConnectOnSelectedAddress { addr: Some(addr) }, - ))) - | Either::Left(dial_request::ToBehaviour::TestCompleted(Err( - dial_request::Error::FailureDuringDialBack { addr: Some(addr) }, - ))) => { - self.pending_events - .push_back(ToSwarm::ExternalAddrExpired(addr)); - } Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { - tracing::debug!("Test failed: {:?}", err); + match err.internal.as_ref() { + dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } + | dial_request::InternalError::UnableToConnectOnSelectedAddress { + addr: Some(addr), + } => { + self.pending_events + .push_back(ToSwarm::ExternalAddrExpired(addr.clone())); + } + _ => { + tracing::debug!("Test failed: {:?}", err); + } + } } Either::Left(dial_request::ToBehaviour::StatusUpdate(update)) => self .pending_events - .push_back(ToSwarm::GenerateEvent(Report { update, peer_id })), + .push_back(ToSwarm::GenerateEvent(update)), } } fn poll( &mut self, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { let pending_event = self.poll_pending_events(); if pending_event.is_ready() { return pending_event; } - if self.ticker.ready() + if self.next_tick.poll_unpin(cx).is_ready() && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { @@ -282,15 +282,12 @@ where pending_nonces: HashSet::new(), known_servers: Vec::new(), rng, + next_tick: Delay::new(config.recheck_interval), config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), peers_to_handlers: HashMap::new(), already_tested: HashSet::new(), - ticker: IntervalTicker { - interval: Duration::from_secs(0), - last_tick: Instant::now(), - }, } } @@ -327,10 +324,6 @@ where } Poll::Pending } - - pub fn inject_test_addr(&mut self, addr: Multiaddr) { - *self.address_candidates.entry(addr).or_default() += 1; - } } impl Default for Behaviour { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index e4a48992727..0776e45e5bd 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,20 +1,51 @@ -// two handlers, share state in behaviour -// do isolated stuff in async function -// -// write basic tests -// Take a look at rendezvous -// TODO: tests -// TODO: Handlers - pub(crate) mod dial_back; pub(crate) mod dial_request; use either::Either; -use std::time::Duration; +use std::{ + fmt::{Display, Formatter}, + sync::Arc, + time::Duration, +}; pub(crate) use dial_request::TestEnd; +use self::dial_request::InternalError; + const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; pub(crate) type Handler = Either; + +#[derive(Clone, Debug)] +pub struct Error { + pub(crate) internal: Arc, +} + +impl From for Error { + fn from(value: InternalError) -> Self { + Self { + internal: Arc::new(value), + } + } +} + +impl From> for Error { + fn from(value: Arc) -> Self { + Self { internal: value } + } +} + +impl From<&Arc> for Error { + fn from(value: &Arc) -> Self { + Self { + internal: Arc::clone(value), + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.internal.fmt(f) + } +} diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index 439d0148ffd..d3a13218ace 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,6 +1,6 @@ use futures::{ channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, + AsyncRead, AsyncWrite, SinkExt, StreamExt, }; use futures_bounded::FuturesSet; use libp2p_core::{ @@ -8,6 +8,7 @@ use libp2p_core::{ Multiaddr, }; +use libp2p_identity::PeerId; use libp2p_swarm::{ handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, @@ -21,6 +22,7 @@ use std::{ convert::identity, io, iter::{once, repeat}, + sync::Arc, task::{Context, Poll}, }; @@ -37,7 +39,7 @@ use crate::{ use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; #[derive(Debug, thiserror::Error)] -pub enum Error { +pub(crate) enum InternalError { #[error("io error")] Io(#[from] io::Error), #[error("invalid referenced address index: {index} (max number of addr: {max})")] @@ -69,21 +71,22 @@ pub enum Error { #[derive(Debug)] pub struct TestEnd { pub(crate) dial_request: DialRequest, - pub(crate) suspicious_addr: Vec, pub(crate) reachable_addr: Multiaddr, } #[derive(Debug)] pub enum ToBehaviour { - TestCompleted(Result), + TestCompleted(Result), StatusUpdate(StatusUpdate), PeerHasServerSupport, } #[derive(Debug)] -pub enum StatusUpdate { - GotDialDataReq { addr: Multiaddr, num_bytes: usize }, - CompletedDialData { addr: Multiaddr, num_bytes: usize }, +pub struct StatusUpdate { + pub tested_addr: Option, + pub data_amount: usize, + pub server: PeerId, + pub result: Result<(), crate::client::handler::Error>, } #[derive(Debug)] @@ -99,7 +102,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet>, queued_streams: VecDeque< oneshot::Sender< Result< @@ -110,10 +113,11 @@ pub struct Handler { >, status_update_rx: mpsc::Receiver, status_update_tx: mpsc::Sender, + server: PeerId, } impl Handler { - pub(crate) fn new() -> Self { + pub(crate) fn new(server: PeerId) -> Self { let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), @@ -121,6 +125,7 @@ impl Handler { queued_streams: VecDeque::default(), status_update_tx, status_update_rx, + server, } } @@ -134,6 +139,7 @@ impl Handler { if self .outbound .try_push(start_substream_handle( + self.server, req, rx, self.status_update_tx.clone(), @@ -173,7 +179,12 @@ impl ConnectionHandler for Handler { } if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestCompleted(m.map_err(Error::Timeout).and_then(identity)), + ToBehaviour::TestCompleted( + m.map_err(InternalError::Timeout) + .map_err(Into::into) + .and_then(identity) + .map_err(Into::into), + ), )); } if let Poll::Ready(Some(status_update)) = self.status_update_rx.poll_next_unpin(cx) { @@ -206,7 +217,7 @@ impl ConnectionHandler for Handler { tracing::debug!("Dial request failed: {}", error); match self.queued_streams.pop_front() { Some(stream_tx) => { - if let Err(_) = stream_tx.send(Err(error)) { + if stream_tx.send(Err(error)).is_err() { tracing::warn!("Failed to send stream to dead handler"); } } @@ -221,7 +232,7 @@ impl ConnectionHandler for Handler { protocol, .. }) => match self.queued_streams.pop_front() { Some(stream_tx) => { - if let Err(_) = stream_tx.send(Ok(protocol)) { + if stream_tx.send(Ok(protocol)).is_err() { tracing::warn!("Failed to send stream to dead handler"); } } @@ -243,6 +254,7 @@ impl ConnectionHandler for Handler { } async fn start_substream_handle( + server: PeerId, dial_request: DialRequest, substream_recv: oneshot::Receiver< Result< @@ -250,133 +262,143 @@ async fn start_substream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, - status_update_tx: mpsc::Sender, -) -> Result { - match substream_recv.await { - Ok(Ok(substream)) => handle_substream(dial_request, substream, status_update_tx).await, - Ok(Err(err)) => Err(Error::from(err)), - Err(_) => Err(Error::InternalServer), - } + mut status_update_tx: mpsc::Sender, +) -> Result { + let substream = match substream_recv.await { + Ok(Ok(substream)) => substream, + Ok(Err(err)) => return Err(InternalError::from(err).into()), + Err(_) => return Err(InternalError::InternalServer.into()), + }; + let mut data_amount = 0; + let mut checked_addr_idx = None; + let addrs = dial_request.addrs.clone(); + let res = handle_substream( + dial_request, + substream, + &mut data_amount, + &mut checked_addr_idx, + ) + .await + .map_err(Arc::new) + .map_err(crate::client::handler::Error::from); + let status_update = StatusUpdate { + tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), + data_amount, + server, + result: res.as_ref().map(|_| ()).map_err(|e| e.clone()), + }; + let _ = status_update_tx.send(status_update).await; + res } async fn handle_substream( dial_request: DialRequest, substream: impl AsyncRead + AsyncWrite + Unpin, - mut status_update_tx: mpsc::Sender, -) -> Result { + data_amount: &mut usize, + checked_addr_idx: &mut Option, +) -> Result { let mut coder = Coder::new(substream); coder .send_request(Request::Dial(dial_request.clone())) .await?; - let mut suspicious_addr = Vec::new(); - loop { - match coder.next_response().await? { - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => { - if addr_idx >= dial_request.addrs.len() { - return Err(Error::InvalidReferencedAddress { - index: addr_idx, - max: dial_request.addrs.len(), - }); - } - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(Error::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); - } - if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(Error::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - let addr = match dial_request.addrs.get(addr_idx).cloned() { - Some(addr) => { - tracing::trace!("the address {addr} is suspicious to the server, sending {num_bytes} bytes of data"); - suspicious_addr.push(addr.clone()); - addr - } - None => { - return Err(Error::InvalidReferencedAddress { - index: addr_idx, - max: dial_request.addrs.len(), - }); - } - }; - - let _ = status_update_tx - .send(StatusUpdate::GotDialDataReq { - addr: addr.clone(), - num_bytes, - }) - .await; - let status_update = StatusUpdate::CompletedDialData { addr, num_bytes }; - send_aap_data(&mut coder, num_bytes).await?; - let _ = status_update_tx.send(status_update).await; + match coder.next_response().await? { + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if addr_idx >= dial_request.addrs.len() { + return Err(InternalError::InvalidReferencedAddress { + index: addr_idx, + max: dial_request.addrs.len(), + }); + } + if num_bytes > DATA_LEN_UPPER_BOUND { + return Err(InternalError::DataRequestTooLarge { + len: num_bytes, + max: DATA_LEN_UPPER_BOUND, + }); + } + if num_bytes < DATA_LEN_LOWER_BOUND { + return Err(InternalError::DataRequestTooSmall { + len: num_bytes, + min: DATA_LEN_LOWER_BOUND, + }); } - Response::Dial(dial_response) => { + *checked_addr_idx = Some(addr_idx); + send_aap_data(&mut coder, num_bytes, data_amount).await?; + if let Response::Dial(dial_response) = coder.next_response().await? { + *checked_addr_idx = Some(dial_response.addr_idx); coder.close().await?; - return test_end_from_dial_response(dial_request, dial_response, suspicious_addr); + test_end_from_dial_response(dial_request, dial_response) + } else { + Err(InternalError::InternalServer) } } + Response::Dial(dial_response) => { + *checked_addr_idx = Some(dial_response.addr_idx); + coder.close().await?; + test_end_from_dial_response(dial_request, dial_response) + } } } fn test_end_from_dial_response( req: DialRequest, resp: DialResponse, - suspicious_addr: Vec, -) -> Result { +) -> Result { if resp.addr_idx >= req.addrs.len() { - return Err(Error::InvalidReferencedAddress { + return Err(InternalError::InvalidReferencedAddress { index: resp.addr_idx, max: req.addrs.len(), }); } match (resp.status, resp.dial_status) { - (ResponseStatus::E_REQUEST_REJECTED, _) => Err(Error::ServerRejectedDialRequest), - (ResponseStatus::E_DIAL_REFUSED, _) => Err(Error::ServerChoseNotToDialAnyAddress), - (ResponseStatus::E_INTERNAL_ERROR, _) => Err(Error::InternalServer), - (ResponseStatus::OK, DialStatus::UNUSED) => Err(Error::InvalidResponse), + (ResponseStatus::E_REQUEST_REJECTED, _) => Err(InternalError::ServerRejectedDialRequest), + (ResponseStatus::E_DIAL_REFUSED, _) => Err(InternalError::ServerChoseNotToDialAnyAddress), + (ResponseStatus::E_INTERNAL_ERROR, _) => Err(InternalError::InternalServer), + (ResponseStatus::OK, DialStatus::UNUSED) => Err(InternalError::InvalidResponse), (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { - Err(Error::UnableToConnectOnSelectedAddress { + Err(InternalError::UnableToConnectOnSelectedAddress { + addr: req.addrs.get(resp.addr_idx).cloned(), + }) + } + (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { + Err(InternalError::FailureDuringDialBack { addr: req.addrs.get(resp.addr_idx).cloned(), }) } - (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => Err(Error::FailureDuringDialBack { - addr: req.addrs.get(resp.addr_idx).cloned(), - }), (ResponseStatus::OK, DialStatus::OK) => req .addrs .get(resp.addr_idx) - .ok_or(Error::InvalidReferencedAddress { + .ok_or(InternalError::InvalidReferencedAddress { index: resp.addr_idx, max: req.addrs.len(), }) .cloned() .map(|reachable_addr| TestEnd { dial_request: req, - suspicious_addr, reachable_addr, }), } } -async fn send_aap_data(substream: &mut Coder, num_bytes: usize) -> io::Result<()> +async fn send_aap_data( + substream: &mut Coder, + num_bytes: usize, + data_amount: &mut usize, +) -> io::Result<()> where I: AsyncWrite + Unpin, { let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; - for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) + for (data_count, req) in repeat(DATA_FIELD_LEN_UPPER_BOUND) .take(count_full) .chain(once(partial_len)) .filter(|e| *e > 0) - .map(|data_count| Request::Data(DialDataResponse { data_count })) + .map(|data_count| (data_count, Request::Data(DialDataResponse { data_count }))) { + *data_amount += data_count; substream.send_request(req).await?; } Ok(()) diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonatv2/src/global_only.rs index e549030eff9..af788639399 100644 --- a/protocols/autonatv2/src/global_only.rs +++ b/protocols/autonatv2/src/global_only.rs @@ -228,7 +228,7 @@ impl IpExt for Ipv6Addr { // AS112-v6 (`2001:4:112::/48`) || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) )) || Ipv6Ext::is_documentation(self) || Ipv6Ext::is_unique_local(self) diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs index be1f6fd8df0..ef746607559 100644 --- a/protocols/autonatv2/src/server.rs +++ b/protocols/autonatv2/src/server.rs @@ -2,3 +2,4 @@ mod behaviour; mod handler; pub use behaviour::Behaviour; +pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index c2592af17e2..9480a113571 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -1,19 +1,17 @@ -use std::fmt::Debug; use std::{ collections::{HashMap, VecDeque}, task::{Context, Poll}, }; -use crate::server::handler::dial_request::DialBack; +use crate::server::handler::dial_request::DialBackStatus; use either::Either; -use libp2p_core::multiaddr::Protocol; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; -use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ - dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialError, DialFailure, - FromSwarm, NetworkBehaviour, NotifyHandler, ToSwarm, + dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, + NetworkBehaviour, ToSwarm, }; +use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; use super::handler::{ @@ -26,8 +24,7 @@ pub struct Behaviour where R: Clone + Send + RngCore + 'static, { - handlers: HashMap<(Multiaddr, PeerId), ConnectionId>, - dialing_dial_back: HashMap<(Multiaddr, PeerId), VecDeque>, + dialing_dial_back: HashMap, pending_events: VecDeque< ToSwarm< ::ToSwarm, @@ -49,7 +46,6 @@ where { pub fn new(rng: R) -> Self { Self { - handlers: HashMap::new(), dialing_dial_back: HashMap::new(), pending_events: VecDeque::new(), rng, @@ -78,16 +74,17 @@ where { type ConnectionHandler = Handler; - type ToSwarm = (); + type ToSwarm = crate::server::handler::dial_request::StatusUpdate; fn handle_established_inbound_connection( &mut self, _connection_id: ConnectionId, - _peer: PeerId, + peer: PeerId, _local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result<::ConnectionHandler, ConnectionDenied> { Ok(Either::Right(dial_request::Handler::new( + peer, remote_addr.clone(), self.rng.clone(), ))) @@ -96,64 +93,28 @@ where fn handle_established_outbound_connection( &mut self, connection_id: ConnectionId, - peer: PeerId, - addr: &Multiaddr, + _peer: PeerId, + _addr: &Multiaddr, _role_override: Endpoint, - port_use: PortUse, + _port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - if port_use == PortUse::New { - self.handlers.insert( - ( - addr.iter() - .filter(|e| !matches!(e, Protocol::P2p(_))) - .collect(), - peer, - ), - connection_id, - ); - } - Ok(Either::Left(dial_back::Handler::new())) + Ok( + if let Some(cmd) = self.dialing_dial_back.remove(&connection_id) { + Either::Left(dial_back::Handler::new(cmd)) + } else { + Either::Left(dial_back::Handler::empty()) + }, + ) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { - FromSwarm::DialFailure(DialFailure { - error: DialError::WrongPeerId { .. }, - peer_id: Some(peer_id), - .. - }) => { - if let Some(key) = self - .dialing_dial_back - .keys() - .find(|(_, p)| *p == peer_id) - .cloned() + FromSwarm::DialFailure(DialFailure { connection_id, .. }) + | FromSwarm::ConnectionClosed(ConnectionClosed { connection_id, .. }) => { + if let Some(DialBackCommand { back_channel, .. }) = + self.dialing_dial_back.remove(&connection_id) { - let cmds = self.dialing_dial_back.remove(&key).unwrap(); - for cmd in cmds { - let _ = cmd.back_channel.send(DialBack::Dial); - } - } - } - FromSwarm::DialFailure(DialFailure { - error: DialError::Transport(pairs), - .. - }) => { - for (addr, _) in pairs.iter() { - let cleaned_addr: Multiaddr = addr - .iter() - .filter(|p| !matches!(p, Protocol::P2p(_))) - .collect(); - let peer_id_opt = addr.iter().find_map(|p| match p { - Protocol::P2p(peer) => Some(peer), - _ => None, - }); - if let Some(peer_id) = peer_id_opt { - if let Some(cmd) = self.dialing_dial_back.remove(&(cleaned_addr, peer_id)) { - for cmd in cmd { - let _ = cmd.back_channel.send(DialBack::Dial); - } - } - } + let _ = back_channel.send(DialBackStatus::DialErr); } } _ => {} @@ -166,36 +127,28 @@ where _connection_id: ConnectionId, event: as ConnectionHandler>::ToBehaviour, ) { - if let Either::Right(m) = event { - match m { - Ok(cmd) => { - let addr = cmd.addr.clone(); - if let Some(connection_id) = self.handlers.get(&(addr.clone(), peer_id)) { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(*connection_id), - event: Either::Left(cmd), - }); - } else if let Some(pending) = - self.dialing_dial_back.get_mut(&(addr.clone(), peer_id)) - { - pending.push_back(cmd); - } else { - self.pending_events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr.clone()])) - .condition(PeerCondition::Always) - .allocate_new_port() - .build(), - }); - self.dialing_dial_back - .insert((addr, peer_id), VecDeque::from([cmd])); - } - } - Err(e) => { - tracing::warn!("incoming dial request failed: {}", e); - } + match event { + Either::Left(Ok(_)) => {} + Either::Left(Err(e)) => { + tracing::debug!("dial back error: {e:?}"); + } + Either::Right(Either::Left(Ok(cmd))) => { + let addr = cmd.addr.clone(); + let opts = DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(); + let conn_id = opts.connection_id(); + self.dialing_dial_back.insert(conn_id, cmd); + self.pending_events.push_back(ToSwarm::Dial { opts }); + } + Either::Right(Either::Left(Err(e))) => { + tracing::warn!("incoming dial request failed: {}", e); } + Either::Right(Either::Right(status_update)) => self + .pending_events + .push_back(ToSwarm::GenerateEvent(status_update)), } } @@ -207,29 +160,6 @@ where if pending_event.is_ready() { return pending_event; } - if let Some((addr, peer)) = self - .dialing_dial_back - .keys() - .filter(|k| self.handlers.contains_key(*k)) - .next() - .cloned() - { - let cmds = self - .dialing_dial_back - .remove(&(addr.clone(), peer)) - .unwrap(); - let cmd_n = cmds.len(); - for cmd in cmds { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: peer.clone(), - handler: NotifyHandler::One(self.handlers[&(addr.clone(), peer)]), - event: Either::Left(cmd), - }); - } - if cmd_n > 0 { - return self.poll_pending_events(cx); - } - } Poll::Pending } } diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index c282ff522dc..8a497dc1f84 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -1,5 +1,4 @@ use std::{ - collections::VecDeque, convert::identity, io, task::{Context, Poll}, @@ -10,36 +9,41 @@ use futures::{AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, ProtocolsChange}, + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, SubstreamProtocol, }; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME, DIAL_BACK_UPGRADE}; +use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; -use super::dial_request::{DialBack as DialBackRes, DialBackCommand}; +use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; pub(crate) type ToBehaviour = io::Result<()>; -pub(crate) type FromBehaviour = DialBackCommand; pub struct Handler { - pending_nonce: VecDeque, - requested_substream_nonce: VecDeque, + pending_nonce: Option, + requested_substream_nonce: Option, outbound: FuturesSet, } impl Handler { - pub(crate) fn new() -> Self { + pub(crate) fn new(cmd: DialBackCommand) -> Self { + let mut ret = Self::empty(); + ret.pending_nonce = Some(cmd); + ret + } + + pub(crate) fn empty() -> Self { Self { - pending_nonce: VecDeque::new(), - requested_substream_nonce: VecDeque::new(), + pending_nonce: None, + requested_substream_nonce: None, outbound: FuturesSet::new(Duration::from_secs(10000), 2), } } } impl ConnectionHandler for Handler { - type FromBehaviour = FromBehaviour; + type FromBehaviour = (); type ToBehaviour = ToBehaviour; type InboundProtocol = DeniedUpgrade; type OutboundProtocol = ReadyUpgrade; @@ -63,8 +67,8 @@ impl ConnectionHandler for Handler { .and_then(identity), )); } - if let Some(cmd) = self.pending_nonce.pop_front() { - self.requested_substream_nonce.push_back(cmd); + if let Some(cmd) = self.pending_nonce.take() { + self.requested_substream_nonce = Some(cmd); return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(DIAL_BACK_UPGRADE, ()), }); @@ -72,9 +76,7 @@ impl ConnectionHandler for Handler { Poll::Pending } - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - self.pending_nonce.push_back(event); - } + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} fn on_connection_event( &mut self, @@ -89,7 +91,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { protocol, .. }) => { - if let Some(cmd) = self.requested_substream_nonce.pop_front() { + if let Some(cmd) = self.requested_substream_nonce.take() { if self .outbound .try_push(perform_dial_back(protocol, cmd)) @@ -109,8 +111,8 @@ impl ConnectionHandler for Handler { error: StreamUpgradeError::Timeout, .. }) => { - if let Some(cmd) = self.requested_substream_nonce.pop_front() { - let _ = cmd.back_channel.send(DialBackRes::DialBack); + if let Some(cmd) = self.requested_substream_nonce.take() { + let _ = cmd.back_channel.send(DialBackRes::DialBackErr); } } _ => {} @@ -128,7 +130,7 @@ async fn perform_dial_back( ) -> io::Result<()> { let res = perform_dial_back_inner(&mut stream, nonce) .await - .map_err(|_| DialBackRes::DialBack) + .map_err(|_| DialBackRes::DialBackErr) .map(|_| DialBackRes::Ok) .unwrap_or_else(|e| e); back_channel diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 6a7cf89de0c..db1e6b284e6 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -1,19 +1,21 @@ use std::{ io, + sync::Arc, task::{Context, Poll}, time::Duration, }; -use futures::future::FusedFuture; +use either::Either; use futures::{ channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt, + AsyncRead, AsyncWrite, SinkExt, StreamExt, }; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, }; +use libp2p_identity::PeerId; use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, @@ -29,11 +31,21 @@ use crate::{ Nonce, REQUEST_UPGRADE, }; +#[derive(Clone, Debug)] +pub struct StatusUpdate { + pub all_addrs: Vec, + pub tested_addr: Option, + pub client: PeerId, + pub data_amount: usize, + pub result: Result<(), Arc>, +} + #[derive(Debug, PartialEq)] -pub(crate) enum DialBack { - #[allow(unused)] - Dial, - DialBack, +pub(crate) enum DialBackStatus { + /// Failure during dial + DialErr, + /// Failure during dial back + DialBackErr, Ok, } @@ -41,14 +53,17 @@ pub(crate) enum DialBack { pub struct DialBackCommand { pub(crate) addr: Multiaddr, pub(crate) nonce: Nonce, - pub(crate) back_channel: oneshot::Sender, + pub(crate) back_channel: oneshot::Sender, } pub struct Handler { + client_id: PeerId, observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, dial_back_cmd_receiver: mpsc::Receiver, - inbound: FuturesSet>, + status_update_sender: mpsc::Sender, + status_update_receiver: mpsc::Receiver, + inbound: FuturesSet>>, rng: R, } @@ -56,12 +71,16 @@ impl Handler where R: RngCore, { - pub(crate) fn new(observed_multiaddr: Multiaddr, rng: R) -> Self { + pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); + let (status_update_sender, status_update_receiver) = mpsc::channel(10); Self { + client_id, observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, + status_update_sender, + status_update_receiver, inbound: FuturesSet::new(Duration::from_secs(10), 10), rng, } @@ -74,7 +93,7 @@ where { type FromBehaviour = (); - type ToBehaviour = io::Result; + type ToBehaviour = Either>, StatusUpdate>; type InboundProtocol = ReadyUpgrade; @@ -96,18 +115,27 @@ where > { match self.inbound.poll_unpin(cx) { Poll::Ready(Ok(Err(e))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err(e))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( + e, + )))); } Poll::Ready(Err(e)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err( - io::Error::new(io::ErrorKind::TimedOut, e), - ))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( + Arc::new(io::Error::new(io::ErrorKind::TimedOut, e)), + )))); } Poll::Ready(Ok(Ok(_))) => {} Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(cmd))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Ok( + cmd, + )))); + } + if let Poll::Ready(Some(status_update)) = self.status_update_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + status_update, + ))); } Poll::Pending } @@ -129,10 +157,12 @@ where }) => { if self .inbound - .try_push(handle_request( + .try_push(start_handle_request( protocol, self.observed_multiaddr.clone(), + self.client_id, self.dial_back_cmd_sender.clone(), + self.status_update_sender.clone(), self.rng.clone(), )) .is_err() @@ -158,7 +188,7 @@ enum HandleFail { InternalError(usize), RequestRejected, DialRefused, - DialBack { idx: usize, err: DialBack }, + DialBack { idx: usize, err: DialBackStatus }, } impl From for DialResponse { @@ -183,9 +213,9 @@ impl From for DialResponse { status: ResponseStatus::OK, addr_idx: idx, dial_status: match err { - DialBack::Dial => DialStatus::E_DIAL_ERROR, - DialBack::DialBack => DialStatus::E_DIAL_BACK_ERROR, - DialBack::Ok => DialStatus::OK, + DialBackStatus::DialErr => DialStatus::E_DIAL_ERROR, + DialBackStatus::DialBackErr => DialStatus::E_DIAL_BACK_ERROR, + DialBackStatus::Ok => DialStatus::OK, }, }, } @@ -197,11 +227,14 @@ async fn handle_request_internal( observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, mut rng: impl RngCore, + all_addrs: &mut Vec, + tested_addrs: &mut Option, + data_amount: &mut usize, ) -> Result where I: AsyncRead + AsyncWrite + Unpin, { - let DialRequest { addrs, nonce } = match coder + let DialRequest { mut addrs, nonce } = match coder .next_request() .await .map_err(|_| HandleFail::InternalError(0))? @@ -211,53 +244,61 @@ where return Err(HandleFail::RequestRejected); } }; - for (idx, addr) in addrs.into_iter().enumerate() { - if addr != observed_multiaddr { - let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); - let mut rem_data = dial_data_request.num_bytes; - coder - .send_response(Response::Data(dial_data_request)) - .await - .map_err(|_| HandleFail::InternalError(idx))?; - while rem_data > 0 { - let DialDataResponse { data_count } = match coder - .next_request() - .await - .map_err(|e| HandleFail::InternalError(idx))? - { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => dial_data_response, - }; - rem_data = rem_data.saturating_sub(data_count); - } - } - let (back_channel, rx) = oneshot::channel(); - let dial_back_cmd = DialBackCommand { - addr, - nonce, - back_channel, - }; - dial_back_cmd_sender - .clone() - .send(dial_back_cmd) + *all_addrs = addrs.clone(); + let idx = 0; + let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; + *tested_addrs = Some(addr.clone()); + *data_amount = 0; + if addr != observed_multiaddr { + let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); + let mut rem_data = dial_data_request.num_bytes; + coder + .send_response(Response::Data(dial_data_request)) .await .map_err(|_| HandleFail::InternalError(idx))?; - let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; - if dial_back != DialBack::Ok { - return Err(HandleFail::DialBack { - idx, - err: dial_back, - }); + while rem_data > 0 { + let DialDataResponse { data_count } = match coder + .next_request() + .await + .map_err(|_e| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response, + }; + rem_data = rem_data.saturating_sub(data_count); + *data_amount += data_count; } - return Ok(DialResponse { - status: ResponseStatus::OK, - addr_idx: idx, - dial_status: DialStatus::OK, + } + let (back_channel, rx) = oneshot::channel(); + let dial_back_cmd = DialBackCommand { + addr, + nonce, + back_channel, + }; + dial_back_cmd_sender + .clone() + .send(dial_back_cmd) + .await + .map_err(|_| HandleFail::DialBack { + idx, + err: DialBackStatus::DialErr, + })?; + + // TODO: add timeout + let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; + if dial_back != DialBackStatus::Ok { + return Err(HandleFail::DialBack { + idx, + err: dial_back, }); } - Err(HandleFail::DialRefused) + Ok(DialResponse { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: DialStatus::OK, + }) } async fn handle_request( @@ -265,13 +306,56 @@ async fn handle_request( observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, rng: impl RngCore, + all_addrs: &mut Vec, + tested_addrs: &mut Option, + data_amount: &mut usize, ) -> io::Result<()> { let mut coder = Coder::new(stream); - let response = - handle_request_internal(&mut coder, observed_multiaddr, dial_back_cmd_sender, rng) - .await - .unwrap_or_else(|e| e.into()); + let response = handle_request_internal( + &mut coder, + observed_multiaddr, + dial_back_cmd_sender, + rng, + all_addrs, + tested_addrs, + data_amount, + ) + .await + .unwrap_or_else(|e| e.into()); coder.send_response(Response::Dial(response)).await?; coder.close().await?; Ok(()) } + +async fn start_handle_request( + stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + client: PeerId, + dial_back_cmd_sender: mpsc::Sender, + mut status_update_sender: mpsc::Sender, + rng: impl RngCore, +) -> Result<(), Arc> { + let mut all_addrs = Vec::new(); + let mut tested_addrs = None; + let mut data_amount = 0; + let res = handle_request( + stream, + observed_multiaddr, + dial_back_cmd_sender, + rng, + &mut all_addrs, + &mut tested_addrs, + &mut data_amount, + ) + .await + .map_err(Arc::new); + let status_update = StatusUpdate { + all_addrs, + tested_addr: tested_addrs, + client, + data_amount, + result: res.as_ref().map_err(Arc::clone).map(|_| ()), + }; + let _ = status_update_sender.send(status_update).await; + res +} diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index 0e947ecdbdd..bc1c2b52c36 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -1,8 +1,15 @@ -use futures::StreamExt; -use libp2p_autonatv2::client::{Report, StatusUpdate}; -use libp2p_core::{multiaddr::Protocol, Multiaddr}; -use libp2p_swarm::{DialError, NetworkBehaviour, Swarm, SwarmEvent}; +use libp2p_autonatv2::client::{self, Config}; +use libp2p_autonatv2::server; +use libp2p_core::transport::TransportError; +use libp2p_core::Multiaddr; +use libp2p_swarm::{ + DialError, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, Swarm, SwarmEvent, +}; use libp2p_swarm_test::SwarmExt; +use rand_core::OsRng; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::oneshot; use tracing_subscriber::EnvFilter; #[tokio::test] @@ -20,40 +27,102 @@ async fn dial_back_to_unsupported_protocol() { .try_init(); let (mut alice, mut bob) = bootstrap().await; - let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + let alice_peer_id = *alice.local_peer_id(); + let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + let bob_test_addr = test_addr.clone(); bob.behaviour_mut() .autonat - .inject_test_addr(test_addr.clone()); + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &test_addr }, + )); - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(_), - .. - }], - [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::GotDialDataReq { - addr: addr_0, - num_bytes: req_num, - }, - .. - })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::CompletedDialData { - addr: addr_1, - num_bytes: done_num, - }, - .. - })), SwarmEvent::ExternalAddrExpired { .. }], - ) => { - assert_eq!(addr_0, test_addr); - assert_eq!(addr_1, test_addr); - assert_eq!(req_num, done_num); + let (bob_done_tx, bob_done_rx) = oneshot::channel(); + + let alice_task = async move { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|e| (e, connection_id)), + _ => None, + }) + .await; + let mut outgoing_conn_error = alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::Transport(transport_errs), + } if connection_id == alice_conn_id && alice_dialing_peer == peer_id => { + Some(transport_errs) + } + _ => None, + }) + .await; + if let Some((multiaddr, TransportError::MultiaddrNotSupported(not_supported_addr))) = + outgoing_conn_error.pop() + { + assert_eq!( + multiaddr, + test_addr.clone().with_p2p(alice_dialing_peer).unwrap() + ); + assert_eq!(not_supported_addr, multiaddr,); + } else { + panic!("Peers are empty"); } - e => panic!("unknown events {e:#?}"), - } + assert_eq!(outgoing_conn_error.len(), 0); + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + all_addrs, + tested_addr: Some(tested_addr), + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![test_addr.clone()] + && tested_addr == test_addr.clone() + && client == alice_dialing_peer => + { + Some(data_amount) + } + _ => None, + }) + .await; + + tokio::select! { + _ = bob_done_rx => { + return data_amount; + } + _ = alice.loop_on_next() => { + unreachable!(); + } + } + }; + + let bob_task = async move { + bob.wait(|event| match event { + SwarmEvent::ExternalAddrExpired { address } if address == bob_test_addr => Some(()), + _ => None, + }) + .await; + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + tested_addr: Some(tested_addr), + data_amount, + server, + result: Err(_), + })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(data_amount), + _ => None, + }) + .await; + bob_done_tx.send(()).unwrap(); + data_amount + }; + let (alice_amount, bob_amount) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_amount, bob_amount); } #[tokio::test] @@ -62,49 +131,99 @@ async fn dial_back_to_non_libp2p() { .with_env_filter(EnvFilter::from_default_env()) .try_init(); let (mut alice, mut bob) = bootstrap().await; + let alice_peer_id = *alice.local_peer_id(); for addr_str in [ - "/dns4/umgefahren.xyz/tcp/809", "/ip4/169.150.247.38/tcp/32", "/ip6/2400:52e0:1e02::1187:1/tcp/1000", ] { let addr: Multiaddr = addr_str.parse().unwrap(); - bob.behaviour_mut().autonat.inject_test_addr(addr.clone()); + let bob_addr = addr.clone(); + bob.behaviour_mut() + .autonat + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { - error: DialError::Transport(mut peers), - .. - }], - [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::GotDialDataReq { - addr: addr_0, - num_bytes: req_num, - }, - .. - })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::CompletedDialData { - addr: addr_1, - num_bytes: done_num, - }, - .. - })), SwarmEvent::ExternalAddrExpired { .. }], - ) => { - let (peer_addr, _) = peers.pop().unwrap(); - let cleaned_addr: Multiaddr = peer_addr - .iter() - .filter(|p| !matches!(p, Protocol::P2p(_))) - .collect(); - assert_eq!(addr, cleaned_addr); - assert_eq!(addr_0, cleaned_addr); - assert_eq!(addr_1, cleaned_addr); - assert_eq!(req_num, done_num); + let alice_task = async move { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|p| (p, connection_id)), + _ => None, + }) + .await; + let mut outgoing_conn_error = alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::Transport(peers), + } if connection_id == alice_conn_id && peer_id == alice_dialing_peer => { + Some(peers) + } + _ => None, + }) + .await; + + if let Some((multiaddr, TransportError::Other(o))) = outgoing_conn_error.pop() { + assert_eq!( + multiaddr, + addr.clone().with_p2p(alice_dialing_peer).unwrap() + ); + let error_string = o.to_string(); + assert!( + error_string.contains("Connection refused"), + "Coorect error string: {error_string}" + ); + } else { + panic!("No outgoing connection errors"); } - e => panic!("unknown events {e:#?}"), - } + + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + all_addrs, + tested_addr: Some(tested_addr), + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![addr.clone()] + && tested_addr == addr + && alice_dialing_peer == client => + { + Some(data_amount) + } + _ => None, + }) + .await; + (alice, data_amount) + }; + let bob_task = async move { + bob.wait(|event| match event { + SwarmEvent::ExternalAddrExpired { address } if address == bob_addr => Some(()), + _ => None, + }) + .await; + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + tested_addr: Some(tested_addr), + data_amount, + server, + result: Err(_), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(data_amount), + _ => None, + }) + .await; + (bob, data_amount) + }; + + let ((a, alice_data_amount), (b, bob_data_amount)) = tokio::join!(alice_task, bob_task); + (alice, bob) = (a, b); + assert_eq!(alice_data_amount, bob_data_amount); } } @@ -115,44 +234,104 @@ async fn dial_back_to_not_supporting() { .try_init(); let (mut alice, mut bob) = bootstrap().await; + let alice_peer_id = *alice.local_peer_id(); - let mut hannes = new_dummy().await; + let (bob_done_tx, bob_done_rx) = oneshot::channel(); + + let hannes = new_dummy().await; + let hannes_peer_id = *hannes.local_peer_id(); let unreachable_address = hannes.external_addresses().next().unwrap().clone(); + let bob_unreachable_address = unreachable_address.clone(); bob.behaviour_mut() .autonat - .inject_test_addr(unreachable_address.clone()); + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { + addr: &unreachable_address, + }, + )); - let handler = tokio::spawn(async move { - loop { - hannes.next_swarm_event().await; - } - }); + let handler = tokio::spawn(async move { hannes.loop_on_next().await }); - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::OutgoingConnectionError { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::GotDialDataReq { - addr: addr_0, - num_bytes: req_num, - }, - .. - })), SwarmEvent::Behaviour(CombinedClientEvent::Autonat(Report { - update: - StatusUpdate::CompletedDialData { - addr: addr_1, - num_bytes: done_num, - }, - .. - })), SwarmEvent::ExternalAddrExpired { .. }], - ) => { - assert_eq!(addr_0, unreachable_address); - assert_eq!(addr_1, unreachable_address); - assert_eq!(req_num, done_num); + let alice_task = async move { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|p| (p, connection_id)), + _ => None, + }) + .await; + alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::WrongPeerId { obtained, .. }, + } if connection_id == alice_conn_id + && peer_id == alice_dialing_peer + && obtained == hannes_peer_id => + { + Some(()) + } + _ => None, + }) + .await; + + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + all_addrs, + tested_addr: Some(tested_addr), + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![unreachable_address.clone()] + && tested_addr == unreachable_address + && alice_dialing_peer == client => + { + Some(data_amount) + } + _ => None, + }) + .await; + tokio::select! { + _ = bob_done_rx => { + return data_amount; + } + _ = alice.loop_on_next() => { + unreachable!(); + } } - e => panic!("unknown events {e:#?}"), - } + }; + + let bob_task = async move { + bob.wait(|event| match event { + SwarmEvent::ExternalAddrExpired { address } if address == bob_unreachable_address => { + Some(()) + } + _ => None, + }) + .await; + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + tested_addr: Some(tested_addr), + data_amount, + server, + result: Err(_), + })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { + Some(data_amount) + } + _ => None, + }) + .await; + bob_done_tx.send(()).unwrap(); + data_amount + }; + + let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_data_amount, bob_data_amount); handler.abort(); } @@ -171,7 +350,10 @@ async fn new_server() -> Swarm { async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { - autonat: libp2p_autonatv2::client::Behaviour::default(), + autonat: libp2p_autonatv2::client::Behaviour::new( + OsRng, + Config::default().with_recheck_interval(Duration::from_nanos(0)), + ), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), identity.public().clone(), @@ -211,44 +393,124 @@ async fn bootstrap() -> (Swarm, Swarm) { let cor_server_peer = alice.local_peer_id().clone(); let mut bob = new_client().await; let cor_client_peer = bob.local_peer_id().clone(); + + let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); + let alice_bob_external_addrs = bob_external_addrs.clone(); + bob.connect(&mut alice).await; - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Behaviour(CombinedServerEvent::Identify(libp2p_identify::Event::Sent { - peer_id: client_peer_sent, - })), SwarmEvent::Behaviour(CombinedServerEvent::Identify( - libp2p_identify::Event::Received { - peer_id: client_peer_recv, + let (tx, rx) = oneshot::channel(); + + let alice_task = async move { + let _ = alice + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), + _ => None, + }) + .await; + + let (dialed_peer_id, dialed_connection_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, .. - }, - )), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::Behaviour(CombinedClientEvent::Identify(libp2p_identify::Event::Sent { - peer_id: server_peer_sent, - })), SwarmEvent::Behaviour(CombinedClientEvent::Identify( - libp2p_identify::Event::Received { - peer_id: server_peer_recv, + } => peer_id.map(|peer_id| (peer_id, connection_id)), + _ => None, + }) + .await; + + assert_eq!(dialed_peer_id, cor_client_peer); + + let _ = alice + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + peer_id, + connection_id, .. - }, - ))], - ) => { - assert_eq!(server_peer_sent, cor_server_peer); - assert_eq!(client_peer_sent, cor_client_peer); - assert_eq!(server_peer_recv, cor_server_peer); - assert_eq!(client_peer_recv, cor_client_peer); - } - e => panic!("unexpected events: {e:#?}"), - } + } if peer_id == dialed_peer_id + && peer_id == cor_client_peer + && connection_id == dialed_connection_id => + { + Some(()) + } + _ => None, + }) + .await; - match libp2p_swarm_test::drive(&mut alice, &mut bob).await { - ( - [SwarmEvent::Dialing { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::Behaviour(CombinedServerEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { .. }], - [SwarmEvent::NewExternalAddrCandidate { address: addr_new }, SwarmEvent::IncomingConnection { .. }, SwarmEvent::ConnectionEstablished { .. }, SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::Behaviour(CombinedClientEvent::Identify(_)), SwarmEvent::NewExternalAddrCandidate { address: addr_snd }, SwarmEvent::ExternalAddrConfirmed { address: addr_ok }], - ) => { - assert_eq!(addr_new, addr_snd); - assert_eq!(addr_snd, addr_ok); - } - e => panic!("unknown events {e:#?}"), - } - (alice, bob) + let server::StatusUpdate { + all_addrs, + tested_addr, + client, + data_amount, + result, + } = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { + Some(status_update) + } + _ => None, + }) + .await; + + assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(client, cor_client_peer); + assert_eq!(&all_addrs[..], &bob_external_addrs[..]); + assert!(result.is_ok(), "Result: {result:?}"); + + rx.await.unwrap(); + alice + }; + + let bob_task = async move { + let address_candidate = bob + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; + let incoming_conn_id = bob + .wait(|event| match event { + SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), + _ => None, + }) + .await; + + let _ = bob.wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }); + + let client::StatusUpdate { + tested_addr, + data_amount, + server, + result, + } = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { + Some(status_update) + } + SwarmEvent::ExternalAddrConfirmed { address } => { + assert_eq!(address, address_candidate); + None + } + _ => None, + }) + .await; + assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(server, cor_server_peer); + assert!(result.is_ok(), "Result is {result:?}"); + + tx.send(()).unwrap(); + bob + }; + + tokio::join!(alice_task, bob_task) } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index aea49c376ab..74725b148e1 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -595,7 +595,9 @@ where } self.behaviour - .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { listener_id })); + .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { + listener_id, + })); Ok(()) } @@ -606,7 +608,9 @@ where /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. pub fn add_external_address(&mut self, a: Multiaddr) { self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &a })); + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &a, + })); self.confirmed_external_addr.insert(a); } @@ -1042,9 +1046,9 @@ where ); let addrs = self.listened_addrs.remove(&listener_id).unwrap_or_default(); for addr in addrs.iter() { - self.behaviour.on_swarm_event( - FromSwarm::ExpiredListenAddr(ExpiredListenAddr { listener_id, addr }) - ); + self.behaviour.on_swarm_event(FromSwarm::ExpiredListenAddr( + ExpiredListenAddr { listener_id, addr }, + )); } self.behaviour .on_swarm_event(FromSwarm::ListenerClosed(ListenerClosed { @@ -2131,9 +2135,9 @@ mod tests { {} match swarm2.poll_next_unpin(cx) { - Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { peer_id, error, .. })) => { - Poll::Ready((peer_id, error)) - } + Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { + peer_id, error, .. + })) => Poll::Ready((peer_id, error)), Poll::Ready(x) => panic!("unexpected {x:?}"), Poll::Pending => Poll::Pending, } @@ -2199,7 +2203,9 @@ mod tests { return Poll::Ready(Ok(())); } } - Poll::Ready(Some(SwarmEvent::IncomingConnectionError { local_addr, .. })) => { + Poll::Ready(Some(SwarmEvent::IncomingConnectionError { + local_addr, .. + })) => { assert!(!got_inc_err); assert_eq!(local_addr, local_address); got_inc_err = true; @@ -2320,9 +2326,10 @@ mod tests { // This constitutes a fairly typical error for chained transports. let error = DialError::Transport(vec![( "/ip4/127.0.0.1/tcp/80".parse().unwrap(), - TransportError::Other( - io::Error::new(io::ErrorKind::Other, MemoryTransportError::Unreachable) - ), + TransportError::Other(io::Error::new( + io::ErrorKind::Other, + MemoryTransportError::Unreachable, + )), )]); let string = format!("{error}"); diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 79f95d2df29..fec7b0802c5 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -802,7 +802,9 @@ mod tests { .unwrap() ), Ok(SocketAddr::new( - IpAddr::V6(Ipv6Addr::new(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,)), + IpAddr::V6(Ipv6Addr::new( + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + )), 8080, )) ); @@ -1275,11 +1277,10 @@ mod tests { #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); assert!(rt.block_on(cycle_listeners::())); } } @@ -1312,11 +1313,10 @@ mod tests { } #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); rt.block_on(async { test::(); }); From da2a7909a32e336705d78da7a43f7532a4f31805 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 22 Dec 2023 20:51:50 +0100 Subject: [PATCH 046/179] Implement all suggestions from the PR review --- protocols/autonatv2/Cargo.toml | 8 +- protocols/autonatv2/src/client/behaviour.rs | 37 ++--- protocols/autonatv2/src/client/handler.rs | 3 - .../autonatv2/src/client/handler/dial_back.rs | 28 ++-- .../src/client/handler/dial_request.rs | 42 +++--- protocols/autonatv2/src/lib.rs | 7 +- protocols/autonatv2/src/request_response.rs | 127 +++++++++++------- protocols/autonatv2/src/server/behaviour.rs | 24 +--- .../autonatv2/src/server/handler/dial_back.rs | 26 +--- .../src/server/handler/dial_request.rs | 26 ++-- 10 files changed, 149 insertions(+), 179 deletions(-) diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml index 55b778c6ef9..4944502c3e1 100644 --- a/protocols/autonatv2/Cargo.toml +++ b/protocols/autonatv2/Cargo.toml @@ -13,7 +13,7 @@ quick-protobuf-codec = { workspace = true } asynchronous-codec = "0.7" libp2p-core = { workspace = true } rand_core = "0.6" -rand = { version = "0.8", optional = true } +rand = "0.8" libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } futures-bounded = { workspace = true } @@ -28,7 +28,7 @@ unsigned-varint = { workspace = true, features = ["futures"] } futures-timer = "3.0.2" [dev-dependencies] -tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] } +tokio = { version = "1", features = ["macros", "rt", "sync"] } libp2p-swarm-test = { workspace = true } libp2p-identify = { workspace = true } libp2p-swarm = { workspace = true, features = ["macros"] } @@ -36,7 +36,3 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"]} [lints] workspace = true - -[features] -default = ["rand"] -rand = ["dep:rand"] diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 47f6ece4d07..859159dcb81 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -22,7 +22,7 @@ use crate::{global_only::IpExt, request_response::DialRequest}; use super::handler::{ dial_back, dial_request::{self, StatusUpdate}, - Handler, TestEnd, + TestEnd, }; #[derive(Debug, Clone, Copy)] @@ -89,7 +89,7 @@ impl NetworkBehaviour for Behaviour where R: RngCore + 'static, { - type ConnectionHandler = Handler; + type ConnectionHandler = Either; type ToSwarm = StatusUpdate; @@ -123,9 +123,7 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - if !self.already_tested.contains(addr) { - *self.address_candidates.entry(addr.clone()).or_default() += 1; - } + *self.address_candidates.entry(addr.clone()).or_default() += 1; } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { self.address_candidates.remove(addr); @@ -144,7 +142,6 @@ where connection_id, .. }) => { - tracing::trace!("connection with {peer_id:?} closed"); self.handle_no_connection(peer_id, connection_id); } FromSwarm::DialFailure(DialFailure { @@ -163,7 +160,7 @@ where &mut self, peer_id: PeerId, connection_id: ConnectionId, - event: ::ToBehaviour, + event: ::ToBehaviour, ) { if matches!(event, Either::Left(_)) { self.peers_to_handlers @@ -171,16 +168,13 @@ where .or_insert(connection_id); } match event { - Either::Right(Ok(nonce)) => { + Either::Right(nonce) => { if self.pending_nonces.remove(&nonce) { tracing::trace!("Received pending nonce from {peer_id:?}"); } else { tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); } } - Either::Right(Err(err)) => { - tracing::debug!("Dial back failed: {:?}", err); - } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { if !self.known_servers.contains(&peer_id) { self.known_servers.push(peer_id); @@ -228,7 +222,8 @@ where fn poll( &mut self, cx: &mut Context<'_>, - ) -> Poll::FromBehaviour>> { + ) -> Poll::FromBehaviour>> + { let pending_event = self.poll_pending_events(); if pending_event.is_ready() { return pending_event; @@ -237,12 +232,19 @@ where && !self.known_servers.is_empty() && !self.address_candidates.is_empty() { - let mut entries = self.address_candidates.drain().collect::>(); + let mut entries = self + .address_candidates + .iter() + .filter(|(addr, _)| !self.already_tested.contains(addr)) + .collect::>(); + if entries.is_empty() { + return Poll::Pending; + } entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries .into_iter() .rev() - .map(|(addr, _)| addr) + .map(|(addr, _)| addr.clone()) .take(self.config.max_addrs_count) .collect::>(); self.already_tested.extend(addrs.iter().cloned()); @@ -296,7 +298,7 @@ where self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, handler: NotifyHandler::One(*conn_id), - event: Either::Left(dial_request::FromBehaviour::PerformRequest(req)), + event: Either::Left(req), }); } else { tracing::debug!( @@ -317,7 +319,10 @@ where fn poll_pending_events( &mut self, ) -> Poll< - ToSwarm<::ToSwarm, ::FromBehaviour>, + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, > { if let Some(event) = self.pending_events.pop_front() { return Poll::Ready(event); diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 0776e45e5bd..711a6599559 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,7 +1,6 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; -use either::Either; use std::{ fmt::{Display, Formatter}, sync::Arc, @@ -15,8 +14,6 @@ use self::dial_request::InternalError; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; -pub(crate) type Handler = Either; - #[derive(Clone, Debug)] pub struct Error { pub(crate) internal: Arc, diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 906ba6a35e8..08704e2e13c 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,5 +1,4 @@ use std::{ - convert::identity, io, task::{Context, Poll}, }; @@ -11,13 +10,12 @@ use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; +use void::Void; -use crate::{request_response::DialBack, Nonce}; +use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; -pub(crate) type ToBehaviour = io::Result; - pub struct Handler { inbound: FuturesSet>, } @@ -31,15 +29,15 @@ impl Handler { } impl ConnectionHandler for Handler { - type FromBehaviour = (); - type ToBehaviour = ToBehaviour; + type FromBehaviour = Void; + type ToBehaviour = Nonce; type InboundProtocol = ReadyUpgrade; type OutboundProtocol = DeniedUpgrade; type InboundOpenInfo = (); type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(crate::DIAL_BACK_UPGRADE, ()) + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()) } fn poll( @@ -49,11 +47,13 @@ impl ConnectionHandler for Handler { ConnectionHandlerEvent, > { if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - result - .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) - .and_then(identity), - )); + match result { + Ok(Ok(nonce)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) + } + Ok(Err(err)) => tracing::debug!("Dial back handler failed with: {err:?}"), + Err(err) => tracing::debug!("Dial back handler timed out with: {err:?}"), + } } Poll::Pending } @@ -83,10 +83,6 @@ impl ConnectionHandler for Handler { _ => {} } } - - fn connection_keep_alive(&self) -> bool { - false - } } async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index d3a13218ace..db152af63d8 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -33,7 +33,7 @@ use crate::{ DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, - REQUEST_PROTOCOL_NAME, REQUEST_UPGRADE, + REQUEST_PROTOCOL_NAME, }; use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; @@ -89,11 +89,6 @@ pub struct StatusUpdate { pub result: Result<(), crate::client::handler::Error>, } -#[derive(Debug)] -pub enum FromBehaviour { - PerformRequest(DialRequest), -} - pub struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< @@ -134,7 +129,7 @@ impl Handler { self.queued_streams.push_back(tx); self.queued_events .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(REQUEST_UPGRADE, ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()), }); if self .outbound @@ -152,16 +147,11 @@ impl Handler { } impl ConnectionHandler for Handler { - type FromBehaviour = FromBehaviour; - + type FromBehaviour = DialRequest; type ToBehaviour = ToBehaviour; - type InboundProtocol = DeniedUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { @@ -196,11 +186,7 @@ impl ConnectionHandler for Handler { } fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - match event { - FromBehaviour::PerformRequest(req) => { - self.perform_request(req); - } - } + self.perform_request(event); } fn on_connection_event( @@ -272,6 +258,7 @@ async fn start_substream_handle( let mut data_amount = 0; let mut checked_addr_idx = None; let addrs = dial_request.addrs.clone(); + assert_ne!(addrs, vec![]); let res = handle_substream( dial_request, substream, @@ -298,10 +285,8 @@ async fn handle_substream( checked_addr_idx: &mut Option, ) -> Result { let mut coder = Coder::new(substream); - coder - .send_request(Request::Dial(dial_request.clone())) - .await?; - match coder.next_response().await? { + coder.send(Request::Dial(dial_request.clone())).await?; + match coder.next().await? { Response::Data(DialDataRequest { addr_idx, num_bytes, @@ -326,7 +311,7 @@ async fn handle_substream( } *checked_addr_idx = Some(addr_idx); send_aap_data(&mut coder, num_bytes, data_amount).await?; - if let Response::Dial(dial_response) = coder.next_response().await? { + if let Response::Dial(dial_response) = coder.next().await? { *checked_addr_idx = Some(dial_response.addr_idx); coder.close().await?; test_end_from_dial_response(dial_request, dial_response) @@ -396,10 +381,17 @@ where .take(count_full) .chain(once(partial_len)) .filter(|e| *e > 0) - .map(|data_count| (data_count, Request::Data(DialDataResponse { data_count }))) + .map(|data_count| { + ( + data_count, + Request::Data( + DialDataResponse::new(data_count).expect("data count is unexpectedly too big"), + ), + ) + }) { *data_amount += data_count; - substream.send_request(req).await?; + substream.send(req).await?; } Ok(()) } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index 00023a8b9c3..cca22be465c 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -1,4 +1,3 @@ -use libp2p_core::upgrade::ReadyUpgrade; use libp2p_swarm::StreamProtocol; pub mod client; @@ -11,9 +10,5 @@ pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); pub(crate) const DIAL_BACK_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-back"); -pub(crate) const REQUEST_UPGRADE: ReadyUpgrade = - ReadyUpgrade::new(REQUEST_PROTOCOL_NAME); -pub(crate) const DIAL_BACK_UPGRADE: ReadyUpgrade = - ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME); -pub type Nonce = u64; +type Nonce = u64; diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index 5716863dee6..c92003eba8a 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -18,15 +18,13 @@ pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; -macro_rules! new_io_invalid_data_err { - ($msg:expr) => { - io::Error::new(io::ErrorKind::InvalidData, $msg) - }; +fn new_io_invalid_data_err(msg: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, msg.into()) } -macro_rules! check_existence { +macro_rules! ok_or_invalid_data { ($field:ident) => { - $field.ok_or_else(|| new_io_invalid_data_err!(concat!(stringify!($field), " is missing"))) + $field.ok_or_else(|| new_io_invalid_data_err(concat!(stringify!($field), " is missing"))) }; } @@ -43,9 +41,8 @@ where inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), } } - pub(crate) async fn close(self) -> io::Result<()> { - let mut stream = self.inner.into_inner(); - stream.close().await?; + pub(crate) async fn close(mut self) -> io::Result<()> { + self.inner.close().await?; Ok(()) } } @@ -54,37 +51,37 @@ impl Coder where I: AsyncRead + Unpin, { + pub(crate) async fn next(&mut self) -> io::Result + where + proto::Message: TryInto, + io::Error: From, + { + Ok(self.next_msg().await?.try_into()?) + } + async fn next_msg(&mut self) -> io::Result { self.inner .next() .await - .ok_or(io::Error::new(ErrorKind::Other, "no request to read"))? + .ok_or(io::Error::new( + ErrorKind::UnexpectedEof, + "no request to read", + ))? .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) } - pub(crate) async fn next_request(&mut self) -> io::Result { - Request::from_proto(self.next_msg().await?) - } - pub(crate) async fn next_response(&mut self) -> io::Result { - Response::from_proto(self.next_msg().await?) - } } impl Coder where I: AsyncWrite + Unpin, { - async fn send_msg(&mut self, msg: proto::Message) -> io::Result<()> { - self.inner.send(msg).await?; + pub(crate) async fn send(&mut self, msg: M) -> io::Result<()> + where + M: Into, + { + self.inner.send(msg.into()).await?; Ok(()) } - - pub(crate) async fn send_request(&mut self, request: Request) -> io::Result<()> { - self.send_msg(request.into_proto()).await - } - - pub(crate) async fn send_response(&mut self, response: Response) -> io::Result<()> { - self.send_msg(response.into_proto()).await - } } #[derive(Debug, Clone, PartialEq)] @@ -101,36 +98,54 @@ pub struct DialRequest { #[derive(Debug, Clone, PartialEq)] pub(crate) struct DialDataResponse { - pub(crate) data_count: usize, + data_count: usize, } -impl Request { - pub(crate) fn from_proto(msg: proto::Message) -> io::Result { +impl DialDataResponse { + pub(crate) fn new(data_count: usize) -> Option { + if data_count <= DATA_FIELD_LEN_UPPER_BOUND { + Some(Self { data_count }) + } else { + None + } + } + + pub(crate) fn get_data_count(&self) -> usize { + self.data_count + } +} + +impl TryFrom for Request { + type Error = io::Error; + + fn try_from(msg: proto::Message) -> Result { match msg.msg { proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { - let addrs: Vec = addrs + let addrs = addrs .into_iter() .map(|e| e.to_vec()) .map(|e| { Multiaddr::try_from(e).map_err(|err| { - new_io_invalid_data_err!(format!("invalid multiaddr: {}", err)) + new_io_invalid_data_err(format!("invalid multiaddr: {}", err)) }) }) .collect::, io::Error>>()?; - let nonce = check_existence!(nonce)?; + let nonce = ok_or_invalid_data!(nonce)?; Ok(Self::Dial(DialRequest { addrs, nonce })) } proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { - let data_count = check_existence!(data)?.len(); + let data_count = ok_or_invalid_data!(data)?.len(); Ok(Self::Data(DialDataResponse { data_count })) } - _ => Err(new_io_invalid_data_err!( - "invalid message type, expected dialRequest or dialDataResponse" + _ => Err(new_io_invalid_data_err( + "expected dialResponse or dialDataRequest", )), } } +} - pub(crate) fn into_proto(self) -> proto::Message { +impl Into for Request { + fn into(self) -> proto::Message { match self { Request::Dial(DialRequest { addrs, nonce }) => { let addrs = addrs.iter().map(|e| e.to_vec()).collect(); @@ -177,18 +192,20 @@ pub(crate) struct DialResponse { pub(crate) dial_status: proto::DialStatus, } -impl Response { - pub(crate) fn from_proto(msg: proto::Message) -> std::io::Result { +impl TryFrom for Response { + type Error = io::Error; + + fn try_from(msg: proto::Message) -> Result { match msg.msg { proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { status, addrIdx, dialStatus, }) => { - let status = check_existence!(status)?; - let addr_idx = check_existence!(addrIdx)? as usize; - let dial_status = check_existence!(dialStatus)?; - Ok(Self::Dial(DialResponse { + let status = ok_or_invalid_data!(status)?; + let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; + let dial_status = ok_or_invalid_data!(dialStatus)?; + Ok(Response::Dial(DialResponse { status, addr_idx, dial_status, @@ -198,20 +215,22 @@ impl Response { addrIdx, numBytes, }) => { - let addr_idx = check_existence!(addrIdx)? as usize; - let num_bytes = check_existence!(numBytes)? as usize; + let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; + let num_bytes = ok_or_invalid_data!(numBytes)? as usize; Ok(Self::Data(DialDataRequest { addr_idx, num_bytes, })) } - _ => Err(new_io_invalid_data_err!( - "invalid message type, expected dialResponse or dialDataRequest" + _ => Err(new_io_invalid_data_err( + "invalid message type, expected dialResponse or dialDataRequest", )), } } +} - pub(crate) fn into_proto(self) -> proto::Message { +impl Into for Response { + fn into(self) -> proto::Message { match self { Self::Dial(DialResponse { status, @@ -260,11 +279,11 @@ impl DialBack { .next() .await .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; - let nonce = check_existence!(nonce)?; + let nonce = ok_or_invalid_data!(nonce)?; Ok(Self { nonce }) } - pub(crate) async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { + async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = proto::DialBack { nonce: Some(self.nonce), }; @@ -275,6 +294,12 @@ impl DialBack { } } +pub(crate) async fn write_nonce(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let dial_back = DialBack { nonce }; + dial_back.write_into(&mut stream).await?; + stream.close().await +} + #[cfg(test)] mod tests { use crate::generated::structs::{ @@ -323,13 +348,13 @@ mod tests { data_count: thread_rng().gen_range(0..4000), }); all_req.push(data_request.clone()); - coder.send_request(data_request.clone()).await.unwrap(); + coder.send(data_request.clone()).await.unwrap(); } let inner = coder.inner.into_inner(); inner.set_position(0); let mut coder = Coder::new(inner); for i in 0..100 { - let read_data_request = coder.next_request().await.unwrap(); + let read_data_request: Request = coder.next().await.unwrap(); assert_eq!(read_data_request, all_req[i]); } } diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 9480a113571..fce414fe667 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; -use super::handler::{ +use crate::server::handler::{ dial_back, dial_request::{self, DialBackCommand}, Handler, @@ -51,21 +51,6 @@ where rng, } } - - fn poll_pending_events( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - > { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - Poll::Pending - } } impl NetworkBehaviour for Behaviour @@ -154,11 +139,10 @@ where fn poll( &mut self, - cx: &mut Context<'_>, + _cx: &mut Context<'_>, ) -> Poll as ConnectionHandler>::FromBehaviour>> { - let pending_event = self.poll_pending_events(cx); - if pending_event.is_ready() { - return pending_event; + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); } Poll::Pending } diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 8a497dc1f84..682a8b19615 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use futures::{AsyncWrite, AsyncWriteExt}; +use futures::AsyncWrite; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_UPGRADE}; +use crate::{request_response::write_nonce, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -70,7 +70,7 @@ impl ConnectionHandler for Handler { if let Some(cmd) = self.pending_nonce.take() { self.requested_substream_nonce = Some(cmd); return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(DIAL_BACK_UPGRADE, ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()), }); } Poll::Pending @@ -104,11 +104,7 @@ impl ConnectionHandler for Handler { } } ConnectionEvent::DialUpgradeError(DialUpgradeError { - error: StreamUpgradeError::NegotiationFailed, - .. - }) - | ConnectionEvent::DialUpgradeError(DialUpgradeError { - error: StreamUpgradeError::Timeout, + error: StreamUpgradeError::NegotiationFailed | StreamUpgradeError::Timeout, .. }) => { if let Some(cmd) = self.requested_substream_nonce.take() { @@ -121,14 +117,14 @@ impl ConnectionHandler for Handler { } async fn perform_dial_back( - mut stream: impl AsyncWrite + Unpin, + stream: impl AsyncWrite + Unpin, DialBackCommand { nonce, back_channel, .. }: DialBackCommand, ) -> io::Result<()> { - let res = perform_dial_back_inner(&mut stream, nonce) + let res = write_nonce(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| DialBackRes::Ok) @@ -138,13 +134,3 @@ async fn perform_dial_back( .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; Ok(()) } - -async fn perform_dial_back_inner( - mut stream: impl AsyncWrite + Unpin, - nonce: Nonce, -) -> io::Result<()> { - let dial_back = DialBack { nonce }; - dial_back.write_into(&mut stream).await?; - stream.close().await?; - Ok(()) -} diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index db1e6b284e6..92880744a67 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -22,14 +22,12 @@ use libp2p_swarm::{ }; use rand_core::RngCore; -use crate::request_response::Coder; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{ - DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, - }, - Nonce, REQUEST_UPGRADE, + request_response::{DialDataRequest, DialRequest, DialResponse, Request, Response}, + Nonce, }; +use crate::{request_response::Coder, REQUEST_PROTOCOL_NAME}; #[derive(Clone, Debug)] pub struct StatusUpdate { @@ -104,7 +102,7 @@ where type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(REQUEST_UPGRADE, ()) + SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()) } fn poll( @@ -178,10 +176,6 @@ where _ => {} } } - - fn connection_keep_alive(&self) -> bool { - true - } } enum HandleFail { @@ -235,7 +229,7 @@ where I: AsyncRead + AsyncWrite + Unpin, { let DialRequest { mut addrs, nonce } = match coder - .next_request() + .next() .await .map_err(|_| HandleFail::InternalError(0))? { @@ -253,19 +247,19 @@ where let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); let mut rem_data = dial_data_request.num_bytes; coder - .send_response(Response::Data(dial_data_request)) + .send(Response::Data(dial_data_request)) .await .map_err(|_| HandleFail::InternalError(idx))?; while rem_data > 0 { - let DialDataResponse { data_count } = match coder - .next_request() + let data_count = match coder + .next() .await .map_err(|_e| HandleFail::InternalError(idx))? { Request::Dial(_) => { return Err(HandleFail::RequestRejected); } - Request::Data(dial_data_response) => dial_data_response, + Request::Data(dial_data_response) => dial_data_response.get_data_count(), }; rem_data = rem_data.saturating_sub(data_count); *data_amount += data_count; @@ -322,7 +316,7 @@ async fn handle_request( ) .await .unwrap_or_else(|e| e.into()); - coder.send_response(Response::Dial(response)).await?; + coder.send(Response::Dial(response)).await?; coder.close().await?; Ok(()) } From 101a3251efdb79a7e5c4ad072e270f35e9958874 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 17:17:55 +0100 Subject: [PATCH 047/179] Implement latest suggestions --- protocols/autonatv2/src/client.rs | 2 +- protocols/autonatv2/src/client/behaviour.rs | 69 +++++- protocols/autonatv2/src/client/handler.rs | 41 +--- .../src/client/handler/dial_request.rs | 33 +-- protocols/autonatv2/src/request_response.rs | 12 +- protocols/autonatv2/src/server.rs | 2 +- protocols/autonatv2/src/server/behaviour.rs | 19 +- .../autonatv2/src/server/handler/dial_back.rs | 4 +- .../src/server/handler/dial_request.rs | 16 +- protocols/autonatv2/tests/autonatv2.rs | 227 ++++++++++++------ 10 files changed, 263 insertions(+), 162 deletions(-) diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonatv2/src/client.rs index fc15af146f3..d3272512f35 100644 --- a/protocols/autonatv2/src/client.rs +++ b/protocols/autonatv2/src/client.rs @@ -1,5 +1,5 @@ mod behaviour; mod handler; +pub use behaviour::Event; pub use behaviour::{Behaviour, Config}; -pub use handler::dial_request::StatusUpdate; diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 859159dcb81..49e0a0a15c9 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -16,12 +16,15 @@ use libp2p_swarm::{ }; use rand::{seq::SliceRandom, Rng}; use rand_core::{OsRng, RngCore}; +use std::fmt::{Debug, Display, Formatter}; +use std::sync::Arc; +use crate::client::handler::dial_request::InternalError; use crate::{global_only::IpExt, request_response::DialRequest}; use super::handler::{ dial_back, - dial_request::{self, StatusUpdate}, + dial_request::{self}, TestEnd, }; @@ -91,7 +94,7 @@ where { type ConnectionHandler = Either; - type ToSwarm = StatusUpdate; + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -337,6 +340,68 @@ impl Default for Behaviour { } } +pub struct Error { + pub(crate) internal: Arc, +} + +impl Error { + pub(crate) fn duplicate(&self) -> Self { + Self { + internal: Arc::clone(&self.internal), + } + } +} + +impl From for Error { + fn from(value: InternalError) -> Self { + Self { + internal: Arc::new(value), + } + } +} + +impl From> for Error { + fn from(value: Arc) -> Self { + Self { internal: value } + } +} + +impl From<&Arc> for Error { + fn from(value: &Arc) -> Self { + Self { + internal: Arc::clone(value), + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.internal, f) + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.internal, f) + } +} + +#[derive(Debug)] +pub struct Event { + /// The address that was selected for testing. + /// Is `None` in the case that the server respond with something unexpected. + pub tested_addr: Option, + /// The amount of data that was sent to the server. + /// Is 0 if it wasn't necessary to send any data. + /// Otherwise it's a number between 30.000 and 100.000. + pub data_amount: usize, + /// The peer id of the server that was selected for testing. + pub server: PeerId, + /// The result of the test. If the test was successful, this is `Ok(())`. + /// Otherwise it's an error. + pub result: Result<(), Error>, +} + fn addr_is_local(addr: &Multiaddr) -> bool { addr.iter().any(|c| match c { Protocol::Dns(addr) diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index 711a6599559..ce1300a6a8b 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,48 +1,9 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; -use std::{ - fmt::{Display, Formatter}, - sync::Arc, - time::Duration, -}; +use std::time::Duration; pub(crate) use dial_request::TestEnd; -use self::dial_request::InternalError; - const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CONCURRENT_REQUESTS: usize = 10; - -#[derive(Clone, Debug)] -pub struct Error { - pub(crate) internal: Arc, -} - -impl From for Error { - fn from(value: InternalError) -> Self { - Self { - internal: Arc::new(value), - } - } -} - -impl From> for Error { - fn from(value: Arc) -> Self { - Self { internal: value } - } -} - -impl From<&Arc> for Error { - fn from(value: &Arc) -> Self { - Self { - internal: Arc::clone(value), - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.internal.fmt(f) - } -} diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index db152af63d8..f11eac4edac 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -26,11 +26,12 @@ use std::{ task::{Context, Poll}, }; -use crate::request_response::Coder; +use crate::client::behaviour::Error; use crate::{ + client::behaviour::Event, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, request_response::{ - DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, REQUEST_PROTOCOL_NAME, @@ -76,19 +77,11 @@ pub struct TestEnd { #[derive(Debug)] pub enum ToBehaviour { - TestCompleted(Result), - StatusUpdate(StatusUpdate), + TestCompleted(Result), + StatusUpdate(Event), PeerHasServerSupport, } -#[derive(Debug)] -pub struct StatusUpdate { - pub tested_addr: Option, - pub data_amount: usize, - pub server: PeerId, - pub result: Result<(), crate::client::handler::Error>, -} - pub struct Handler { queued_events: VecDeque< ConnectionHandlerEvent< @@ -97,7 +90,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet>, queued_streams: VecDeque< oneshot::Sender< Result< @@ -106,8 +99,8 @@ pub struct Handler { >, >, >, - status_update_rx: mpsc::Receiver, - status_update_tx: mpsc::Sender, + status_update_rx: mpsc::Receiver, + status_update_tx: mpsc::Sender, server: PeerId, } @@ -248,8 +241,8 @@ async fn start_substream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, - mut status_update_tx: mpsc::Sender, -) -> Result { + mut status_update_tx: mpsc::Sender, +) -> Result { let substream = match substream_recv.await { Ok(Ok(substream)) => substream, Ok(Err(err)) => return Err(InternalError::from(err).into()), @@ -267,12 +260,12 @@ async fn start_substream_handle( ) .await .map_err(Arc::new) - .map_err(crate::client::handler::Error::from); - let status_update = StatusUpdate { + .map_err(crate::client::behaviour::Error::from); + let status_update = Event { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), data_amount, server, - result: res.as_ref().map(|_| ()).map_err(|e| e.clone()), + result: res.as_ref().map(|_| ()).map_err(|e| e.duplicate()), }; let _ = status_update_tx.send(status_update).await; res diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/request_response.rs index c92003eba8a..026d7c57c5c 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/request_response.rs @@ -266,6 +266,12 @@ impl DialDataRequest { } } +pub(crate) async fn dial_back(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let dial_back = DialBack { nonce }; + dial_back.write_into(&mut stream).await?; + stream.close().await +} + const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { @@ -294,12 +300,6 @@ impl DialBack { } } -pub(crate) async fn write_nonce(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let dial_back = DialBack { nonce }; - dial_back.write_into(&mut stream).await?; - stream.close().await -} - #[cfg(test)] mod tests { use crate::generated::structs::{ diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonatv2/src/server.rs index ef746607559..e864893d73d 100644 --- a/protocols/autonatv2/src/server.rs +++ b/protocols/autonatv2/src/server.rs @@ -2,4 +2,4 @@ mod behaviour; mod handler; pub use behaviour::Behaviour; -pub use handler::dial_request::StatusUpdate; +pub use behaviour::StatusUpdate; diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index fce414fe667..0a7bee427df 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -1,5 +1,6 @@ use std::{ collections::{HashMap, VecDeque}, + io, task::{Context, Poll}, }; @@ -13,6 +14,7 @@ use libp2p_swarm::{ }; use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; +use std::sync::Arc; use crate::server::handler::{ dial_back, @@ -59,7 +61,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = crate::server::handler::dial_request::StatusUpdate; + type ToSwarm = StatusUpdate; fn handle_established_inbound_connection( &mut self, @@ -147,3 +149,18 @@ where Poll::Pending } } + +#[derive(Debug)] +pub struct StatusUpdate { + /// All address that were submitted for testing. + pub all_addrs: Vec, + /// The address that was eventually tested. + /// This is `None` if the client send and unexpected message. + pub tested_addr: Option, + /// The peer id of the client that submitted addresses for testing. + pub client: PeerId, + /// The amount of data that was requested by the server and was transmitted. + pub data_amount: usize, + /// The result of the test. + pub result: Result<(), Arc>, +} diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 682a8b19615..06e7d36f305 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{request_response::write_nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::{request_response::dial_back, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -124,7 +124,7 @@ async fn perform_dial_back( .. }: DialBackCommand, ) -> io::Result<()> { - let res = write_nonce(stream, nonce) + let res = dial_back(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| DialBackRes::Ok) diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 92880744a67..6d660f31ad9 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -24,19 +24,10 @@ use rand_core::RngCore; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{DialDataRequest, DialRequest, DialResponse, Request, Response}, - Nonce, + request_response::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, + server::behaviour::StatusUpdate, + Nonce, REQUEST_PROTOCOL_NAME, }; -use crate::{request_response::Coder, REQUEST_PROTOCOL_NAME}; - -#[derive(Clone, Debug)] -pub struct StatusUpdate { - pub all_addrs: Vec, - pub tested_addr: Option, - pub client: PeerId, - pub data_amount: usize, - pub result: Result<(), Arc>, -} #[derive(Debug, PartialEq)] pub(crate) enum DialBackStatus { @@ -280,7 +271,6 @@ where err: DialBackStatus::DialErr, })?; - // TODO: add timeout let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; if dial_back != DialBackStatus::Ok { return Err(HandleFail::DialBack { diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index bc1c2b52c36..3024c801660 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -17,7 +17,119 @@ async fn confirm_successful() { let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); - let (_alice, _bob) = bootstrap().await; + let (mut alice, mut bob) = start_and_connect().await; + + let cor_server_peer = alice.local_peer_id().clone(); + let cor_client_peer = bob.local_peer_id().clone(); + let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); + let alice_bob_external_addrs = bob_external_addrs.clone(); + + let alice_task = async { + let _ = alice + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), + _ => None, + }) + .await; + + let (dialed_peer_id, dialed_connection_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + .. + } => peer_id.map(|peer_id| (peer_id, connection_id)), + _ => None, + }) + .await; + + assert_eq!(dialed_peer_id, cor_client_peer); + + let _ = alice + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + peer_id, + connection_id, + .. + } if peer_id == dialed_peer_id + && peer_id == cor_client_peer + && connection_id == dialed_connection_id => + { + Some(()) + } + _ => None, + }) + .await; + + let server::StatusUpdate { + all_addrs, + tested_addr, + client, + data_amount, + result, + } = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { + Some(status_update) + } + _ => None, + }) + .await; + + assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(client, cor_client_peer); + assert_eq!(&all_addrs[..], &bob_external_addrs[..]); + assert!(result.is_ok(), "Result: {result:?}"); + }; + + let bob_task = async { + let address_candidate = bob + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; + let incoming_conn_id = bob + .wait(|event| match event { + SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), + _ => None, + }) + .await; + + let _ = bob.wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }); + + let client::Event { + tested_addr, + data_amount, + server, + result, + } = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { + Some(status_update) + } + SwarmEvent::ExternalAddrConfirmed { address } => { + assert_eq!(address, address_candidate); + None + } + _ => None, + }) + .await; + assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); + assert_eq!(data_amount, 0); + assert_eq!(server, cor_server_peer); + assert!(result.is_ok(), "Result is {result:?}"); + }; + + tokio::join!(alice_task, bob_task); } #[tokio::test] @@ -39,7 +151,7 @@ async fn dial_back_to_unsupported_protocol() { let (bob_done_tx, bob_done_rx) = oneshot::channel(); - let alice_task = async move { + let alice_task = async { let (alice_dialing_peer, alice_conn_id) = alice .wait(|event| match event { SwarmEvent::Dialing { @@ -101,7 +213,7 @@ async fn dial_back_to_unsupported_protocol() { } }; - let bob_task = async move { + let bob_task = async { bob.wait(|event| match event { SwarmEvent::ExternalAddrExpired { address } if address == bob_test_addr => Some(()), _ => None, @@ -109,7 +221,7 @@ async fn dial_back_to_unsupported_protocol() { .await; let data_amount = bob .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), data_amount, server, @@ -145,7 +257,7 @@ async fn dial_back_to_non_libp2p() { NewExternalAddrCandidate { addr: &addr }, )); - let alice_task = async move { + let alice_task = async { let (alice_dialing_peer, alice_conn_id) = alice .wait(|event| match event { SwarmEvent::Dialing { @@ -199,9 +311,9 @@ async fn dial_back_to_non_libp2p() { _ => None, }) .await; - (alice, data_amount) + data_amount }; - let bob_task = async move { + let bob_task = async { bob.wait(|event| match event { SwarmEvent::ExternalAddrExpired { address } if address == bob_addr => Some(()), _ => None, @@ -209,7 +321,7 @@ async fn dial_back_to_non_libp2p() { .await; let data_amount = bob .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), data_amount, server, @@ -218,11 +330,10 @@ async fn dial_back_to_non_libp2p() { _ => None, }) .await; - (bob, data_amount) + data_amount }; - let ((a, alice_data_amount), (b, bob_data_amount)) = tokio::join!(alice_task, bob_task); - (alice, bob) = (a, b); + let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); assert_eq!(alice_data_amount, bob_data_amount); } } @@ -250,9 +361,9 @@ async fn dial_back_to_not_supporting() { }, )); - let handler = tokio::spawn(async move { hannes.loop_on_next().await }); + let handler = tokio::spawn(async { hannes.loop_on_next().await }); - let alice_task = async move { + let alice_task = async { let (alice_dialing_peer, alice_conn_id) = alice .wait(|event| match event { SwarmEvent::Dialing { @@ -305,7 +416,7 @@ async fn dial_back_to_not_supporting() { } }; - let bob_task = async move { + let bob_task = async { bob.wait(|event| match event { SwarmEvent::ExternalAddrExpired { address } if address == bob_unreachable_address => { Some(()) @@ -315,7 +426,7 @@ async fn dial_back_to_not_supporting() { .await; let data_amount = bob .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::StatusUpdate { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), data_amount, server, @@ -388,20 +499,21 @@ async fn new_dummy() -> Swarm { node } -async fn bootstrap() -> (Swarm, Swarm) { +async fn start_and_connect() -> (Swarm, Swarm) { let mut alice = new_server().await; - let cor_server_peer = alice.local_peer_id().clone(); let mut bob = new_client().await; - let cor_client_peer = bob.local_peer_id().clone(); - - let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); - let alice_bob_external_addrs = bob_external_addrs.clone(); bob.connect(&mut alice).await; + (alice, bob) +} - let (tx, rx) = oneshot::channel(); +async fn bootstrap() -> (Swarm, Swarm) { + let (mut alice, mut bob) = start_and_connect().await; - let alice_task = async move { + let cor_server_peer = alice.local_peer_id().clone(); + let cor_client_peer = bob.local_peer_id().clone(); + + let alice_task = async { let _ = alice .wait(|event| match event { SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), @@ -420,8 +532,6 @@ async fn bootstrap() -> (Swarm, Swarm) { }) .await; - assert_eq!(dialed_peer_id, cor_client_peer); - let _ = alice .wait(|event| match event { SwarmEvent::ConnectionEstablished { @@ -438,38 +548,20 @@ async fn bootstrap() -> (Swarm, Swarm) { }) .await; - let server::StatusUpdate { - all_addrs, - tested_addr, - client, - data_amount, - result, - } = alice + alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { - Some(status_update) - } + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(_)) => Some(()), _ => None, }) .await; - - assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); - assert_eq!(data_amount, 0); - assert_eq!(client, cor_client_peer); - assert_eq!(&all_addrs[..], &bob_external_addrs[..]); - assert!(result.is_ok(), "Result: {result:?}"); - - rx.await.unwrap(); - alice }; - let bob_task = async move { - let address_candidate = bob - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; + let bob_task = async { + bob.wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; let incoming_conn_id = bob .wait(|event| match event { SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), @@ -486,31 +578,14 @@ async fn bootstrap() -> (Swarm, Swarm) { _ => None, }); - let client::StatusUpdate { - tested_addr, - data_amount, - server, - result, - } = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { - Some(status_update) - } - SwarmEvent::ExternalAddrConfirmed { address } => { - assert_eq!(address, address_candidate); - None - } - _ => None, - }) - .await; - assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); - assert_eq!(data_amount, 0); - assert_eq!(server, cor_server_peer); - assert!(result.is_ok(), "Result is {result:?}"); - - tx.send(()).unwrap(); - bob + bob.wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), + SwarmEvent::ExternalAddrConfirmed { .. } => None, + _ => None, + }) + .await; }; - tokio::join!(alice_task, bob_task) + tokio::join!(alice_task, bob_task); + (alice, bob) } From bf2662a73356d74d4e9c2acb25fa5e4153ec8915 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 18:39:59 +0100 Subject: [PATCH 048/179] Implement old suggestions i overlooked --- protocols/autonatv2/src/client/behaviour.rs | 158 +++++++++--------- protocols/autonatv2/src/client/handler.rs | 4 - .../autonatv2/src/client/handler/dial_back.rs | 9 +- .../src/client/handler/dial_request.rs | 56 ++++--- protocols/autonatv2/src/lib.rs | 2 +- .../src/{request_response.rs => protocol.rs} | 4 +- .../autonatv2/src/server/handler/dial_back.rs | 2 +- .../src/server/handler/dial_request.rs | 2 +- 8 files changed, 122 insertions(+), 115 deletions(-) rename protocols/autonatv2/src/{request_response.rs => protocol.rs} (98%) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 49e0a0a15c9..08c9d27dd84 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -19,8 +19,8 @@ use rand_core::{OsRng, RngCore}; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; -use crate::client::handler::dial_request::InternalError; -use crate::{global_only::IpExt, request_response::DialRequest}; +use crate::{client::handler::dial_request::InternalError, Nonce}; +use crate::{global_only::IpExt, protocol::DialRequest}; use super::handler::{ dial_back, @@ -126,7 +126,7 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - *self.address_candidates.entry(addr.clone()).or_default() += 1; + self.inject_address_candiate(addr.clone()) } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { self.address_candidates.remove(addr); @@ -179,12 +179,10 @@ where } } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { - if !self.known_servers.contains(&peer_id) { - self.known_servers.push(peer_id); - } + self.inject_kown_server(peer_id); } Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { - dial_request: DialRequest { nonce, addrs }, + dial_request: DialRequest { nonce, .. }, reachable_addr, }))) => { if self.pending_nonces.remove(&nonce) { @@ -193,12 +191,6 @@ where ); return; } - self.pending_events.extend( - addrs - .into_iter() - .take_while(|addr| addr != &reachable_addr) - .map(ToSwarm::ExternalAddrExpired), - ); self.pending_events .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); } @@ -211,6 +203,15 @@ where self.pending_events .push_back(ToSwarm::ExternalAddrExpired(addr.clone())); } + dial_request::InternalError::InternalServer + | dial_request::InternalError::DataRequestTooLarge { .. } + | dial_request::InternalError::DataRequestTooSmall { .. } + | dial_request::InternalError::InvalidResponse + | dial_request::InternalError::ServerRejectedDialRequest + | dial_request::InternalError::InvalidReferencedAddress { .. } + | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { + self.handle_no_connection(peer_id, connection_id); + } _ => { tracing::debug!("Test failed: {:?}", err); } @@ -227,50 +228,13 @@ where cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { - let pending_event = self.poll_pending_events(); - if pending_event.is_ready() { - return pending_event; + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); } - if self.next_tick.poll_unpin(cx).is_ready() - && !self.known_servers.is_empty() - && !self.address_candidates.is_empty() - { - let mut entries = self - .address_candidates - .iter() - .filter(|(addr, _)| !self.already_tested.contains(addr)) - .collect::>(); - if entries.is_empty() { - return Poll::Pending; - } - entries.sort_unstable_by_key(|(_, count)| *count); - let addrs = entries - .into_iter() - .rev() - .map(|(addr, _)| addr.clone()) - .take(self.config.max_addrs_count) - .collect::>(); - self.already_tested.extend(addrs.iter().cloned()); - let peers = if self.known_servers.len() < self.config.test_server_count { - self.known_servers.clone() - } else { - self.known_servers - .choose_multiple(&mut self.rng, self.config.test_server_count) - .copied() - .collect() - }; - for peer in peers { - let nonce = self.rng.gen(); - let req = DialRequest { - nonce, - addrs: addrs.clone(), - }; - self.pending_nonces.insert(nonce); - self.submit_req_for_peer(peer, req); - } - let pending_event = self.poll_pending_events(); - if pending_event.is_ready() { - return pending_event; + if self.next_tick.poll_unpin(cx).is_ready() { + self.inject_address_candiate_test(); + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); } } Poll::Pending @@ -296,18 +260,74 @@ where } } - fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest) { + /// Injects a known server into the behaviour. It's mostly useful if you are not using identify + /// or for testing purposes. + pub fn inject_kown_server(&mut self, peer: PeerId) { + if !self.known_servers.contains(&peer) { + self.known_servers.push(peer); + } + } + + /// Inject a new address candidate into the behaviour. + pub fn inject_address_candiate(&mut self, addr: Multiaddr) { + *self.address_candidates.entry(addr).or_default() += 1; + } + + /// Inject an immediate test for all pending address candidates. + pub fn inject_address_candiate_test(&mut self) { + if self.known_servers.is_empty() || self.address_candidates.is_empty() { + return; + } + let mut entries = self + .address_candidates + .iter() + .filter(|(addr, _)| !self.already_tested.contains(addr)) + .map(|(addr, count)| (addr.clone(), *count)) + .collect::>(); + entries.sort_unstable_by_key(|(_, count)| *count); + let addrs = entries + .iter() + .rev() + .map(|(addr, _)| addr) + .take(self.config.max_addrs_count) + .cloned() + .collect::>(); + self.already_tested.extend(addrs.iter().cloned()); + let peers = if self.known_servers.len() < self.config.test_server_count { + self.known_servers.clone() + } else { + self.known_servers + .choose_multiple(&mut self.rng, self.config.test_server_count) + .copied() + .collect() + }; + for peer in peers { + let mut remaining_entries = entries.clone(); + let mut addrs = Vec::with_capacity(self.config.max_addrs_count); + while !remaining_entries.is_empty() && addrs.len() < self.config.max_addrs_count { + let addr = remaining_entries + .choose_weighted(&mut self.rng, |item| item.1) + .unwrap() + .0 + .clone(); + remaining_entries.retain(|(a, _)| a != &addr); + addrs.push(addr); + } + let nonce = self.rng.gen(); + let req = DialRequest { nonce, addrs }; + self.submit_req_for_peer(peer, req, nonce); + } + self.next_tick.reset(self.config.recheck_interval); + } + + fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest, nonce: Nonce) { + self.pending_nonces.insert(nonce); if let Some(conn_id) = self.peers_to_handlers.get(&peer) { self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, handler: NotifyHandler::One(*conn_id), event: Either::Left(req), }); - } else { - tracing::debug!( - "There should be a connection to {:?}, but there isn't", - peer - ); } } @@ -318,20 +338,6 @@ where } self.known_servers.retain(|p| p != &peer_id); } - - fn poll_pending_events( - &mut self, - ) -> Poll< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - > { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - Poll::Pending - } } impl Default for Behaviour { diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index ce1300a6a8b..b30a10d8aae 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -1,9 +1,5 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; -use std::time::Duration; - pub(crate) use dial_request::TestEnd; -const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); -const MAX_CONCURRENT_REQUESTS: usize = 10; diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 08704e2e13c..61d21fcf7ce 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -1,6 +1,7 @@ use std::{ io, task::{Context, Poll}, + time::Duration, }; use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; @@ -12,9 +13,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::{request_response::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; - -use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; +use crate::{protocol::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; pub struct Handler { inbound: FuturesSet>, @@ -23,7 +22,7 @@ pub struct Handler { impl Handler { pub(crate) fn new() -> Self { Self { - inbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + inbound: FuturesSet::new(Duration::from_secs(5), 2), } } } @@ -78,7 +77,7 @@ impl ConnectionHandler for Handler { } } ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { - tracing::debug!("Dial back request failed: {:?}", error); + void::unreachable(error); } _ => {} } diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index f11eac4edac..bf709a8f0eb 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -24,21 +24,20 @@ use std::{ iter::{once, repeat}, sync::Arc, task::{Context, Poll}, + time::Duration, }; use crate::client::behaviour::Error; use crate::{ client::behaviour::Event, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{ + protocol::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, REQUEST_PROTOCOL_NAME, }; -use super::{DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS}; - #[derive(Debug, thiserror::Error)] pub(crate) enum InternalError { #[error("io error")] @@ -49,8 +48,6 @@ pub(crate) enum InternalError { DataRequestTooLarge { len: usize, max: usize }, #[error("data request too small: {len} (min: {min})")] DataRequestTooSmall { len: usize, min: usize }, - #[error("timeout")] - Timeout(#[from] futures_bounded::Timeout), #[error("server rejected dial request")] ServerRejectedDialRequest, #[error("server chose not to dial any provided address")] @@ -63,10 +60,6 @@ pub(crate) enum InternalError { UnableToConnectOnSelectedAddress { addr: Option }, #[error("server experienced failure during dial back on address: {addr:?}")] FailureDuringDialBack { addr: Option }, - #[error("error during substream upgrad")] - Substream( - #[from] StreamUpgradeError< as OutboundUpgradeSend>::Error>, - ), } #[derive(Debug)] @@ -109,7 +102,7 @@ impl Handler { let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(DEFAULT_TIMEOUT, MAX_CONCURRENT_REQUESTS), + outbound: FuturesSet::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), status_update_tx, status_update_rx, @@ -126,7 +119,7 @@ impl Handler { }); if self .outbound - .try_push(start_substream_handle( + .try_push(start_stream_handle( self.server, req, rx, @@ -163,7 +156,7 @@ impl ConnectionHandler for Handler { if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( ToBehaviour::TestCompleted( - m.map_err(InternalError::Timeout) + m.map_err(|_| InternalError::Io(io::Error::from(io::ErrorKind::TimedOut))) .map_err(Into::into) .and_then(identity) .map_err(Into::into), @@ -196,9 +189,7 @@ impl ConnectionHandler for Handler { tracing::debug!("Dial request failed: {}", error); match self.queued_streams.pop_front() { Some(stream_tx) => { - if stream_tx.send(Err(error)).is_err() { - tracing::warn!("Failed to send stream to dead handler"); - } + let _ = stream_tx.send(Err(error)); } None => { tracing::warn!( @@ -232,10 +223,10 @@ impl ConnectionHandler for Handler { } } -async fn start_substream_handle( +async fn start_stream_handle( server: PeerId, dial_request: DialRequest, - substream_recv: oneshot::Receiver< + stream_recv: oneshot::Receiver< Result< Stream, StreamUpgradeError< as OutboundUpgradeSend>::Error>, @@ -243,16 +234,31 @@ async fn start_substream_handle( >, mut status_update_tx: mpsc::Sender, ) -> Result { - let substream = match substream_recv.await { + let substream = match stream_recv.await { Ok(Ok(substream)) => substream, - Ok(Err(err)) => return Err(InternalError::from(err).into()), + Ok(Err(StreamUpgradeError::Io(io))) => return Err(InternalError::from(io).into()), + Ok(Err(StreamUpgradeError::Timeout)) => { + return Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) + } + Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => { + return Err( + InternalError::Io(io::Error::new(io::ErrorKind::Other, upgrade_error)).into(), + ) + } + Ok(Err(StreamUpgradeError::NegotiationFailed)) => { + return Err(InternalError::Io(io::Error::new( + io::ErrorKind::Other, + "negotiation failed", + )) + .into()) + } Err(_) => return Err(InternalError::InternalServer.into()), }; let mut data_amount = 0; let mut checked_addr_idx = None; let addrs = dial_request.addrs.clone(); assert_ne!(addrs, vec![]); - let res = handle_substream( + let res = handle_stream( dial_request, substream, &mut data_amount, @@ -271,13 +277,13 @@ async fn start_substream_handle( res } -async fn handle_substream( +async fn handle_stream( dial_request: DialRequest, - substream: impl AsyncRead + AsyncWrite + Unpin, + stream: impl AsyncRead + AsyncWrite + Unpin, data_amount: &mut usize, checked_addr_idx: &mut Option, ) -> Result { - let mut coder = Coder::new(substream); + let mut coder = Coder::new(stream); coder.send(Request::Dial(dial_request.clone())).await?; match coder.next().await? { Response::Data(DialDataRequest { @@ -361,7 +367,7 @@ fn test_end_from_dial_response( } async fn send_aap_data( - substream: &mut Coder, + stream: &mut Coder, num_bytes: usize, data_amount: &mut usize, ) -> io::Result<()> @@ -384,7 +390,7 @@ where }) { *data_amount += data_count; - substream.send(req).await?; + stream.send(req).await?; } Ok(()) } diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonatv2/src/lib.rs index cca22be465c..0fae928921b 100644 --- a/protocols/autonatv2/src/lib.rs +++ b/protocols/autonatv2/src/lib.rs @@ -3,7 +3,7 @@ use libp2p_swarm::StreamProtocol; pub mod client; mod generated; mod global_only; -pub(crate) mod request_response; +pub(crate) mod protocol; pub mod server; pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = diff --git a/protocols/autonatv2/src/request_response.rs b/protocols/autonatv2/src/protocol.rs similarity index 98% rename from protocols/autonatv2/src/request_response.rs rename to protocols/autonatv2/src/protocol.rs index 026d7c57c5c..a12e70bb7c3 100644 --- a/protocols/autonatv2/src/request_response.rs +++ b/protocols/autonatv2/src/protocol.rs @@ -284,7 +284,7 @@ impl DialBack { FramedRead::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) .next() .await - .ok_or(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"))??; + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; let nonce = ok_or_invalid_data!(nonce)?; Ok(Self { nonce }) } @@ -305,7 +305,7 @@ mod tests { use crate::generated::structs::{ mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; - use crate::request_response::{Coder, DialDataResponse, Request}; + use crate::protocol::{Coder, DialDataResponse, Request}; use futures::io::Cursor; use rand::{thread_rng, Rng}; diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonatv2/src/server/handler/dial_back.rs index 06e7d36f305..c04c45c6fe8 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonatv2/src/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{request_response::dial_back, DIAL_BACK_PROTOCOL_NAME}; +use crate::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index 6d660f31ad9..e04a3d8fc9b 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -24,7 +24,7 @@ use rand_core::RngCore; use crate::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - request_response::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, + protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::StatusUpdate, Nonce, REQUEST_PROTOCOL_NAME, }; From efdbe350cf0024dd32e5e3db3f79e585e5ff3b62 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:05:17 +0100 Subject: [PATCH 049/179] Resolve conflict --- protocols/autonatv2/src/client/behaviour.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index 08c9d27dd84..b9bb3f0eeb4 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -284,6 +284,9 @@ where .filter(|(addr, _)| !self.already_tested.contains(addr)) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); + if entries.is_empty() { + return; + } entries.sort_unstable_by_key(|(_, count)| *count); let addrs = entries .iter() From c7350f901b7010d3198c82724489d472bd1a2238 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:20:09 +0100 Subject: [PATCH 050/179] Port dns transport --- transports/dns/src/lib.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index 3aeac4e4154..265d77ad997 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -148,9 +148,8 @@ pub mod tokio { use async_trait::async_trait; use futures::{future::BoxFuture, prelude::*}; use libp2p_core::{ - connection::Endpoint, multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -231,15 +230,12 @@ where self.inner.lock().remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.do_dial(addr, Endpoint::Dialer) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - self.do_dial(addr, Endpoint::Listener) + self.do_dial(addr, dial_opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { @@ -269,7 +265,7 @@ where fn do_dial( &mut self, addr: Multiaddr, - role_override: Endpoint, + dial_opts: DialOpts, ) -> Result< ::Dial, TransportError<::Error>, @@ -358,10 +354,7 @@ where tracing::debug!(address=%addr, "Dialing address"); let transport = inner.clone(); - let dial = match role_override { - Endpoint::Dialer => transport.lock().dial(addr), - Endpoint::Listener => transport.lock().dial_as_listener(addr), - }; + let dial = transport.lock().dial(addr, dial_opts); let result = match dial { Ok(out) => { // We only count attempts that the inner transport From 3079aa9fd1334c5f13cc788e093de8ada61d2b08 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:21:12 +0100 Subject: [PATCH 051/179] Port request response --- protocols/request-response/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index fc68bd6cf1f..8a743d731ea 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -79,7 +79,7 @@ pub use handler::ProtocolSupport; use crate::handler::OutboundMessage; use futures::channel::oneshot; use handler::Handler; -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{AddressChange, ConnectionClosed, DialFailure, FromSwarm}, @@ -777,6 +777,7 @@ where peer: PeerId, remote_address: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { let mut handler = Handler::new( self.inbound_protocols.clone(), From 596bb06eb9cb26fd4d7a370124e9978862918e29 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 19:27:02 +0100 Subject: [PATCH 052/179] Port websocket --- transports/websocket/src/framed.rs | 34 ++++++++++++------------------ transports/websocket/src/lib.rs | 11 ++++------ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/transports/websocket/src/framed.rs b/transports/websocket/src/framed.rs index 3593e1eaff2..73747295170 100644 --- a/transports/websocket/src/framed.rs +++ b/transports/websocket/src/framed.rs @@ -23,9 +23,8 @@ use either::Either; use futures::{future::BoxFuture, prelude::*, ready, stream::BoxStream}; use futures_rustls::{client, rustls, server}; use libp2p_core::{ - connection::Endpoint, multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, Transport, }; use parking_lot::Mutex; @@ -149,15 +148,12 @@ where self.transport.lock().remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.do_dial(addr, Endpoint::Dialer) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - self.do_dial(addr, Endpoint::Listener) + self.do_dial(addr, dial_opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { @@ -263,7 +259,7 @@ where fn do_dial( &mut self, addr: Multiaddr, - role_override: Endpoint, + dial_opts: DialOpts, ) -> Result<::Dial, TransportError<::Error>> { let mut addr = match parse_ws_dial_addr(addr) { Ok(addr) => addr, @@ -282,8 +278,7 @@ where let future = async move { loop { - match Self::dial_once(transport.clone(), addr, tls_config.clone(), role_override) - .await + match Self::dial_once(transport.clone(), addr, tls_config.clone(), dial_opts).await { Ok(Either::Left(redirect)) => { if remaining_redirects == 0 { @@ -307,18 +302,17 @@ where transport: Arc>, addr: WsAddress, tls_config: tls::Config, - role_override: Endpoint, + dial_opts: DialOpts, ) -> Result>, Error> { tracing::trace!(address=?addr, "Dialing websocket address"); - let dial = match role_override { - Endpoint::Dialer => transport.lock().dial(addr.tcp_addr), - Endpoint::Listener => transport.lock().dial_as_listener(addr.tcp_addr), - } - .map_err(|e| match e { - TransportError::MultiaddrNotSupported(a) => Error::InvalidMultiaddr(a), - TransportError::Other(e) => Error::Transport(e), - })?; + let dial = transport + .lock() + .dial(addr.tcp_addr, dial_opts) + .map_err(|e| match e { + TransportError::MultiaddrNotSupported(a) => Error::InvalidMultiaddr(a), + TransportError::Other(e) => Error::Transport(e), + })?; let stream = dial.map_err(Error::Transport).await?; tracing::trace!(port=%addr.host_port, "TCP connection established"); diff --git a/transports/websocket/src/lib.rs b/transports/websocket/src/lib.rs index e0b3d09ca25..3ead3184a5a 100644 --- a/transports/websocket/src/lib.rs +++ b/transports/websocket/src/lib.rs @@ -33,7 +33,7 @@ use futures::{future::BoxFuture, prelude::*, ready}; use libp2p_core::{ connection::ConnectedPoint, multiaddr::Multiaddr, - transport::{map::MapFuture, ListenerId, TransportError, TransportEvent}, + transport::{map::MapFuture, DialOpts, ListenerId, TransportError, TransportEvent}, Transport, }; use rw_stream_sink::RwStreamSink; @@ -202,15 +202,12 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.transport.dial(addr) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { - self.transport.dial_as_listener(addr) + self.transport.dial(addr, opts) } fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { From 37cd309ed41e78f7a808c5751ed1564ac875fc56 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:36:51 +0100 Subject: [PATCH 053/179] Port allow block list and connection limits --- misc/allow-block-list/src/lib.rs | 2 ++ misc/connection-limits/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/allow-block-list/src/lib.rs b/misc/allow-block-list/src/lib.rs index c1d31433db1..c877ab09c9b 100644 --- a/misc/allow-block-list/src/lib.rs +++ b/misc/allow-block-list/src/lib.rs @@ -61,6 +61,7 @@ //! # } //! ``` +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -225,6 +226,7 @@ where peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { self.state.enforce(&peer)?; diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index dbe68a8ad11..99008deb36b 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr, transport::PortUse}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, DialFailure, ListenFailure}, @@ -278,6 +278,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { self.pending_outbound_connections.remove(&connection_id); From 9eea564819ce634d9ae339f9287baa4357029883 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:10:08 +0100 Subject: [PATCH 054/179] Address more concerns --- misc/connection-limits/src/lib.rs | 2 +- protocols/autonatv2/src/client/behaviour.rs | 373 ++++++++++-------- protocols/autonatv2/src/client/handler.rs | 1 - .../autonatv2/src/client/handler/dial_back.rs | 35 +- .../src/client/handler/dial_request.rs | 115 +++--- protocols/autonatv2/src/protocol.rs | 8 + protocols/autonatv2/src/server/behaviour.rs | 5 +- .../src/server/handler/dial_request.rs | 24 +- protocols/autonatv2/tests/autonatv2.rs | 70 ++-- 9 files changed, 342 insertions(+), 291 deletions(-) diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index 99008deb36b..e31f50b83cb 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr, transport::PortUse}; +use libp2p_core::{transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, DialFailure, ListenFailure}, diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonatv2/src/client/behaviour.rs index b9bb3f0eeb4..65a35c57add 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonatv2/src/client/behaviour.rs @@ -14,35 +14,26 @@ use libp2p_swarm::{ ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; -use rand::{seq::SliceRandom, Rng}; -use rand_core::{OsRng, RngCore}; +use rand::prelude::*; +use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use std::sync::Arc; -use crate::{client::handler::dial_request::InternalError, Nonce}; +use crate::client::handler::dial_request::InternalError; use crate::{global_only::IpExt, protocol::DialRequest}; use super::handler::{ dial_back, - dial_request::{self}, + dial_request::{self, InternalStatusUpdate}, TestEnd, }; #[derive(Debug, Clone, Copy)] pub struct Config { - pub(crate) test_server_count: usize, pub(crate) max_addrs_count: usize, pub(crate) recheck_interval: Duration, } impl Config { - pub fn with_test_server_count(self, test_server_count: usize) -> Self { - Self { - test_server_count, - ..self - } - } - pub fn with_max_addrs_count(self, max_addrs_count: usize) -> Self { Self { max_addrs_count, @@ -61,19 +52,17 @@ impl Config { impl Default for Config { fn default() -> Self { Self { - test_server_count: 3, max_addrs_count: 10, recheck_interval: Duration::from_secs(5), } } } + pub struct Behaviour where R: RngCore + 'static, { - local_peers: HashSet, - pending_nonces: HashSet, - known_servers: Vec, + pending_nonces: HashMap, rng: R, config: Config, pending_events: VecDeque< @@ -82,10 +71,10 @@ where <::ConnectionHandler as ConnectionHandler>::FromBehaviour, >, >, - address_candidates: HashMap, + address_candidates: HashMap, already_tested: HashSet, - peers_to_handlers: HashMap, next_tick: Delay, + peer_info: HashMap, } impl NetworkBehaviour for Behaviour @@ -99,12 +88,19 @@ where fn handle_established_inbound_connection( &mut self, connection_id: ConnectionId, - _peer: PeerId, + peer_id: PeerId, _local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result<::ConnectionHandler, ConnectionDenied> { if addr_is_local(remote_addr) { - self.local_peers.insert(connection_id); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: false, + is_local: true, + }) + .is_local = true; } Ok(Either::Right(dial_back::Handler::new())) } @@ -112,33 +108,49 @@ where fn handle_established_outbound_connection( &mut self, connection_id: ConnectionId, - peer: PeerId, + peer_id: PeerId, addr: &Multiaddr, _role_override: Endpoint, _port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { if addr_is_local(addr) { - self.local_peers.insert(connection_id); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: false, + is_local: true, + }) + .is_local = true; } - Ok(Either::Left(dial_request::Handler::new(peer))) + Ok(Either::Left(dial_request::Handler::new(peer_id))) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - self.inject_address_candiate(addr.clone()) + self.address_candidates + .entry(addr.clone()) + .or_default() + .score += 1; } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - self.address_candidates.remove(addr); + if let Some(info) = self.address_candidates.get_mut(addr) { + info.is_tested = true; + } } FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, .. }) => { - self.peers_to_handlers - .entry(peer_id) - .or_insert(connection_id); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: false, + is_local: false, + }); } FromSwarm::ConnectionClosed(ConnectionClosed { peer_id, @@ -149,10 +161,9 @@ where } FromSwarm::DialFailure(DialFailure { peer_id: Some(peer_id), - error, connection_id, + .. }) => { - tracing::trace!("dialing {peer_id:?} failed: {error:?}"); self.handle_no_connection(peer_id, connection_id); } _ => {} @@ -165,61 +176,92 @@ where connection_id: ConnectionId, event: ::ToBehaviour, ) { - if matches!(event, Either::Left(_)) { - self.peers_to_handlers - .entry(peer_id) - .or_insert(connection_id); - } match event { Either::Right(nonce) => { - if self.pending_nonces.remove(&nonce) { + if let Some(status) = self.pending_nonces.get_mut(&nonce) { + *status = NonceStatus::Received; tracing::trace!("Received pending nonce from {peer_id:?}"); } else { tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); } } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { - self.inject_kown_server(peer_id); + self.peer_info + .values_mut() + .filter(|info| info.peer_id == peer_id) + .for_each(|info| { + info.supports_autonat = true; + }); + self.peer_info + .entry(connection_id) + .or_insert(ConnectionInfo { + peer_id, + supports_autonat: true, + is_local: false, + }) + .supports_autonat = true; } - Either::Left(dial_request::ToBehaviour::TestCompleted(Ok(TestEnd { - dial_request: DialRequest { nonce, .. }, - reachable_addr, - }))) => { - if self.pending_nonces.remove(&nonce) { - tracing::debug!( - "server reported reachbility, but didn't actually reached this node." - ); - return; + Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { + tested_addr, + bytes_sent: data_amount, + server, + result, + server_no_support, + })) => { + if server_no_support { + self.peer_info + .values_mut() + .filter(|info| info.peer_id == peer_id) + .for_each(|info| { + info.supports_autonat = false; + }); } - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr)); - } - Either::Left(dial_request::ToBehaviour::TestCompleted(Err(err))) => { - match err.internal.as_ref() { - dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } - | dial_request::InternalError::UnableToConnectOnSelectedAddress { - addr: Some(addr), - } => { - self.pending_events - .push_back(ToSwarm::ExternalAddrExpired(addr.clone())); - } - dial_request::InternalError::InternalServer - | dial_request::InternalError::DataRequestTooLarge { .. } - | dial_request::InternalError::DataRequestTooSmall { .. } - | dial_request::InternalError::InvalidResponse - | dial_request::InternalError::ServerRejectedDialRequest - | dial_request::InternalError::InvalidReferencedAddress { .. } - | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { - self.handle_no_connection(peer_id, connection_id); - } - _ => { - tracing::debug!("Test failed: {:?}", err); + match result { + Ok(TestEnd { + dial_request: DialRequest { nonce, .. }, + ref reachable_addr, + }) => { + if !matches!(self.pending_nonces.get(&nonce), Some(NonceStatus::Received)) { + tracing::debug!( + "server reported reachbility, but didn't actually reached this node." + ); + } else { + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); + } } + Err(ref err) => match &err.internal { + dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } + | dial_request::InternalError::UnableToConnectOnSelectedAddress { + addr: Some(addr), + } => { + if let Some(peer_info) = self.address_candidates.get_mut(addr) { + peer_info.is_tested = true; + } + tracing::debug!(addr = %addr, "Was unable to connect to the server on the selected address.") + } + dial_request::InternalError::InternalServer + | dial_request::InternalError::DataRequestTooLarge { .. } + | dial_request::InternalError::DataRequestTooSmall { .. } + | dial_request::InternalError::InvalidResponse + | dial_request::InternalError::ServerRejectedDialRequest + | dial_request::InternalError::InvalidReferencedAddress { .. } + | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { + self.handle_no_connection(peer_id, connection_id); + } + _ => { + tracing::debug!("Test failed: {:?}", err); + } + }, } + let event = crate::client::Event { + tested_addr, + bytes_sent: data_amount, + server: server.unwrap_or(peer_id), + result: result.map(|_| ()), + }; + self.pending_events.push_back(ToSwarm::GenerateEvent(event)); } - Either::Left(dial_request::ToBehaviour::StatusUpdate(update)) => self - .pending_events - .push_back(ToSwarm::GenerateEvent(update)), } } @@ -247,40 +289,32 @@ where { pub fn new(rng: R, config: Config) -> Self { Self { - local_peers: HashSet::new(), - pending_nonces: HashSet::new(), - known_servers: Vec::new(), + pending_nonces: HashMap::new(), rng, next_tick: Delay::new(config.recheck_interval), config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), - peers_to_handlers: HashMap::new(), already_tested: HashSet::new(), + peer_info: HashMap::new(), } } - /// Injects a known server into the behaviour. It's mostly useful if you are not using identify - /// or for testing purposes. - pub fn inject_kown_server(&mut self, peer: PeerId) { - if !self.known_servers.contains(&peer) { - self.known_servers.push(peer); - } - } - - /// Inject a new address candidate into the behaviour. - pub fn inject_address_candiate(&mut self, addr: Multiaddr) { - *self.address_candidates.entry(addr).or_default() += 1; - } - /// Inject an immediate test for all pending address candidates. - pub fn inject_address_candiate_test(&mut self) { - if self.known_servers.is_empty() || self.address_candidates.is_empty() { + fn inject_address_candiate_test(&mut self) { + if self.peer_info.values().all(|info| !info.supports_autonat) { + return; + } + if self.address_candidates.is_empty() { + return; + } + if self.address_candidates.values().all(|info| info.is_tested) { return; } let mut entries = self .address_candidates .iter() + .filter(|(_, info)| !info.is_tested) .filter(|(addr, _)| !self.already_tested.contains(addr)) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); @@ -294,52 +328,68 @@ where .map(|(addr, _)| addr) .take(self.config.max_addrs_count) .cloned() - .collect::>(); - self.already_tested.extend(addrs.iter().cloned()); - let peers = if self.known_servers.len() < self.config.test_server_count { - self.known_servers.clone() - } else { - self.known_servers - .choose_multiple(&mut self.rng, self.config.test_server_count) - .copied() - .collect() - }; - for peer in peers { - let mut remaining_entries = entries.clone(); - let mut addrs = Vec::with_capacity(self.config.max_addrs_count); - while !remaining_entries.is_empty() && addrs.len() < self.config.max_addrs_count { - let addr = remaining_entries - .choose_weighted(&mut self.rng, |item| item.1) - .unwrap() - .0 - .clone(); - remaining_entries.retain(|(a, _)| a != &addr); - addrs.push(addr); - } - let nonce = self.rng.gen(); - let req = DialRequest { nonce, addrs }; - self.submit_req_for_peer(peer, req, nonce); + .collect(); + if let Some(ConnectionInfo { peer_id, .. }) = self + .peer_info + .values() + .filter(|e| e.supports_autonat) + .choose(&mut self.rng) + { + self.submit_req_for_peer(*peer_id, addrs); } self.next_tick.reset(self.config.recheck_interval); } - fn submit_req_for_peer(&mut self, peer: PeerId, req: DialRequest, nonce: Nonce) { - self.pending_nonces.insert(nonce); - if let Some(conn_id) = self.peers_to_handlers.get(&peer) { + fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { + let nonce = self.rng.gen(); + let req = DialRequest { nonce, addrs }; + self.pending_nonces.insert(nonce, NonceStatus::Pending); + if let Some(conn_id) = self + .peer_info + .iter() + .filter(|(_, info)| info.supports_autonat) + .find(|(_, info)| info.peer_id == peer) + .map(|(id, _)| *id) + { self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id: peer, - handler: NotifyHandler::One(*conn_id), + handler: NotifyHandler::One(conn_id), event: Either::Left(req), }); } } fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { - if matches!(self.peers_to_handlers.get(&peer_id), Some(conn_id) if *conn_id == connection_id) - { - self.peers_to_handlers.remove(&peer_id); + let removeable_conn_ids = self + .peer_info + .iter() + .filter(|(conn_id, info)| info.peer_id == peer_id && **conn_id == connection_id) + .map(|(id, _)| *id) + .collect::>(); + for conn_id in removeable_conn_ids { + self.peer_info.remove(&conn_id); + } + let known_servers_n = self + .peer_info + .values() + .filter(|info| info.supports_autonat) + .count(); + let changed_n = self + .peer_info + .values_mut() + .filter(|info| info.supports_autonat) + .filter(|info| info.peer_id == peer_id) + .map(|info| info.supports_autonat = false) + .count(); + if known_servers_n != changed_n { + tracing::trace!(server = %peer_id, "Removing potential Autonat server due to dial failure"); + } + } + + pub fn validate_addr(&mut self, addr: &Multiaddr) { + if let Some(info) = self.address_candidates.get_mut(addr) { + info.is_tested = true; } - self.known_servers.retain(|p| p != &peer_id); } } @@ -350,36 +400,12 @@ impl Default for Behaviour { } pub struct Error { - pub(crate) internal: Arc, -} - -impl Error { - pub(crate) fn duplicate(&self) -> Self { - Self { - internal: Arc::clone(&self.internal), - } - } + pub(crate) internal: InternalError, } impl From for Error { - fn from(value: InternalError) -> Self { - Self { - internal: Arc::new(value), - } - } -} - -impl From> for Error { - fn from(value: Arc) -> Self { - Self { internal: value } - } -} - -impl From<&Arc> for Error { - fn from(value: &Arc) -> Self { - Self { - internal: Arc::clone(value), - } + fn from(internal: InternalError) -> Self { + Self { internal } } } @@ -403,7 +429,7 @@ pub struct Event { /// The amount of data that was sent to the server. /// Is 0 if it wasn't necessary to send any data. /// Otherwise it's a number between 30.000 and 100.000. - pub data_amount: usize, + pub bytes_sent: usize, /// The peer id of the server that was selected for testing. pub server: PeerId, /// The result of the test. If the test was successful, this is `Ok(())`. @@ -413,12 +439,45 @@ pub struct Event { fn addr_is_local(addr: &Multiaddr) -> bool { addr.iter().any(|c| match c { - Protocol::Dns(addr) - | Protocol::Dns4(addr) - | Protocol::Dns6(addr) - | Protocol::Dnsaddr(addr) => addr.ends_with(".local"), Protocol::Ip4(ip) => !IpExt::is_global(&ip), Protocol::Ip6(ip) => !IpExt::is_global(&ip), _ => false, }) } + +enum NonceStatus { + Pending, + Received, +} + +struct ConnectionInfo { + peer_id: PeerId, + supports_autonat: bool, + is_local: bool, +} + +#[derive(Copy, Clone, Default)] +struct AddressInfo { + score: usize, + is_tested: bool, +} + +impl PartialOrd for AddressInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.score.cmp(&other.score)) + } +} + +impl PartialEq for AddressInfo { + fn eq(&self, other: &Self) -> bool { + self.score == other.score + } +} + +impl Ord for AddressInfo { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.score.cmp(&other.score) + } +} + +impl Eq for AddressInfo {} diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonatv2/src/client/handler.rs index b30a10d8aae..bb6c9636e2c 100644 --- a/protocols/autonatv2/src/client/handler.rs +++ b/protocols/autonatv2/src/client/handler.rs @@ -2,4 +2,3 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; pub(crate) use dial_request::TestEnd; - diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonatv2/src/client/handler/dial_back.rs index 61d21fcf7ce..2e5eea3038b 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonatv2/src/client/handler/dial_back.rs @@ -4,7 +4,6 @@ use std::{ time::Duration, }; -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -13,7 +12,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::{protocol::DialBack, Nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; pub struct Handler { inbound: FuturesSet>, @@ -45,16 +44,20 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - if let Poll::Ready(result) = self.inbound.poll_unpin(cx) { - match result { - Ok(Ok(nonce)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) - } - Ok(Err(err)) => tracing::debug!("Dial back handler failed with: {err:?}"), - Err(err) => tracing::debug!("Dial back handler timed out with: {err:?}"), + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(Ok(nonce))) => { + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) + } + Poll::Ready(Ok(Err(err))) => { + tracing::debug!("Dial back handler failed with: {err:?}"); + Poll::Pending + } + Poll::Ready(Err(err)) => { + tracing::debug!("Dial back handler timed out with: {err:?}"); + Poll::Pending } + Poll::Pending => Poll::Pending, } - Poll::Pending } fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} @@ -72,7 +75,11 @@ impl ConnectionHandler for Handler { ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { protocol, .. }) => { - if self.inbound.try_push(perform_dial_back(protocol)).is_err() { + if self + .inbound + .try_push(protocol::recv_dial_back(protocol)) + .is_err() + { tracing::warn!("Dial back request dropped, too many requests in flight"); } } @@ -83,9 +90,3 @@ impl ConnectionHandler for Handler { } } } - -async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { - let DialBack { nonce } = DialBack::read_from(&mut stream).await?; - stream.close().await?; - Ok(nonce) -} diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonatv2/src/client/handler/dial_request.rs index bf709a8f0eb..6efa1d8d08b 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonatv2/src/client/handler/dial_request.rs @@ -1,7 +1,4 @@ -use futures::{ - channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, SinkExt, StreamExt, -}; +use futures::{channel::oneshot, AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, @@ -19,17 +16,14 @@ use libp2p_swarm::{ }; use std::{ collections::VecDeque, - convert::identity, io, iter::{once, repeat}, - sync::Arc, task::{Context, Poll}, time::Duration, }; use crate::client::behaviour::Error; use crate::{ - client::behaviour::Event, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, @@ -62,6 +56,15 @@ pub(crate) enum InternalError { FailureDuringDialBack { addr: Option }, } +#[derive(Debug)] +pub struct InternalStatusUpdate { + pub(crate) tested_addr: Option, + pub(crate) bytes_sent: usize, + pub(crate) server: Option, + pub result: Result, + pub(crate) server_no_support: bool, +} + #[derive(Debug)] pub struct TestEnd { pub(crate) dial_request: DialRequest, @@ -70,8 +73,7 @@ pub struct TestEnd { #[derive(Debug)] pub enum ToBehaviour { - TestCompleted(Result), - StatusUpdate(Event), + TestCompleted(InternalStatusUpdate), PeerHasServerSupport, } @@ -83,7 +85,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet>, + outbound: futures_bounded::FuturesSet, queued_streams: VecDeque< oneshot::Sender< Result< @@ -92,20 +94,15 @@ pub struct Handler { >, >, >, - status_update_rx: mpsc::Receiver, - status_update_tx: mpsc::Sender, server: PeerId, } impl Handler { pub(crate) fn new(server: PeerId) -> Self { - let (status_update_tx, status_update_rx) = mpsc::channel(10); Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), - status_update_tx, - status_update_rx, server, } } @@ -119,12 +116,7 @@ impl Handler { }); if self .outbound - .try_push(start_stream_handle( - self.server, - req, - rx, - self.status_update_tx.clone(), - )) + .try_push(start_stream_handle(self.server, req, rx)) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -154,18 +146,20 @@ impl ConnectionHandler for Handler { return Poll::Ready(event); } if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { + let status_update = match m { + Ok(ok) => ok, + Err(_) => InternalStatusUpdate { + tested_addr: None, + bytes_sent: 0, + server: None, + result: Err(Error { + internal: InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)), + }), + server_no_support: false, + }, + }; return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestCompleted( - m.map_err(|_| InternalError::Io(io::Error::from(io::ErrorKind::TimedOut))) - .map_err(Into::into) - .and_then(identity) - .map_err(Into::into), - ), - )); - } - if let Poll::Ready(Some(status_update)) = self.status_update_rx.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::StatusUpdate(status_update), + ToBehaviour::TestCompleted(status_update), )); } Poll::Pending @@ -232,49 +226,56 @@ async fn start_stream_handle( StreamUpgradeError< as OutboundUpgradeSend>::Error>, >, >, - mut status_update_tx: mpsc::Sender, -) -> Result { - let substream = match stream_recv.await { - Ok(Ok(substream)) => substream, - Ok(Err(StreamUpgradeError::Io(io))) => return Err(InternalError::from(io).into()), +) -> InternalStatusUpdate { + let mut server_no_support = false; + let substream_result = match stream_recv.await { + Ok(Ok(substream)) => Ok(substream), + Ok(Err(StreamUpgradeError::Io(io))) => Err(InternalError::from(io).into()), Ok(Err(StreamUpgradeError::Timeout)) => { - return Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) + Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) } - Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => { - return Err( - InternalError::Io(io::Error::new(io::ErrorKind::Other, upgrade_error)).into(), + Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => void::unreachable(upgrade_error), + Ok(Err(StreamUpgradeError::NegotiationFailed)) => { + server_no_support = true; + Err( + InternalError::Io(io::Error::new(io::ErrorKind::Other, "negotiation failed")) + .into(), ) } - Ok(Err(StreamUpgradeError::NegotiationFailed)) => { - return Err(InternalError::Io(io::Error::new( - io::ErrorKind::Other, - "negotiation failed", - )) - .into()) + Err(_) => Err(InternalError::InternalServer.into()), + }; + let substream = match substream_result { + Ok(substream) => substream, + Err(err) => { + let status_update = InternalStatusUpdate { + tested_addr: None, + bytes_sent: 0, + server: Some(server), + result: Err(err), + server_no_support, + }; + return status_update; } - Err(_) => return Err(InternalError::InternalServer.into()), }; let mut data_amount = 0; let mut checked_addr_idx = None; let addrs = dial_request.addrs.clone(); assert_ne!(addrs, vec![]); - let res = handle_stream( + let result = handle_stream( dial_request, substream, &mut data_amount, &mut checked_addr_idx, ) .await - .map_err(Arc::new) .map_err(crate::client::behaviour::Error::from); - let status_update = Event { + InternalStatusUpdate { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), - data_amount, - server, - result: res.as_ref().map(|_| ()).map_err(|e| e.duplicate()), - }; - let _ = status_update_tx.send(status_update).await; - res + bytes_sent: data_amount, + server: Some(server), + result, + server_no_support, + } } async fn handle_stream( diff --git a/protocols/autonatv2/src/protocol.rs b/protocols/autonatv2/src/protocol.rs index a12e70bb7c3..b83660cbc65 100644 --- a/protocols/autonatv2/src/protocol.rs +++ b/protocols/autonatv2/src/protocol.rs @@ -272,6 +272,14 @@ pub(crate) async fn dial_back(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) stream.close().await } +pub(crate) async fn recv_dial_back( + mut stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result { + let DialBack { nonce } = DialBack::read_from(&mut stream).await?; + stream.close().await?; + Ok(nonce) +} + const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) struct DialBack { diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonatv2/src/server/behaviour.rs index 0a7bee427df..f1027b43ca4 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonatv2/src/server/behaviour.rs @@ -14,7 +14,6 @@ use libp2p_swarm::{ }; use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; -use std::sync::Arc; use crate::server::handler::{ dial_back, @@ -156,11 +155,11 @@ pub struct StatusUpdate { pub all_addrs: Vec, /// The address that was eventually tested. /// This is `None` if the client send and unexpected message. - pub tested_addr: Option, + pub tested_addr: Multiaddr, /// The peer id of the client that submitted addresses for testing. pub client: PeerId, /// The amount of data that was requested by the server and was transmitted. pub data_amount: usize, /// The result of the test. - pub result: Result<(), Arc>, + pub result: Result<(), io::Error>, } diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonatv2/src/server/handler/dial_request.rs index e04a3d8fc9b..3553ffb1dfd 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonatv2/src/server/handler/dial_request.rs @@ -1,6 +1,5 @@ use std::{ io, - sync::Arc, task::{Context, Poll}, time::Duration, }; @@ -52,7 +51,7 @@ pub struct Handler { dial_back_cmd_receiver: mpsc::Receiver, status_update_sender: mpsc::Sender, status_update_receiver: mpsc::Receiver, - inbound: FuturesSet>>, + inbound: FuturesSet>, rng: R, } @@ -82,7 +81,7 @@ where { type FromBehaviour = (); - type ToBehaviour = Either>, StatusUpdate>; + type ToBehaviour = Either, StatusUpdate>; type InboundProtocol = ReadyUpgrade; @@ -110,7 +109,7 @@ where } Poll::Ready(Err(e)) => { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( - Arc::new(io::Error::new(io::ErrorKind::TimedOut, e)), + io::Error::new(io::ErrorKind::TimedOut, e), )))); } Poll::Ready(Ok(Ok(_))) => {} @@ -318,11 +317,11 @@ async fn start_handle_request( dial_back_cmd_sender: mpsc::Sender, mut status_update_sender: mpsc::Sender, rng: impl RngCore, -) -> Result<(), Arc> { +) -> io::Result<()> { let mut all_addrs = Vec::new(); let mut tested_addrs = None; let mut data_amount = 0; - let res = handle_request( + let result = handle_request( stream, observed_multiaddr, dial_back_cmd_sender, @@ -331,15 +330,18 @@ async fn start_handle_request( &mut tested_addrs, &mut data_amount, ) - .await - .map_err(Arc::new); + .await; + if tested_addrs.is_none() { + tracing::warn!("client violated the protocol"); + return Err(io::Error::from(io::ErrorKind::InvalidData)); + } let status_update = StatusUpdate { all_addrs, - tested_addr: tested_addrs, + tested_addr: tested_addrs.unwrap(), client, data_amount, - result: res.as_ref().map_err(Arc::clone).map(|_| ()), + result, }; let _ = status_update_sender.send(status_update).await; - res + Ok(()) } diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonatv2/tests/autonatv2.rs index 3024c801660..63e7f5f90a0 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonatv2/tests/autonatv2.rs @@ -76,7 +76,7 @@ async fn confirm_successful() { }) .await; - assert_eq!(tested_addr, bob_external_addrs.get(0).cloned()); + assert_eq!(tested_addr, bob_external_addrs.get(0).cloned().unwrap()); assert_eq!(data_amount, 0); assert_eq!(client, cor_client_peer); assert_eq!(&all_addrs[..], &bob_external_addrs[..]); @@ -108,7 +108,7 @@ async fn confirm_successful() { let client::Event { tested_addr, - data_amount, + bytes_sent, server, result, } = bob @@ -124,7 +124,7 @@ async fn confirm_successful() { }) .await; assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); - assert_eq!(data_amount, 0); + assert_eq!(bytes_sent, 0); assert_eq!(server, cor_server_peer); assert!(result.is_ok(), "Result is {result:?}"); }; @@ -189,7 +189,7 @@ async fn dial_back_to_unsupported_protocol() { .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { all_addrs, - tested_addr: Some(tested_addr), + tested_addr, client, data_amount, result: Ok(()), @@ -203,30 +203,23 @@ async fn dial_back_to_unsupported_protocol() { }) .await; - tokio::select! { - _ = bob_done_rx => { - return data_amount; - } - _ = alice.loop_on_next() => { - unreachable!(); - } - } + let handler = tokio::spawn(async move { + alice.loop_on_next().await; + }); + let _ = bob_done_rx.await; + handler.abort(); + data_amount }; let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::ExternalAddrExpired { address } if address == bob_test_addr => Some(()), - _ => None, - }) - .await; let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), - data_amount, + bytes_sent, server, result: Err(_), - })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(data_amount), + })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(bytes_sent), _ => None, }) .await; @@ -298,7 +291,7 @@ async fn dial_back_to_non_libp2p() { .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { all_addrs, - tested_addr: Some(tested_addr), + tested_addr, client, data_amount, result: Ok(()), @@ -314,27 +307,23 @@ async fn dial_back_to_non_libp2p() { data_amount }; let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::ExternalAddrExpired { address } if address == bob_addr => Some(()), - _ => None, - }) - .await; let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), - data_amount, + bytes_sent, server, result: Err(_), - })) if tested_addr == bob_addr && server == alice_peer_id => Some(data_amount), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), _ => None, }) .await; data_amount }; - let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_data_amount, bob_data_amount); + let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_bytes_sent, bob_bytes_sent); + bob.behaviour_mut().autonat.validate_addr(&addr); } } @@ -393,7 +382,7 @@ async fn dial_back_to_not_supporting() { .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { all_addrs, - tested_addr: Some(tested_addr), + tested_addr, client, data_amount, result: Ok(()), @@ -417,32 +406,25 @@ async fn dial_back_to_not_supporting() { }; let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::ExternalAddrExpired { address } if address == bob_unreachable_address => { - Some(()) - } - _ => None, - }) - .await; - let data_amount = bob + let bytes_sent = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { tested_addr: Some(tested_addr), - data_amount, + bytes_sent, server, result: Err(_), })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { - Some(data_amount) + Some(bytes_sent) } _ => None, }) .await; bob_done_tx.send(()).unwrap(); - data_amount + bytes_sent }; - let (alice_data_amount, bob_data_amount) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_data_amount, bob_data_amount); + let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_bytes_sent, bob_bytes_sent); handler.abort(); } @@ -463,7 +445,7 @@ async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { autonat: libp2p_autonatv2::client::Behaviour::new( OsRng, - Config::default().with_recheck_interval(Duration::from_nanos(0)), + Config::default().with_recheck_interval(Duration::from_millis(100)), ), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), From 9a852099deddb341203e685585281e94f5c5202d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:28:08 +0100 Subject: [PATCH 055/179] Move autonat down --- protocols/autonat/src/lib.rs | 42 +------------------ protocols/autonat/src/v1.rs | 41 ++++++++++++++++++ protocols/autonat/src/{ => v1}/behaviour.rs | 17 ++++++-- .../src/{ => v1}/behaviour/as_client.rs | 2 +- .../src/{ => v1}/behaviour/as_server.rs | 1 + .../autonat/src/{ => v1}/generated/mod.rs | 0 .../src/{ => v1}/generated/structs.proto | 0 .../autonat/src/{ => v1}/generated/structs.rs | 0 protocols/autonat/src/{ => v1}/protocol.rs | 2 +- protocols/autonat/tests/test_client.rs | 2 +- protocols/autonat/tests/test_server.rs | 4 +- 11 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 protocols/autonat/src/v1.rs rename protocols/autonat/src/{ => v1}/behaviour.rs (98%) rename protocols/autonat/src/{ => v1}/behaviour/as_client.rs (99%) rename protocols/autonat/src/{ => v1}/behaviour/as_server.rs (99%) rename protocols/autonat/src/{ => v1}/generated/mod.rs (100%) rename protocols/autonat/src/{ => v1}/generated/structs.proto (100%) rename protocols/autonat/src/{ => v1}/generated/structs.rs (100%) rename protocols/autonat/src/{ => v1}/protocol.rs (99%) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 10c87b1e984..a3a6d96c3f5 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1,41 +1 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. - -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - -mod behaviour; -mod protocol; - -pub use self::{ - behaviour::{ - Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, - OutboundProbeError, OutboundProbeEvent, ProbeId, - }, - protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, -}; -pub use libp2p_request_response::{InboundFailure, OutboundFailure}; - -mod proto { - #![allow(unreachable_pub)] - include!("generated/mod.rs"); - pub(crate) use self::structs::{mod_Message::*, Message}; -} +pub mod v1; diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs new file mode 100644 index 00000000000..9974d7af59a --- /dev/null +++ b/protocols/autonat/src/v1.rs @@ -0,0 +1,41 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. + +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod behaviour; +mod protocol; + +pub use self::{ + behaviour::{ + Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, + OutboundProbeError, OutboundProbeEvent, ProbeId, + }, + protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, +}; +pub use libp2p_request_response::{InboundFailure, OutboundFailure}; + +mod proto { + #![allow(unreachable_pub)] + include!("v1/generated/mod.rs"); + pub(crate) use self::structs::{mod_Message::*, Message}; +} diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/v1/behaviour.rs similarity index 98% rename from protocols/autonat/src/behaviour.rs rename to protocols/autonat/src/v1/behaviour.rs index e95163ab23f..b60fcbb635c 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/v1/behaviour.rs @@ -21,14 +21,15 @@ mod as_client; mod as_server; -use crate::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; -use crate::DEFAULT_PROTOCOL_NAME; +use crate::v1::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; +use crate::v1::DEFAULT_PROTOCOL_NAME; use as_client::AsClient; pub use as_client::{OutboundProbeError, OutboundProbeEvent}; use as_server::AsServer; pub use as_server::{InboundProbeError, InboundProbeEvent}; use futures_timer::Delay; use instant::Instant; +use libp2p_core::transport::PortUse; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_request_response::{ @@ -337,6 +338,7 @@ impl Behaviour { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + port_use: _, } => { if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { self.pending_actions @@ -346,6 +348,7 @@ impl Behaviour { ConnectedPoint::Dialer { address: _, role_override: Endpoint::Listener, + port_use: _, } => { // Outgoing connection was dialed as a listener. In other words outgoing connection // was dialed as part of a hole punch. `libp2p-autonat` never attempts to hole @@ -511,9 +514,15 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/protocols/autonat/src/behaviour/as_client.rs b/protocols/autonat/src/v1/behaviour/as_client.rs similarity index 99% rename from protocols/autonat/src/behaviour/as_client.rs rename to protocols/autonat/src/v1/behaviour/as_client.rs index 668f3b93719..867a5bf171d 100644 --- a/protocols/autonat/src/behaviour/as_client.rs +++ b/protocols/autonat/src/v1/behaviour/as_client.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::ResponseError; +use crate::v1::ResponseError; use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, NatStatus, diff --git a/protocols/autonat/src/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs similarity index 99% rename from protocols/autonat/src/behaviour/as_server.rs rename to protocols/autonat/src/v1/behaviour/as_server.rs index 878fd713dda..4e3cfc77891 100644 --- a/protocols/autonat/src/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -135,6 +135,7 @@ impl<'a> HandleInnerEvent for AsServer<'a> { NonZeroU8::new(1).expect("1 > 0"), ) .addresses(addrs) + .allocate_new_port() .build(), }, ]) diff --git a/protocols/autonat/src/generated/mod.rs b/protocols/autonat/src/v1/generated/mod.rs similarity index 100% rename from protocols/autonat/src/generated/mod.rs rename to protocols/autonat/src/v1/generated/mod.rs diff --git a/protocols/autonat/src/generated/structs.proto b/protocols/autonat/src/v1/generated/structs.proto similarity index 100% rename from protocols/autonat/src/generated/structs.proto rename to protocols/autonat/src/v1/generated/structs.proto diff --git a/protocols/autonat/src/generated/structs.rs b/protocols/autonat/src/v1/generated/structs.rs similarity index 100% rename from protocols/autonat/src/generated/structs.rs rename to protocols/autonat/src/v1/generated/structs.rs diff --git a/protocols/autonat/src/protocol.rs b/protocols/autonat/src/v1/protocol.rs similarity index 99% rename from protocols/autonat/src/protocol.rs rename to protocols/autonat/src/v1/protocol.rs index c1862058400..bee29cae44c 100644 --- a/protocols/autonat/src/protocol.rs +++ b/protocols/autonat/src/v1/protocol.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::proto; +use crate::v1::proto; use async_trait::async_trait; use asynchronous_codec::{FramedRead, FramedWrite}; use futures::io::{AsyncRead, AsyncWrite}; diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 7509d3ef425..8eb7939fbdc 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use async_std::task::JoinHandle; -use libp2p_autonat::{ +use libp2p_autonat::v1::{ Behaviour, Config, Event, NatStatus, OutboundProbeError, OutboundProbeEvent, ResponseError, }; use libp2p_core::Multiaddr; diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index b0610ef59a4..f7e77ce75a7 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_autonat::{ +use libp2p_autonat::v1::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, }; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; @@ -92,6 +92,7 @@ async fn test_dial_back() { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + .. }, num_established, concurrent_dial_errors, @@ -300,6 +301,7 @@ async fn test_dial_multiple_addr() { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + .. }, concurrent_dial_errors, .. From 4399bab570af52a76e3427ffd5c211e0053729d8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 29 Dec 2023 20:56:51 +0100 Subject: [PATCH 056/179] Move autonatv2 to autonat --- Cargo.lock | 41 +++++-------------- Cargo.toml | 5 +-- examples/autonatv2/Cargo.toml | 12 ++++++ examples/autonatv2/src/main.rs | 3 ++ protocols/autonat/Cargo.toml | 31 ++++++++++---- protocols/autonat/src/lib.rs | 10 +++++ .../src/lib.rs => autonat/src/v2.rs} | 0 .../src => autonat/src/v2}/client.rs | 0 .../src/v2}/client/behaviour.rs | 6 +-- .../src => autonat/src/v2}/client/handler.rs | 0 .../src/v2}/client/handler/dial_back.rs | 2 +- .../src/v2}/client/handler/dial_request.rs | 6 +-- .../src => autonat/src/v2}/generated/mod.rs | 0 .../src/v2}/generated/structs.proto | 0 .../src/v2}/generated/structs.rs | 0 .../src => autonat/src/v2}/global_only.rs | 0 .../src => autonat/src/v2}/protocol.rs | 6 +-- .../src => autonat/src/v2}/server.rs | 0 .../src/v2}/server/behaviour.rs | 4 +- .../src => autonat/src/v2}/server/handler.rs | 0 .../src/v2}/server/handler/dial_back.rs | 2 +- .../src/v2}/server/handler/dial_request.rs | 2 +- .../{autonatv2 => autonat}/tests/autonatv2.rs | 12 +++--- protocols/autonatv2/Cargo.toml | 38 ----------------- 24 files changed, 81 insertions(+), 99 deletions(-) create mode 100644 examples/autonatv2/Cargo.toml create mode 100644 examples/autonatv2/src/main.rs rename protocols/{autonatv2/src/lib.rs => autonat/src/v2.rs} (100%) rename protocols/{autonatv2/src => autonat/src/v2}/client.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/client/behaviour.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/client/handler.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/client/handler/dial_back.rs (97%) rename protocols/{autonatv2/src => autonat/src/v2}/client/handler/dial_request.rs (99%) rename protocols/{autonatv2/src => autonat/src/v2}/generated/mod.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/generated/structs.proto (100%) rename protocols/{autonatv2/src => autonat/src/v2}/generated/structs.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/global_only.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/protocol.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/server.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/server/behaviour.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/server/handler.rs (100%) rename protocols/{autonatv2/src => autonat/src/v2}/server/handler/dial_back.rs (98%) rename protocols/{autonatv2/src => autonat/src/v2}/server/handler/dial_request.rs (99%) rename protocols/{autonatv2 => autonat}/tests/autonatv2.rs (98%) delete mode 100644 protocols/autonatv2/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 0d25c80c7d0..c1ec0acdf05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,6 +475,10 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "autonatv2" +version = "0.1.0" + [[package]] name = "axum" version = "0.6.20" @@ -2629,30 +2633,9 @@ dependencies = [ [[package]] name = "libp2p-autonat" -version = "0.12.0" +version = "0.13.0" dependencies = [ "async-std", - "async-trait", - "asynchronous-codec", - "futures", - "futures-timer", - "instant", - "libp2p-core", - "libp2p-identity", - "libp2p-request-response", - "libp2p-swarm", - "libp2p-swarm-test", - "quick-protobuf", - "quick-protobuf-codec", - "rand 0.8.5", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "libp2p-autonatv2" -version = "0.1.0" -dependencies = [ "async-trait", "asynchronous-codec", "bytes", @@ -2660,21 +2643,21 @@ dependencies = [ "futures", "futures-bounded", "futures-timer", + "instant", "libp2p-core", "libp2p-identify", "libp2p-identity", + "libp2p-request-response", "libp2p-swarm", "libp2p-swarm-test", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", "rand_core 0.6.4", - "static_assertions", "thiserror", "tokio", "tracing", "tracing-subscriber", - "unsigned-varint 0.8.0", "void", ] @@ -5792,18 +5775,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", @@ -6268,8 +6251,6 @@ checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" dependencies = [ "asynchronous-codec", "bytes", - "futures-io", - "futures-util", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 630a134d975..0ceed838ed6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ members = [ "muxers/test-harness", "muxers/yamux", "protocols/autonat", - "protocols/autonatv2", "protocols/dcutr", "protocols/floodsub", "protocols/gossipsub", @@ -63,7 +62,7 @@ members = [ "transports/websocket", "transports/webtransport-websys", "transports/websocket-websys", - "wasm-tests/webtransport-tests", + "wasm-tests/webtransport-tests", "examples/autonatv2", ] resolver = "2" @@ -75,7 +74,7 @@ asynchronous-codec = { version = "0.7.0" } futures-bounded = { version = "0.2.3", path = "misc/futures-bounded" } libp2p = { version = "0.53.2", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } -libp2p-autonat = { version = "0.12.0", path = "protocols/autonat" } +libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } libp2p-core = { version = "0.41.2", path = "core" } libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml new file mode 100644 index 00000000000..ccefc57c1f7 --- /dev/null +++ b/examples/autonatv2/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "autonatv2" +version = "0.1.0" +edition = "2021" +rust-version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lints] +workspace = true diff --git a/examples/autonatv2/src/main.rs b/examples/autonatv2/src/main.rs new file mode 100644 index 00000000000..e7a11a969c0 --- /dev/null +++ b/examples/autonatv2/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index a1ecae7ccab..b20fb4c596c 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -3,32 +3,50 @@ name = "libp2p-autonat" edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" -version = "0.12.0" +version = "0.13.0" authors = ["David Craven ", "Elena Frank "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] + [dependencies] async-trait = "0.1" +asynchronous-codec = { workspace = true } +bytes = { version = "1", optional = true } +either = { version = "1.9.0", optional = true } futures = "0.3" +futures-bounded = { workspace = true, optional = true } futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } -libp2p-swarm = { workspace = true } -libp2p-request-response = { workspace = true } libp2p-identity = { workspace = true } +libp2p-request-response = { workspace = true, optional = true } +libp2p-swarm = { workspace = true } quick-protobuf = "0.8" +quick-protobuf-codec = { workspace = true } rand = "0.8" +rand_core = { version = "0.6", optional = true } +thiserror = { version = "1.0.52", optional = true } tracing = "0.1.37" -quick-protobuf-codec = { workspace = true } -asynchronous-codec = { workspace = true } +void = { version = "1", optional = true } [dev-dependencies] +tokio = { version = "1", features = ["macros", "rt", "sync"]} async-std = { version = "1.10", features = ["attributes"] } libp2p-swarm-test = { path = "../../swarm-test" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } +libp2p-identify = { workspace = true } +libp2p-swarm = { workspace = true, features = ["macros"]} + +[features] +default = ["v1", "v2"] +v1 = ["dep:libp2p-request-response"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] + +[lints] +workspace = true # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling @@ -36,6 +54,3 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } all-features = true rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "docsrs"] - -[lints] -workspace = true diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index a3a6d96c3f5..8a06bea6605 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1 +1,11 @@ +#[cfg(feature = "v1")] pub mod v1; + +#[cfg(feature = "v2")] +pub mod v2; + +#[cfg(feature = "v1")] +#[deprecated(since = "0.13.0", note = "Please use `v1` module instead.")] +pub use v1::*; + + diff --git a/protocols/autonatv2/src/lib.rs b/protocols/autonat/src/v2.rs similarity index 100% rename from protocols/autonatv2/src/lib.rs rename to protocols/autonat/src/v2.rs diff --git a/protocols/autonatv2/src/client.rs b/protocols/autonat/src/v2/client.rs similarity index 100% rename from protocols/autonatv2/src/client.rs rename to protocols/autonat/src/v2/client.rs diff --git a/protocols/autonatv2/src/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs similarity index 98% rename from protocols/autonatv2/src/client/behaviour.rs rename to protocols/autonat/src/v2/client/behaviour.rs index 65a35c57add..657bee24e86 100644 --- a/protocols/autonatv2/src/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -18,8 +18,8 @@ use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::client::handler::dial_request::InternalError; -use crate::{global_only::IpExt, protocol::DialRequest}; +use crate::v2::client::handler::dial_request::InternalError; +use crate::v2::{global_only::IpExt, protocol::DialRequest}; use super::handler::{ dial_back, @@ -254,7 +254,7 @@ where } }, } - let event = crate::client::Event { + let event = crate::v2::client::Event { tested_addr, bytes_sent: data_amount, server: server.unwrap_or(peer_id), diff --git a/protocols/autonatv2/src/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs similarity index 100% rename from protocols/autonatv2/src/client/handler.rs rename to protocols/autonat/src/v2/client/handler.rs diff --git a/protocols/autonatv2/src/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs similarity index 97% rename from protocols/autonatv2/src/client/handler/dial_back.rs rename to protocols/autonat/src/v2/client/handler/dial_back.rs index 2e5eea3038b..492cc7729a2 100644 --- a/protocols/autonatv2/src/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -12,7 +12,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; pub struct Handler { inbound: FuturesSet>, diff --git a/protocols/autonatv2/src/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs similarity index 99% rename from protocols/autonatv2/src/client/handler/dial_request.rs rename to protocols/autonat/src/v2/client/handler/dial_request.rs index 6efa1d8d08b..a34c76003a4 100644 --- a/protocols/autonatv2/src/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -22,8 +22,8 @@ use std::{ time::Duration, }; -use crate::client::behaviour::Error; -use crate::{ +use crate::v2::{ + client::behaviour::Error, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, @@ -268,7 +268,7 @@ async fn start_stream_handle( &mut checked_addr_idx, ) .await - .map_err(crate::client::behaviour::Error::from); + .map_err(crate::v2::client::behaviour::Error::from); InternalStatusUpdate { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), bytes_sent: data_amount, diff --git a/protocols/autonatv2/src/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs similarity index 100% rename from protocols/autonatv2/src/generated/mod.rs rename to protocols/autonat/src/v2/generated/mod.rs diff --git a/protocols/autonatv2/src/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto similarity index 100% rename from protocols/autonatv2/src/generated/structs.proto rename to protocols/autonat/src/v2/generated/structs.proto diff --git a/protocols/autonatv2/src/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs similarity index 100% rename from protocols/autonatv2/src/generated/structs.rs rename to protocols/autonat/src/v2/generated/structs.rs diff --git a/protocols/autonatv2/src/global_only.rs b/protocols/autonat/src/v2/global_only.rs similarity index 100% rename from protocols/autonatv2/src/global_only.rs rename to protocols/autonat/src/v2/global_only.rs diff --git a/protocols/autonatv2/src/protocol.rs b/protocols/autonat/src/v2/protocol.rs similarity index 98% rename from protocols/autonatv2/src/protocol.rs rename to protocols/autonat/src/v2/protocol.rs index b83660cbc65..69f80bf8d10 100644 --- a/protocols/autonatv2/src/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -11,7 +11,7 @@ use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; use rand::Rng; -use crate::{generated::structs as proto, Nonce}; +use crate::v2::{generated::structs as proto, Nonce}; const REQUEST_MAX_SIZE: usize = 4104; pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; @@ -310,10 +310,10 @@ impl DialBack { #[cfg(test)] mod tests { - use crate::generated::structs::{ + use crate::v2::generated::structs::{ mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; - use crate::protocol::{Coder, DialDataResponse, Request}; + use crate::v2::protocol::{Coder, DialDataResponse, Request}; use futures::io::Cursor; use rand::{thread_rng, Rng}; diff --git a/protocols/autonatv2/src/server.rs b/protocols/autonat/src/v2/server.rs similarity index 100% rename from protocols/autonatv2/src/server.rs rename to protocols/autonat/src/v2/server.rs diff --git a/protocols/autonatv2/src/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs similarity index 98% rename from protocols/autonatv2/src/server/behaviour.rs rename to protocols/autonat/src/v2/server/behaviour.rs index f1027b43ca4..8f84da1e18f 100644 --- a/protocols/autonatv2/src/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -4,7 +4,7 @@ use std::{ task::{Context, Poll}, }; -use crate::server::handler::dial_request::DialBackStatus; +use crate::v2::server::handler::dial_request::DialBackStatus; use either::Either; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -15,7 +15,7 @@ use libp2p_swarm::{ use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; -use crate::server::handler::{ +use crate::v2::server::handler::{ dial_back, dial_request::{self, DialBackCommand}, Handler, diff --git a/protocols/autonatv2/src/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs similarity index 100% rename from protocols/autonatv2/src/server/handler.rs rename to protocols/autonat/src/v2/server/handler.rs diff --git a/protocols/autonatv2/src/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs similarity index 98% rename from protocols/autonatv2/src/server/handler/dial_back.rs rename to protocols/autonat/src/v2/server/handler/dial_back.rs index c04c45c6fe8..f671434eabe 100644 --- a/protocols/autonatv2/src/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; diff --git a/protocols/autonatv2/src/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs similarity index 99% rename from protocols/autonatv2/src/server/handler/dial_request.rs rename to protocols/autonat/src/v2/server/handler/dial_request.rs index 3553ffb1dfd..beb17d38b7f 100644 --- a/protocols/autonatv2/src/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -21,7 +21,7 @@ use libp2p_swarm::{ }; use rand_core::RngCore; -use crate::{ +use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::StatusUpdate, diff --git a/protocols/autonatv2/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs similarity index 98% rename from protocols/autonatv2/tests/autonatv2.rs rename to protocols/autonat/tests/autonatv2.rs index 63e7f5f90a0..618ad73f6ec 100644 --- a/protocols/autonatv2/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -1,5 +1,5 @@ -use libp2p_autonatv2::client::{self, Config}; -use libp2p_autonatv2::server; +use libp2p_autonat::v2::client::{self, Config}; +use libp2p_autonat::v2::server; use libp2p_core::transport::TransportError; use libp2p_core::Multiaddr; use libp2p_swarm::{ @@ -430,7 +430,7 @@ async fn dial_back_to_not_supporting() { async fn new_server() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedServer { - autonat: libp2p_autonatv2::server::Behaviour::default(), + autonat: libp2p_autonat::v2::server::Behaviour::default(), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), identity.public().clone(), @@ -443,7 +443,7 @@ async fn new_server() -> Swarm { async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { - autonat: libp2p_autonatv2::client::Behaviour::new( + autonat: libp2p_autonat::v2::client::Behaviour::new( OsRng, Config::default().with_recheck_interval(Duration::from_millis(100)), ), @@ -459,14 +459,14 @@ async fn new_client() -> Swarm { #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct CombinedServer { - autonat: libp2p_autonatv2::server::Behaviour, + autonat: libp2p_autonat::v2::server::Behaviour, identify: libp2p_identify::Behaviour, } #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct CombinedClient { - autonat: libp2p_autonatv2::client::Behaviour, + autonat: libp2p_autonat::v2::client::Behaviour, identify: libp2p_identify::Behaviour, } diff --git a/protocols/autonatv2/Cargo.toml b/protocols/autonatv2/Cargo.toml deleted file mode 100644 index 4944502c3e1..00000000000 --- a/protocols/autonatv2/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "libp2p-autonatv2" -version = "0.1.0" -edition = "2021" -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -async-trait = "0.1" -quick-protobuf = "0.8" -quick-protobuf-codec = { workspace = true } -asynchronous-codec = "0.7" -libp2p-core = { workspace = true } -rand_core = "0.6" -rand = "0.8" -libp2p-swarm = { workspace = true } -libp2p-identity = { workspace = true } -futures-bounded = { workspace = true } -void = "1.0.2" -either = "1.9.0" -futures = "0.3.29" -thiserror = "1.0.50" -bytes = "1" -static_assertions = "1.1.0" -tracing = "0.1.40" -unsigned-varint = { workspace = true, features = ["futures"] } -futures-timer = "3.0.2" - -[dev-dependencies] -tokio = { version = "1", features = ["macros", "rt", "sync"] } -libp2p-swarm-test = { workspace = true } -libp2p-identify = { workspace = true } -libp2p-swarm = { workspace = true, features = ["macros"] } -tracing-subscriber = { version = "0.3", features = ["env-filter"]} - -[lints] -workspace = true From 663055e416dd5a02298d99462a832e3978605e88 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 12:55:10 +1100 Subject: [PATCH 057/179] Format code --- protocols/autonat/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 8a06bea6605..714f0f5a89f 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -7,5 +7,3 @@ pub mod v2; #[cfg(feature = "v1")] #[deprecated(since = "0.13.0", note = "Please use `v1` module instead.")] pub use v1::*; - - From ba232cbe8a950a2c2375e58fbe79acc920f69022 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 12:55:48 +1100 Subject: [PATCH 058/179] Alphabetically sort members list --- Cargo.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ceed838ed6..47dfd2014df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "core", "examples/autonat", + "examples/autonatv2", "examples/browser-webrtc", "examples/chat", "examples/dcutr", @@ -46,9 +47,9 @@ members = [ "protocols/rendezvous", "protocols/request-response", "protocols/upnp", - "swarm", "swarm-derive", "swarm-test", + "swarm", "transports/dns", "transports/noise", "transports/plaintext", @@ -57,12 +58,12 @@ members = [ "transports/tcp", "transports/tls", "transports/uds", - "transports/webrtc", "transports/webrtc-websys", + "transports/webrtc", + "transports/websocket-websys", "transports/websocket", "transports/webtransport-websys", - "transports/websocket-websys", - "wasm-tests/webtransport-tests", "examples/autonatv2", + "wasm-tests/webtransport-tests", ] resolver = "2" From 22b96b30252584048bc5585e9a0a1fd2b20d2f96 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 12:59:27 +1100 Subject: [PATCH 059/179] Undo changes to global-only transport --- core/src/transport/global_only.rs | 462 ++++++++++++++---------------- 1 file changed, 220 insertions(+), 242 deletions(-) diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index a40f99054d0..d975070ea0f 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -17,272 +17,250 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// -mod ip { - use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - trait Ipv4Ext { - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - fn is_reserved(&self) -> bool; - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - fn is_benchmarking(&self) -> bool; - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - fn is_shared(&self) -> bool; - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - fn is_private(&self) -> bool; +use crate::{ + multiaddr::{Multiaddr, Protocol}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, +}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +/// Dropping all dial requests to non-global IP addresses. +#[derive(Debug, Clone, Default)] +pub struct Transport { + inner: T, +} + +/// This module contains an implementation of the `is_global` IPv4 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) +mod ipv4_global { + use std::net::Ipv4Addr; + + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + #[inline] + const fn is_reserved(a: Ipv4Addr) -> bool { + a.octets()[0] & 240 == 240 && !a.is_broadcast() } - impl Ipv4Ext for Ipv4Addr { - #[inline] - fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - #[inline] - fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - #[inline] - fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - #[inline] - fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if (16..=31).contains(&b) => true, - [192, 168, ..] => true, - _ => false, - } - } + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + #[inline] + const fn is_benchmarking(a: Ipv4Addr) -> bool { + a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 } - trait Ipv6Ext { - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - fn is_unicast_link_local(&self) -> bool; - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - fn is_unique_local(&self) -> bool; - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - fn is_documentation(&self) -> bool; + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + #[inline] + const fn is_shared(a: Ipv4Addr) -> bool { + a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) } - impl Ipv6Ext for Ipv6Addr { - #[inline] - fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + #[inline] + const fn is_private(a: Ipv4Addr) -> bool { + match a.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, } + } - #[inline] - fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[must_use] + #[inline] + pub(crate) const fn is_global(a: Ipv4Addr) -> bool { + !(a.octets()[0] == 0 // "This network" + || is_private(a) + || is_shared(a) + || a.is_loopback() + || a.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) + || a.is_documentation() + || is_benchmarking(a) + || is_reserved(a) + || a.is_broadcast()) + } +} - #[inline] - fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } +/// This module contains an implementation of the `is_global` IPv6 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) +mod ipv6_global { + use std::net::Ipv6Addr; + + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + #[inline] + const fn is_unicast_link_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xffc0) == 0xfe80 } - pub(super) trait IpExt { - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. - #[must_use] - fn is_global(&self) -> bool; + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + #[inline] + const fn is_unique_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xfe00) == 0xfc00 } - impl IpExt for Ipv4Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[inline] - fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || Ipv4Ext::is_shared(self) - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || Ipv4Ext::is_benchmarking(self) - || Ipv4Ext::is_reserved(self) - || self.is_broadcast()) - } + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + #[inline] + const fn is_documentation(a: Ipv6Addr) -> bool { + (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) } - impl IpExt for Ipv6Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[inline] - fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + #[inline] + pub(crate) const fn is_global(a: Ipv6Addr) -> bool { + !(a.is_unspecified() + || a.is_loopback() // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) && !( // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) + || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) )) - || Ipv6Ext::is_documentation(self) - || Ipv6Ext::is_unique_local(self) - || Ipv6Ext::is_unicast_link_local(self)) - } + || is_documentation(a) + || is_unique_local(a) + || is_unicast_link_local(a)) } - - impl IpExt for IpAddr { - #[inline] - fn is_global(&self) -> bool { - match self { - Self::V4(v4) => IpExt::is_global(v4), - Self::V6(v6) => IpExt::is_global(v6), - } - } - } -} - -use crate::{ - multiaddr::{Multiaddr, Protocol}, - transport::{DialOpts, ListenerId, TransportError, TransportEvent}, -}; -use ip::IpExt; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; -use tracing::debug; - -/// Dropping all dial requests to non-global IP addresses. -#[derive(Debug, Clone, Default)] -pub struct Transport { - inner: T, } impl Transport { @@ -316,15 +294,15 @@ impl crate::Transport for Transport { ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { - if !IpExt::is_global(&a) { - debug!("Not dialing non global IP address {:?}.", a); + if !ipv4_global::is_global(a) { + tracing::debug!(ip=%a, "Not dialing non global IP address"); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr, opts) } Some(Protocol::Ip6(a)) => { - if !IpExt::is_global(&a) { - debug!("Not dialing non global IP address {:?}.", a); + if !ipv6_global::is_global(a) { + tracing::debug!(ip=%a, "Not dialing non global IP address"); return Err(TransportError::MultiaddrNotSupported(addr)); } self.inner.dial(addr, opts) From a19d7c89ddab093cd8462847433956c46c80e12a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:05:54 +1100 Subject: [PATCH 060/179] Make deprecation warning work --- protocols/autonat/src/lib.rs | 2 +- protocols/autonat/src/v1.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 714f0f5a89f..a6fc66b28d1 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -5,5 +5,5 @@ pub mod v1; pub mod v2; #[cfg(feature = "v1")] -#[deprecated(since = "0.13.0", note = "Please use `v1` module instead.")] +#[allow(deprecated)] pub use v1::*; diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 9974d7af59a..1c493ce731a 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -21,6 +21,7 @@ //! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![deprecated(note = "Please use `v2` module instead.")] mod behaviour; mod protocol; From 46b8e84d1d5c067de8cc42c3f535d74e05a51e1e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:08:04 +1100 Subject: [PATCH 061/179] Avoid diff in v1 --- protocols/autonat/src/v1.rs | 6 +++--- protocols/autonat/src/v1/behaviour.rs | 4 ++-- protocols/autonat/src/v1/behaviour/as_client.rs | 2 +- protocols/autonat/src/v1/protocol.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 1c493ce731a..245748125cf 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -23,8 +23,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![deprecated(note = "Please use `v2` module instead.")] -mod behaviour; -mod protocol; +pub(crate) mod behaviour; +pub(crate) mod protocol; pub use self::{ behaviour::{ @@ -35,7 +35,7 @@ pub use self::{ }; pub use libp2p_request_response::{InboundFailure, OutboundFailure}; -mod proto { +pub(crate) mod proto { #![allow(unreachable_pub)] include!("v1/generated/mod.rs"); pub(crate) use self::structs::{mod_Message::*, Message}; diff --git a/protocols/autonat/src/v1/behaviour.rs b/protocols/autonat/src/v1/behaviour.rs index b60fcbb635c..4104e9688dc 100644 --- a/protocols/autonat/src/v1/behaviour.rs +++ b/protocols/autonat/src/v1/behaviour.rs @@ -21,8 +21,8 @@ mod as_client; mod as_server; -use crate::v1::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; -use crate::v1::DEFAULT_PROTOCOL_NAME; +use crate::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; +use crate::DEFAULT_PROTOCOL_NAME; use as_client::AsClient; pub use as_client::{OutboundProbeError, OutboundProbeEvent}; use as_server::AsServer; diff --git a/protocols/autonat/src/v1/behaviour/as_client.rs b/protocols/autonat/src/v1/behaviour/as_client.rs index 867a5bf171d..668f3b93719 100644 --- a/protocols/autonat/src/v1/behaviour/as_client.rs +++ b/protocols/autonat/src/v1/behaviour/as_client.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::v1::ResponseError; +use crate::ResponseError; use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, NatStatus, diff --git a/protocols/autonat/src/v1/protocol.rs b/protocols/autonat/src/v1/protocol.rs index bee29cae44c..c1862058400 100644 --- a/protocols/autonat/src/v1/protocol.rs +++ b/protocols/autonat/src/v1/protocol.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::v1::proto; +use crate::proto; use async_trait::async_trait; use asynchronous_codec::{FramedRead, FramedWrite}; use futures::io::{AsyncRead, AsyncWrite}; From 037aa997204ddda763f8061680d699cd2434388f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:09:06 +1100 Subject: [PATCH 062/179] Undo behaviour change in v1 --- protocols/autonat/src/v1/behaviour/as_server.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index 4e3cfc77891..878fd713dda 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -135,7 +135,6 @@ impl<'a> HandleInnerEvent for AsServer<'a> { NonZeroU8::new(1).expect("1 > 0"), ) .addresses(addrs) - .allocate_new_port() .build(), }, ]) From 230ad0847a15fd9846e01d049508d3f82bd63b4c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:12:34 +1100 Subject: [PATCH 063/179] Rename client config fields --- protocols/autonat/src/v2/client/behaviour.rs | 25 +++++++++++--------- protocols/autonat/tests/autonatv2.rs | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 657bee24e86..cb68e236944 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -29,21 +29,24 @@ use super::handler::{ #[derive(Debug, Clone, Copy)] pub struct Config { - pub(crate) max_addrs_count: usize, - pub(crate) recheck_interval: Duration, + /// How many candidates we will test at most. + pub(crate) max_candidates: usize, + + /// The interval at which we will attempt to confirm candidates as external addresses. + pub(crate) probe_interval: Duration, } impl Config { - pub fn with_max_addrs_count(self, max_addrs_count: usize) -> Self { + pub fn with_max_candidates(self, max_candidates: usize) -> Self { Self { - max_addrs_count, + max_candidates, ..self } } - pub fn with_recheck_interval(self, recheck_interval: Duration) -> Self { + pub fn with_probe_interval(self, probe_interval: Duration) -> Self { Self { - recheck_interval, + probe_interval, ..self } } @@ -52,8 +55,8 @@ impl Config { impl Default for Config { fn default() -> Self { Self { - max_addrs_count: 10, - recheck_interval: Duration::from_secs(5), + max_candidates: 10, + probe_interval: Duration::from_secs(5), } } } @@ -291,7 +294,7 @@ where Self { pending_nonces: HashMap::new(), rng, - next_tick: Delay::new(config.recheck_interval), + next_tick: Delay::new(config.probe_interval), config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), @@ -326,7 +329,7 @@ where .iter() .rev() .map(|(addr, _)| addr) - .take(self.config.max_addrs_count) + .take(self.config.max_candidates) .cloned() .collect(); if let Some(ConnectionInfo { peer_id, .. }) = self @@ -337,7 +340,7 @@ where { self.submit_req_for_peer(*peer_id, addrs); } - self.next_tick.reset(self.config.recheck_interval); + self.next_tick.reset(self.config.probe_interval); } fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 618ad73f6ec..951610d84b2 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -445,7 +445,7 @@ async fn new_client() -> Swarm { let mut node = Swarm::new_ephemeral(|identity| CombinedClient { autonat: libp2p_autonat::v2::client::Behaviour::new( OsRng, - Config::default().with_recheck_interval(Duration::from_millis(100)), + Config::default().with_probe_interval(Duration::from_millis(100)), ), identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( "/libp2p-test/1.0.0".into(), From 7cbf4131ecaed3549e4ab2ca0476aff6a68cd99a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:17:03 +1100 Subject: [PATCH 064/179] Streamline creation of `peer_info` state --- protocols/autonat/src/v2/client/behaviour.rs | 39 +++++--------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index cb68e236944..524b6de618f 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -90,42 +90,22 @@ where fn handle_established_inbound_connection( &mut self, - connection_id: ConnectionId, - peer_id: PeerId, - _local_addr: &Multiaddr, - remote_addr: &Multiaddr, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: &Multiaddr, ) -> Result<::ConnectionHandler, ConnectionDenied> { - if addr_is_local(remote_addr) { - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { - peer_id, - supports_autonat: false, - is_local: true, - }) - .is_local = true; - } Ok(Either::Right(dial_back::Handler::new())) } fn handle_established_outbound_connection( &mut self, - connection_id: ConnectionId, + _: ConnectionId, peer_id: PeerId, - addr: &Multiaddr, - _role_override: Endpoint, - _port_use: PortUse, + _: &Multiaddr, + _: Endpoint, + _: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - if addr_is_local(addr) { - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { - peer_id, - supports_autonat: false, - is_local: true, - }) - .is_local = true; - } Ok(Either::Left(dial_request::Handler::new(peer_id))) } @@ -145,6 +125,7 @@ where FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, + endpoint, .. }) => { self.peer_info @@ -152,7 +133,7 @@ where .or_insert(ConnectionInfo { peer_id, supports_autonat: false, - is_local: false, + is_local: addr_is_local(endpoint.get_remote_address()), }); } FromSwarm::ConnectionClosed(ConnectionClosed { From 16b4d7c0bce936c06c6175fcbb16498283d77fb7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:20:36 +1100 Subject: [PATCH 065/179] Rewrite pending nonce handling Through the use of let-else, we can early-exit on the unhappy-path. We also update the log message to remove the "cause" as there can be a multitude of reasons, why we received a nonce that we didn't expect, e.g. it could also be a bug in the client or the server implementation so we don't want to jump to conclusions here. --- protocols/autonat/src/v2/client/behaviour.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 524b6de618f..e6055389eb1 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -162,12 +162,13 @@ where ) { match event { Either::Right(nonce) => { - if let Some(status) = self.pending_nonces.get_mut(&nonce) { - *status = NonceStatus::Received; - tracing::trace!("Received pending nonce from {peer_id:?}"); - } else { - tracing::warn!("Received unexpected nonce from {peer_id:?}, this means that another node tried to be reachable on an address this node is reachable on."); - } + let Some(status) = self.pending_nonces.get_mut(&nonce) else { + tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); + return; + }; + + *status = NonceStatus::Received; + tracing::debug!(%peer_id, %nonce, "Successful dial-back"); } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { self.peer_info From fdc0c2d34de830328f17c2dbc388493fd95bdc69 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:23:22 +1100 Subject: [PATCH 066/179] A server might not support autonat on every connection Fix the event handling to only update the flag for the connection that the event originated from. --- protocols/autonat/src/v2/client/behaviour.rs | 23 +++++--------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e6055389eb1..be0eec26f8a 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -172,18 +172,8 @@ where } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { self.peer_info - .values_mut() - .filter(|info| info.peer_id == peer_id) - .for_each(|info| { - info.supports_autonat = true; - }); - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { - peer_id, - supports_autonat: true, - is_local: false, - }) + .get_mut(&connection_id) + .expect("inconsistent state") .supports_autonat = true; } Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { @@ -195,12 +185,11 @@ where })) => { if server_no_support { self.peer_info - .values_mut() - .filter(|info| info.peer_id == peer_id) - .for_each(|info| { - info.supports_autonat = false; - }); + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; } + match result { Ok(TestEnd { dial_request: DialRequest { nonce, .. }, From a78c265c53d82f69511a9f14336450aac6216ebd Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:26:37 +1100 Subject: [PATCH 067/179] Prefer early-exit over `else` --- protocols/autonat/src/v2/client/behaviour.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index be0eec26f8a..f196b8864f4 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -196,13 +196,16 @@ where ref reachable_addr, }) => { if !matches!(self.pending_nonces.get(&nonce), Some(NonceStatus::Received)) { - tracing::debug!( - "server reported reachbility, but didn't actually reached this node." - ); - } else { - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); + tracing::warn!( + %peer_id, + %nonce, + "Server reported reachbility but we never received a dial-back" + ); + return; } + + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); } Err(ref err) => match &err.internal { dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } From 8f6c27a03c7f75f640a7959983de0fe6153adef1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:27:29 +1100 Subject: [PATCH 068/179] Use shorthand field logging syntax --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index f196b8864f4..2923163424a 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -215,7 +215,7 @@ where if let Some(peer_info) = self.address_candidates.get_mut(addr) { peer_info.is_tested = true; } - tracing::debug!(addr = %addr, "Was unable to connect to the server on the selected address.") + tracing::debug!(%addr, "Was unable to connect to the server on the selected address.") } dial_request::InternalError::InternalServer | dial_request::InternalError::DataRequestTooLarge { .. } From 170270a6fb4912ed310c2bd4ca4d51b41ec6464a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:27:45 +1100 Subject: [PATCH 069/179] Fix variable name --- protocols/autonat/src/v2/client/behaviour.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 2923163424a..4a7de708e14 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -212,8 +212,8 @@ where | dial_request::InternalError::UnableToConnectOnSelectedAddress { addr: Some(addr), } => { - if let Some(peer_info) = self.address_candidates.get_mut(addr) { - peer_info.is_tested = true; + if let Some(address_info) = self.address_candidates.get_mut(addr) { + address_info.is_tested = true; } tracing::debug!(%addr, "Was unable to connect to the server on the selected address.") } From 589cf6cf691347d0d6faa71b0960045c652efe68 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:29:16 +1100 Subject: [PATCH 070/179] Improve log message --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 4a7de708e14..3bf2bf2cc61 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -215,7 +215,7 @@ where if let Some(address_info) = self.address_candidates.get_mut(addr) { address_info.is_tested = true; } - tracing::debug!(%addr, "Was unable to connect to the server on the selected address.") + tracing::debug!(%peer_id, %addr, "Server failed to dial address, candidate is not a public address") } dial_request::InternalError::InternalServer | dial_request::InternalError::DataRequestTooLarge { .. } From feea82f906a2fc99064bdebc14ce33dc54fddce9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:29:37 +1100 Subject: [PATCH 071/179] Log error using `fmt::Display` --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 3bf2bf2cc61..422416fe416 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -227,7 +227,7 @@ where self.handle_no_connection(peer_id, connection_id); } _ => { - tracing::debug!("Test failed: {:?}", err); + tracing::debug!("Test failed: {err}"); } }, } From 8b0a544d2bb698093153b4265c84d9dd6373abc1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:33:29 +1100 Subject: [PATCH 072/179] Remove `server` field from `InternalStatusUpdate` Connections are always scoped to a particular peer and the PeerId is reported with every event sent from the handler to the behaviour. We don't need to include it in the event. --- protocols/autonat/src/v2/client/behaviour.rs | 7 +++---- .../autonat/src/v2/client/handler/dial_request.rs | 12 ++---------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 422416fe416..0886c0715bf 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -101,12 +101,12 @@ where fn handle_established_outbound_connection( &mut self, _: ConnectionId, - peer_id: PeerId, + _: PeerId, _: &Multiaddr, _: Endpoint, _: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Left(dial_request::Handler::new(peer_id))) + Ok(Either::Left(dial_request::Handler::new())) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -179,7 +179,6 @@ where Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { tested_addr, bytes_sent: data_amount, - server, result, server_no_support, })) => { @@ -234,7 +233,7 @@ where let event = crate::v2::client::Event { tested_addr, bytes_sent: data_amount, - server: server.unwrap_or(peer_id), + server: peer_id, result: result.map(|_| ()), }; self.pending_events.push_back(ToSwarm::GenerateEvent(event)); diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index a34c76003a4..0850c4a2bb3 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -5,7 +5,6 @@ use libp2p_core::{ Multiaddr, }; -use libp2p_identity::PeerId; use libp2p_swarm::{ handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, @@ -60,7 +59,6 @@ pub(crate) enum InternalError { pub struct InternalStatusUpdate { pub(crate) tested_addr: Option, pub(crate) bytes_sent: usize, - pub(crate) server: Option, pub result: Result, pub(crate) server_no_support: bool, } @@ -94,16 +92,14 @@ pub struct Handler { >, >, >, - server: PeerId, } impl Handler { - pub(crate) fn new(server: PeerId) -> Self { + pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), outbound: FuturesSet::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), - server, } } @@ -116,7 +112,7 @@ impl Handler { }); if self .outbound - .try_push(start_stream_handle(self.server, req, rx)) + .try_push(start_stream_handle(req, rx)) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -151,7 +147,6 @@ impl ConnectionHandler for Handler { Err(_) => InternalStatusUpdate { tested_addr: None, bytes_sent: 0, - server: None, result: Err(Error { internal: InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)), }), @@ -218,7 +213,6 @@ impl ConnectionHandler for Handler { } async fn start_stream_handle( - server: PeerId, dial_request: DialRequest, stream_recv: oneshot::Receiver< Result< @@ -250,7 +244,6 @@ async fn start_stream_handle( let status_update = InternalStatusUpdate { tested_addr: None, bytes_sent: 0, - server: Some(server), result: Err(err), server_no_support, }; @@ -272,7 +265,6 @@ async fn start_stream_handle( InternalStatusUpdate { tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), bytes_sent: data_amount, - server: Some(server), result, server_no_support, } From 86849e510604978f01d60a54375b86213ec3a6db Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:34:51 +1100 Subject: [PATCH 073/179] Loop instead of repeating code --- protocols/autonat/src/v2/client/behaviour.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 0886c0715bf..d8ca3900bb1 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -246,16 +246,18 @@ where cx: &mut Context<'_>, ) -> Poll::FromBehaviour>> { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - if self.next_tick.poll_unpin(cx).is_ready() { - self.inject_address_candiate_test(); + loop { if let Some(event) = self.pending_events.pop_front() { return Poll::Ready(event); } + + if self.next_tick.poll_unpin(cx).is_ready() { + self.inject_address_candiate_test(); + continue; + } + + return Poll::Pending; } - Poll::Pending } } From 372d88d828900e6b5adf636417fa599a51ea3e20 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:36:03 +1100 Subject: [PATCH 074/179] Remove set that we never write to This is now handled in the `AddressInfo` struct. --- protocols/autonat/src/v2/client/behaviour.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index d8ca3900bb1..81fecd4e7f5 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, VecDeque}, task::{Context, Poll}, time::Duration, }; @@ -75,7 +75,6 @@ where >, >, address_candidates: HashMap, - already_tested: HashSet, next_tick: Delay, peer_info: HashMap, } @@ -273,7 +272,6 @@ where config, pending_events: VecDeque::new(), address_candidates: HashMap::new(), - already_tested: HashSet::new(), peer_info: HashMap::new(), } } @@ -293,7 +291,6 @@ where .address_candidates .iter() .filter(|(_, info)| !info.is_tested) - .filter(|(addr, _)| !self.already_tested.contains(addr)) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); if entries.is_empty() { From a2fcadd30568f5675a60a91b7c19267a6892a4d6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:36:47 +1100 Subject: [PATCH 075/179] Remove superflous tests We filter straight after this for the same conditions. --- protocols/autonat/src/v2/client/behaviour.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 81fecd4e7f5..f89f46d813f 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -281,12 +281,7 @@ where if self.peer_info.values().all(|info| !info.supports_autonat) { return; } - if self.address_candidates.is_empty() { - return; - } - if self.address_candidates.values().all(|info| info.is_tested) { - return; - } + let mut entries = self .address_candidates .iter() From 90d7d077dfe32469c7c8f917e080bf6a6546d295 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:38:40 +1100 Subject: [PATCH 076/179] Handle tick reset next to polling --- protocols/autonat/src/v2/client/behaviour.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index f89f46d813f..4996310e841 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -251,6 +251,8 @@ where } if self.next_tick.poll_unpin(cx).is_ready() { + self.next_tick.reset(self.config.probe_interval); + self.inject_address_candiate_test(); continue; } @@ -307,7 +309,6 @@ where { self.submit_req_for_peer(*peer_id, addrs); } - self.next_tick.reset(self.config.probe_interval); } fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { From 2a6ad947caece2a07bcb26b8d84a3d49ad3a8f63 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:52:22 +1100 Subject: [PATCH 077/179] Rewrite generation of dial requests An AutoNAT server will always only dial a single address. Thus, we are more likely to get a quicker results for all our candidates if we send each candidate to a different server. --- protocols/autonat/src/v2/client/behaviour.rs | 62 ++++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 4996310e841..b0c73c40d54 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -280,54 +280,51 @@ where /// Inject an immediate test for all pending address candidates. fn inject_address_candiate_test(&mut self) { - if self.peer_info.values().all(|info| !info.supports_autonat) { - return; + for addr in self.untested_candidates() { + let Some((conn_id, peer_id)) = self.random_autonat_server() else { + tracing::debug!("Not connected to any AutoNAT servers"); + return; + }; + + let nonce = self.rng.gen(); + self.pending_nonces.insert(nonce, NonceStatus::Pending); + + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(conn_id), + event: Either::Left(DialRequest { + nonce, + addrs: vec![addr], + }), + }); } + } + fn untested_candidates(&self) -> impl Iterator { let mut entries = self .address_candidates .iter() .filter(|(_, info)| !info.is_tested) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); - if entries.is_empty() { - return; - } + entries.sort_unstable_by_key(|(_, count)| *count); - let addrs = entries - .iter() + + entries + .into_iter() .rev() - .map(|(addr, _)| addr) .take(self.config.max_candidates) - .cloned() - .collect(); - if let Some(ConnectionInfo { peer_id, .. }) = self - .peer_info - .values() - .filter(|e| e.supports_autonat) - .choose(&mut self.rng) - { - self.submit_req_for_peer(*peer_id, addrs); - } + .map(|(addr, _)| addr) } - fn submit_req_for_peer(&mut self, peer: PeerId, addrs: Vec) { - let nonce = self.rng.gen(); - let req = DialRequest { nonce, addrs }; - self.pending_nonces.insert(nonce, NonceStatus::Pending); - if let Some(conn_id) = self + fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { + let (conn_id, info) = self .peer_info .iter() .filter(|(_, info)| info.supports_autonat) - .find(|(_, info)| info.peer_id == peer) - .map(|(id, _)| *id) - { - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id: peer, - handler: NotifyHandler::One(conn_id), - event: Either::Left(req), - }); - } + .choose(&mut self.rng)?; + + Some((*conn_id, info.peer_id)) } fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { @@ -421,6 +418,7 @@ enum NonceStatus { Received, } +#[derive(Clone, Copy)] struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, From b2d147f4c658cb2e81564bbe9aeae08240040166 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 13:57:43 +1100 Subject: [PATCH 078/179] Add docs and additional log statements for no candidates --- protocols/autonat/src/v2/client/behaviour.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index b0c73c40d54..12f5c052af5 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -253,7 +253,7 @@ where if self.next_tick.poll_unpin(cx).is_ready() { self.next_tick.reset(self.config.probe_interval); - self.inject_address_candiate_test(); + self.issue_dial_requests_for_untested_candidates(); continue; } @@ -278,8 +278,11 @@ where } } - /// Inject an immediate test for all pending address candidates. - fn inject_address_candiate_test(&mut self) { + /// Issues dial requests to random AutoNAT servers for the most frequently reported, untested candidates. + /// + /// In the current implementation, we only send a single address to each AutoNAT server. + /// This spreads our candidates out across all servers we are connected to which should give us pretty fast feedback on all of them. + fn issue_dial_requests_for_untested_candidates(&mut self) { for addr in self.untested_candidates() { let Some((conn_id, peer_id)) = self.random_autonat_server() else { tracing::debug!("Not connected to any AutoNAT servers"); @@ -300,6 +303,9 @@ where } } + /// Returns all untested candidates, sorted by the frequency they were reported at. + /// + /// More frequently reported candidates are considered to more likely be external addresses and thus tested first. fn untested_candidates(&self) -> impl Iterator { let mut entries = self .address_candidates @@ -310,6 +316,10 @@ where entries.sort_unstable_by_key(|(_, count)| *count); + if entries.is_empty() { + tracing::debug!("No untested address candidates"); + } + entries .into_iter() .rev() @@ -418,7 +428,6 @@ enum NonceStatus { Received, } -#[derive(Clone, Copy)] struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, From 7e3da61c5a7d063ee23ec8effb991da2c9ef01ae Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:01:51 +1100 Subject: [PATCH 079/179] Add more docs and rename protocol name constants --- protocols/autonat/src/v2.rs | 4 ++-- protocols/autonat/src/v2/client/behaviour.rs | 1 + protocols/autonat/src/v2/client/handler/dial_back.rs | 4 ++-- protocols/autonat/src/v2/client/handler/dial_request.rs | 6 +++--- protocols/autonat/src/v2/server/handler/dial_back.rs | 4 ++-- protocols/autonat/src/v2/server/handler/dial_request.rs | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 0fae928921b..885c8fda4d0 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -6,9 +6,9 @@ mod global_only; pub(crate) mod protocol; pub mod server; -pub(crate) const REQUEST_PROTOCOL_NAME: StreamProtocol = +pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); -pub(crate) const DIAL_BACK_PROTOCOL_NAME: StreamProtocol = +pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-back"); type Nonce = u64; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 12f5c052af5..897184b2c02 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -327,6 +327,7 @@ where .map(|(addr, _)| addr) } + /// Chooses an active connection to one of our peers that reported support for the [`DIAL_REQUEST_PROTOCOL`](crate::v2::DIAL_REQUEST_PROTOCOL) protocol. fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { let (conn_id, info) = self .peer_info diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 492cc7729a2..18bfee792f1 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -12,7 +12,7 @@ use libp2p_swarm::{ }; use void::Void; -use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; pub struct Handler { inbound: FuturesSet>, @@ -35,7 +35,7 @@ impl ConnectionHandler for Handler { type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()) + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()) } fn poll( diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 0850c4a2bb3..7ff57f9fe8c 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -28,7 +28,7 @@ use crate::v2::{ Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, - REQUEST_PROTOCOL_NAME, + DIAL_REQUEST_PROTOCOL, }; #[derive(Debug, thiserror::Error)] @@ -108,7 +108,7 @@ impl Handler { self.queued_streams.push_back(tx); self.queued_events .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()), }); if self .outbound @@ -200,7 +200,7 @@ impl ConnectionHandler for Handler { } }, ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { - if added.any(|p| p.as_ref() == REQUEST_PROTOCOL_NAME) { + if added.any(|p| p.as_ref() == DIAL_REQUEST_PROTOCOL) { self.queued_events .push_back(ConnectionHandlerEvent::NotifyBehaviour( ToBehaviour::PeerHasServerSupport, diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index f671434eabe..d9a6e61f4be 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -14,7 +14,7 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL_NAME}; +use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -70,7 +70,7 @@ impl ConnectionHandler for Handler { if let Some(cmd) = self.pending_nonce.take() { self.requested_substream_nonce = Some(cmd); return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL_NAME), ()), + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()), }); } Poll::Pending diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index beb17d38b7f..04e61d54082 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -25,7 +25,7 @@ use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::StatusUpdate, - Nonce, REQUEST_PROTOCOL_NAME, + Nonce, DIAL_REQUEST_PROTOCOL, }; #[derive(Debug, PartialEq)] @@ -92,7 +92,7 @@ where type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(REQUEST_PROTOCOL_NAME), ()) + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()) } fn poll( From e2fe41dbb1d6589bd09498b4636804c5018cffed Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:48:42 +1100 Subject: [PATCH 080/179] Inline `DialBack` struct --- protocols/autonat/src/v2/protocol.rs | 56 +++++++++++----------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 69f80bf8d10..ee696f3835b 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, io}; use asynchronous_codec::{Framed, FramedRead, FramedWrite}; -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; @@ -266,46 +266,32 @@ impl DialDataRequest { } } -pub(crate) async fn dial_back(mut stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let dial_back = DialBack { nonce }; - dial_back.write_into(&mut stream).await?; - stream.close().await -} +const DIAL_BACK_MAX_SIZE: usize = 10; -pub(crate) async fn recv_dial_back( - mut stream: impl AsyncRead + AsyncWrite + Unpin, -) -> io::Result { - let DialBack { nonce } = DialBack::read_from(&mut stream).await?; - stream.close().await?; - Ok(nonce) -} +pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let msg = proto::DialBack { nonce: Some(nonce) }; + let mut framed = FramedWrite::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); -const DIAL_BACK_MAX_SIZE: usize = 10; + framed + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + framed.close().await?; -pub(crate) struct DialBack { - pub(crate) nonce: Nonce, + Ok(()) } -impl DialBack { - pub(crate) async fn read_from(io: impl AsyncRead + Unpin) -> io::Result { - let proto::DialBack { nonce } = - FramedRead::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) - .next() - .await - .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - let nonce = ok_or_invalid_data!(nonce)?; - Ok(Self { nonce }) - } +pub(crate) async fn recv_dial_back( + stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result { + let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); + let proto::DialBack { nonce } = framed + .next() + .await + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; + let nonce = ok_or_invalid_data!(nonce)?; - async fn write_into(self, io: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = proto::DialBack { - nonce: Some(self.nonce), - }; - FramedWrite::new(io, Codec::::new(DIAL_BACK_MAX_SIZE)) - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) - } + Ok(nonce) } #[cfg(test)] From 1e1aeca9031a8579450da73c5311e7dddc1b61d8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:58:53 +1100 Subject: [PATCH 081/179] Simplify handling of closed connection --- protocols/autonat/src/v2/client/behaviour.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 897184b2c02..e154b8b3702 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -140,7 +140,14 @@ where connection_id, .. }) => { - self.handle_no_connection(peer_id, connection_id); + let info = self + .peer_info + .remove(&connection_id) + .expect("inconsistent state"); + + if info.supports_autonat { + tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); + } } FromSwarm::DialFailure(DialFailure { peer_id: Some(peer_id), From d5724c13f17b5af70ba3dbf7a4f197e6aaa2cdee Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 14:59:47 +1100 Subject: [PATCH 082/179] ConnectionIds are unique, meaning we can always just insert --- protocols/autonat/src/v2/client/behaviour.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e154b8b3702..07f2ac00a2c 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -127,13 +127,14 @@ where endpoint, .. }) => { - self.peer_info - .entry(connection_id) - .or_insert(ConnectionInfo { + self.peer_info.insert( + connection_id, + ConnectionInfo { peer_id, supports_autonat: false, is_local: addr_is_local(endpoint.get_remote_address()), - }); + }, + ); } FromSwarm::ConnectionClosed(ConnectionClosed { peer_id, From de91c5553a252e9a97580aa1bc4088106178bfd3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:00:53 +1100 Subject: [PATCH 083/179] Remove handling of `DialFailure` We only ever insert into the `peer_info` map for established connections. We either establish a connection or receive a `DialFailure`. Thus, there will never be any state in `peer_info` for a failed connection so there is nothing to be cleaned up. --- protocols/autonat/src/v2/client/behaviour.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 07f2ac00a2c..e1ef0866b52 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -11,7 +11,7 @@ use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, - ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, + ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; use rand::prelude::*; @@ -150,13 +150,6 @@ where tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); } } - FromSwarm::DialFailure(DialFailure { - peer_id: Some(peer_id), - connection_id, - .. - }) => { - self.handle_no_connection(peer_id, connection_id); - } _ => {} } } From 3719440bdf98101e3f71bf0b2d0a9234c1a15a7d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:04:51 +1100 Subject: [PATCH 084/179] Disable connection for future autonat requests on error --- protocols/autonat/src/v2/client/behaviour.rs | 39 +++++--------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e1ef0866b52..fe8f201c898 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -216,14 +216,20 @@ where } tracing::debug!(%peer_id, %addr, "Server failed to dial address, candidate is not a public address") } - dial_request::InternalError::InternalServer + e @ (dial_request::InternalError::InternalServer | dial_request::InternalError::DataRequestTooLarge { .. } | dial_request::InternalError::DataRequestTooSmall { .. } | dial_request::InternalError::InvalidResponse | dial_request::InternalError::ServerRejectedDialRequest | dial_request::InternalError::InvalidReferencedAddress { .. } - | dial_request::InternalError::ServerChoseNotToDialAnyAddress => { - self.handle_no_connection(peer_id, connection_id); + | dial_request::InternalError::ServerChoseNotToDialAnyAddress) => { + // Disable this server for future requests. + self.peer_info + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; + + tracing::debug!(%peer_id, %connection_id, %e, "Disabling server for future AutoNAT requests"); } _ => { tracing::debug!("Test failed: {err}"); @@ -339,33 +345,6 @@ where Some((*conn_id, info.peer_id)) } - fn handle_no_connection(&mut self, peer_id: PeerId, connection_id: ConnectionId) { - let removeable_conn_ids = self - .peer_info - .iter() - .filter(|(conn_id, info)| info.peer_id == peer_id && **conn_id == connection_id) - .map(|(id, _)| *id) - .collect::>(); - for conn_id in removeable_conn_ids { - self.peer_info.remove(&conn_id); - } - let known_servers_n = self - .peer_info - .values() - .filter(|info| info.supports_autonat) - .count(); - let changed_n = self - .peer_info - .values_mut() - .filter(|info| info.supports_autonat) - .filter(|info| info.peer_id == peer_id) - .map(|info| info.supports_autonat = false) - .count(); - if known_servers_n != changed_n { - tracing::trace!(server = %peer_id, "Removing potential Autonat server due to dial failure"); - } - } - pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { info.is_tested = true; From 7b6d35473fea20f773e3ff43a26f5540f7e2f41d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:06:39 +1100 Subject: [PATCH 085/179] Remove unnecessary trait implementations --- protocols/autonat/src/v2/client/behaviour.rs | 22 +------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index fe8f201c898..5ca8d029b40 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -321,7 +321,7 @@ where .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); - entries.sort_unstable_by_key(|(_, count)| *count); + entries.sort_unstable_by_key(|(_, info)| info.score); if entries.is_empty() { tracing::debug!("No untested address candidates"); @@ -420,23 +420,3 @@ struct AddressInfo { score: usize, is_tested: bool, } - -impl PartialOrd for AddressInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.score.cmp(&other.score)) - } -} - -impl PartialEq for AddressInfo { - fn eq(&self, other: &Self) -> bool { - self.score == other.score - } -} - -impl Ord for AddressInfo { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.score.cmp(&other.score) - } -} - -impl Eq for AddressInfo {} From 8ce2a01a5e2553f1c9e4ea18e6f91ff0d4ba7b9a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 15:07:19 +1100 Subject: [PATCH 086/179] Add comment --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 5ca8d029b40..0022d07f643 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -329,7 +329,7 @@ where entries .into_iter() - .rev() + .rev() // `sort_unstable` is ascending .take(self.config.max_candidates) .map(|(addr, _)| addr) } From 42ac03c60058a0c9de5d99067ff99301a16a6e8d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 16:43:13 +1100 Subject: [PATCH 087/179] Rewrite dial-request to always index by nonce The nonce of a probe is essentially like a primary key that is decided by the client. Thus, any event emitted by the handler should be indexed by the provided nonce. We can achieve this by using a `FuturesMap` instead of a `FuturesSet`. This gives us access to the nonce even in the case that the actual protocol times out. With the nonce in place, we had to re-model the event returned to the behaviour. Most importantly. we need to separate the different kinds of errors: - Complete execution of the protocol but address is not reachable - Protocol was aborted mid-way - Server does not support the protocol We can't really do anything if the protocols is aborted so we just represent this case with an `io::Error` that gets logged further up. As a result, this means we can remove the `Option` from the event emitted to the user and _always_ give them a `Multiaddr`. --- protocols/autonat/src/v2/client/behaviour.rs | 158 ++++---- protocols/autonat/src/v2/client/handler.rs | 2 - .../src/v2/client/handler/dial_request.rs | 341 ++++++++---------- protocols/autonat/src/v2/protocol.rs | 55 ++- 4 files changed, 238 insertions(+), 318 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 0022d07f643..7d558f6434a 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -18,14 +18,9 @@ use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::v2::client::handler::dial_request::InternalError; use crate::v2::{global_only::IpExt, protocol::DialRequest}; -use super::handler::{ - dial_back, - dial_request::{self, InternalStatusUpdate}, - TestEnd, -}; +use super::handler::{dial_back, dial_request}; #[derive(Debug, Clone, Copy)] pub struct Config { @@ -118,7 +113,7 @@ where } FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { if let Some(info) = self.address_candidates.get_mut(addr) { - info.is_tested = true; + info.status = TestStatus::Tested; } } FromSwarm::ConnectionEstablished(ConnectionEstablished { @@ -160,7 +155,7 @@ where connection_id: ConnectionId, event: ::ToBehaviour, ) { - match event { + let (nonce, outcome) = match event { Either::Right(nonce) => { let Some(status) = self.pending_nonces.get_mut(&nonce) else { tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); @@ -169,82 +164,63 @@ where *status = NonceStatus::Received; tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + return; } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { self.peer_info .get_mut(&connection_id) .expect("inconsistent state") .supports_autonat = true; + return; + } + Either::Left(dial_request::ToBehaviour::TestOutcome { nonce, outcome }) => { + (nonce, outcome) } - Either::Left(dial_request::ToBehaviour::TestCompleted(InternalStatusUpdate { - tested_addr, - bytes_sent: data_amount, - result, - server_no_support, - })) => { - if server_no_support { - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = false; + }; + + let nonce_status = self.pending_nonces.remove(&nonce); + + let ((tested_addr, bytes_sent), result) = match outcome { + Ok(address) => { + if nonce_status != Some(NonceStatus::Received) { + tracing::warn!( + %peer_id, + %nonce, + "Server reported reachbility but we never received a dial-back" + ); + return; } - match result { - Ok(TestEnd { - dial_request: DialRequest { nonce, .. }, - ref reachable_addr, - }) => { - if !matches!(self.pending_nonces.get(&nonce), Some(NonceStatus::Received)) { - tracing::warn!( - %peer_id, - %nonce, - "Server reported reachbility but we never received a dial-back" - ); - return; - } - - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(reachable_addr.clone())); - } - Err(ref err) => match &err.internal { - dial_request::InternalError::FailureDuringDialBack { addr: Some(addr) } - | dial_request::InternalError::UnableToConnectOnSelectedAddress { - addr: Some(addr), - } => { - if let Some(address_info) = self.address_candidates.get_mut(addr) { - address_info.is_tested = true; - } - tracing::debug!(%peer_id, %addr, "Server failed to dial address, candidate is not a public address") - } - e @ (dial_request::InternalError::InternalServer - | dial_request::InternalError::DataRequestTooLarge { .. } - | dial_request::InternalError::DataRequestTooSmall { .. } - | dial_request::InternalError::InvalidResponse - | dial_request::InternalError::ServerRejectedDialRequest - | dial_request::InternalError::InvalidReferencedAddress { .. } - | dial_request::InternalError::ServerChoseNotToDialAnyAddress) => { - // Disable this server for future requests. - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = false; - - tracing::debug!(%peer_id, %connection_id, %e, "Disabling server for future AutoNAT requests"); - } - _ => { - tracing::debug!("Test failed: {err}"); - } - }, - } - let event = crate::v2::client::Event { - tested_addr, - bytes_sent: data_amount, - server: peer_id, - result: result.map(|_| ()), - }; - self.pending_events.push_back(ToSwarm::GenerateEvent(event)); + (address, Ok(())) } - } + Err(dial_request::Error::UnsupportedProtocol) => { + self.peer_info + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; + return; + } + Err(dial_request::Error::Io(e)) => { + tracing::debug!( + %peer_id, + %nonce, + "Failed to complete AutoNAT probe: {e}" + ); + return; + } + Err(dial_request::Error::AddressNotReachable { + address, + bytes_sent, + error, + }) => ((address, bytes_sent), Err(error)), + }; + + self.pending_events.push_back(ToSwarm::GenerateEvent(Event { + tested_addr, + bytes_sent, + server: peer_id, + result: result.map_err(|e| Error { inner: e }), + })); } fn poll( @@ -298,6 +274,10 @@ where let nonce = self.rng.gen(); self.pending_nonces.insert(nonce, NonceStatus::Pending); + self.address_candidates + .get_mut(&addr) + .expect("only emit candidates") + .status = TestStatus::Pending; self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id, @@ -317,7 +297,7 @@ where let mut entries = self .address_candidates .iter() - .filter(|(_, info)| !info.is_tested) + .filter(|(_, info)| info.status == TestStatus::Untested) .map(|(addr, count)| (addr.clone(), *count)) .collect::>(); @@ -347,7 +327,7 @@ where pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { - info.is_tested = true; + info.status = TestStatus::Tested; } } } @@ -359,32 +339,25 @@ impl Default for Behaviour { } pub struct Error { - pub(crate) internal: InternalError, -} - -impl From for Error { - fn from(internal: InternalError) -> Self { - Self { internal } - } + pub(crate) inner: dial_request::DialBackError, } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(&self.internal, f) + Display::fmt(&self.inner, f) } } impl Debug for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self.internal, f) + Debug::fmt(&self.inner, f) } } #[derive(Debug)] pub struct Event { /// The address that was selected for testing. - /// Is `None` in the case that the server respond with something unexpected. - pub tested_addr: Option, + pub tested_addr: Multiaddr, /// The amount of data that was sent to the server. /// Is 0 if it wasn't necessary to send any data. /// Otherwise it's a number between 30.000 and 100.000. @@ -404,6 +377,7 @@ fn addr_is_local(addr: &Multiaddr) -> bool { }) } +#[derive(Debug, PartialEq)] enum NonceStatus { Pending, Received, @@ -418,5 +392,13 @@ struct ConnectionInfo { #[derive(Copy, Clone, Default)] struct AddressInfo { score: usize, - is_tested: bool, + status: TestStatus, +} + +#[derive(Clone, Copy, Default, PartialEq)] +enum TestStatus { + #[default] + Untested, + Pending, + Tested, } diff --git a/protocols/autonat/src/v2/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs index bb6c9636e2c..e526c2fb44c 100644 --- a/protocols/autonat/src/v2/client/handler.rs +++ b/protocols/autonat/src/v2/client/handler.rs @@ -1,4 +1,2 @@ pub(crate) mod dial_back; pub(crate) mod dial_request; - -pub(crate) use dial_request::TestEnd; diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 7ff57f9fe8c..4b071894215 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -1,5 +1,5 @@ -use futures::{channel::oneshot, AsyncRead, AsyncWrite}; -use futures_bounded::FuturesSet; +use futures::{channel::oneshot, AsyncWrite}; +use futures_bounded::FuturesMap; use libp2p_core::{ upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, @@ -22,57 +22,49 @@ use std::{ }; use crate::v2::{ - client::behaviour::Error, generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{ - Coder, DialDataRequest, DialDataResponse, DialRequest, DialResponse, Request, Response, + Coder, DialDataRequest, DialDataResponse, DialRequest, Response, DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, }, - DIAL_REQUEST_PROTOCOL, + Nonce, DIAL_REQUEST_PROTOCOL, }; -#[derive(Debug, thiserror::Error)] -pub(crate) enum InternalError { - #[error("io error")] - Io(#[from] io::Error), - #[error("invalid referenced address index: {index} (max number of addr: {max})")] - InvalidReferencedAddress { index: usize, max: usize }, - #[error("data request too large: {len} (max: {max})")] - DataRequestTooLarge { len: usize, max: usize }, - #[error("data request too small: {len} (min: {min})")] - DataRequestTooSmall { len: usize, min: usize }, - #[error("server rejected dial request")] - ServerRejectedDialRequest, - #[error("server chose not to dial any provided address")] - ServerChoseNotToDialAnyAddress, - #[error("server ran into an internal error")] - InternalServer, - #[error("server did not respond correctly to dial request")] - InvalidResponse, - #[error("server was unable to connect to address: {addr:?}")] - UnableToConnectOnSelectedAddress { addr: Option }, - #[error("server experienced failure during dial back on address: {addr:?}")] - FailureDuringDialBack { addr: Option }, +#[derive(Debug)] +pub enum ToBehaviour { + TestOutcome { + nonce: Nonce, + outcome: Result<(Multiaddr, usize), Error>, + }, + PeerHasServerSupport, } -#[derive(Debug)] -pub struct InternalStatusUpdate { - pub(crate) tested_addr: Option, - pub(crate) bytes_sent: usize, - pub result: Result, - pub(crate) server_no_support: bool, +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Address is not reachable: {error}")] + AddressNotReachable { + address: Multiaddr, + bytes_sent: usize, + error: DialBackError, + }, + #[error("Peer does not support AutoNAT dial-request protocol")] + UnsupportedProtocol, + #[error("IO error: {0}")] + Io(io::Error), } -#[derive(Debug)] -pub struct TestEnd { - pub(crate) dial_request: DialRequest, - pub(crate) reachable_addr: Multiaddr, +impl From for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } } -#[derive(Debug)] -pub enum ToBehaviour { - TestCompleted(InternalStatusUpdate), - PeerHasServerSupport, +#[derive(thiserror::Error, Debug)] +pub enum DialBackError { + #[error("server failed to establish a connection")] + NoConnection, + #[error("dial back stream failed")] + StreamFailed, } pub struct Handler { @@ -83,7 +75,7 @@ pub struct Handler { ::ToBehaviour, >, >, - outbound: futures_bounded::FuturesSet, + outbound: FuturesMap>, queued_streams: VecDeque< oneshot::Sender< Result< @@ -98,7 +90,7 @@ impl Handler { pub(crate) fn new() -> Self { Self { queued_events: VecDeque::new(), - outbound: FuturesSet::new(Duration::from_secs(10), 10), + outbound: FuturesMap::new(Duration::from_secs(10), 10), queued_streams: VecDeque::default(), } } @@ -112,7 +104,7 @@ impl Handler { }); if self .outbound - .try_push(start_stream_handle(req, rx)) + .try_push(req.nonce, start_stream_handle(req, rx)) .is_err() { tracing::debug!("Dial request dropped, too many requests in flight"); @@ -141,22 +133,24 @@ impl ConnectionHandler for Handler { if let Some(event) = self.queued_events.pop_front() { return Poll::Ready(event); } - if let Poll::Ready(m) = self.outbound.poll_unpin(cx) { - let status_update = match m { - Ok(ok) => ok, - Err(_) => InternalStatusUpdate { - tested_addr: None, - bytes_sent: 0, - result: Err(Error { - internal: InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)), - }), - server_no_support: false, - }, - }; - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestCompleted(status_update), - )); + + match self.outbound.poll_unpin(cx) { + Poll::Ready((nonce, Ok(outcome))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestOutcome { nonce, outcome }, + )) + } + Poll::Ready((nonce, Err(_))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestOutcome { + nonce, + outcome: Err(Error::Io(io::ErrorKind::TimedOut.into())), + }, + )); + } + Poll::Pending => {} } + Poll::Pending } @@ -213,177 +207,126 @@ impl ConnectionHandler for Handler { } async fn start_stream_handle( - dial_request: DialRequest, - stream_recv: oneshot::Receiver< - Result< - Stream, - StreamUpgradeError< as OutboundUpgradeSend>::Error>, - >, - >, -) -> InternalStatusUpdate { - let mut server_no_support = false; - let substream_result = match stream_recv.await { - Ok(Ok(substream)) => Ok(substream), - Ok(Err(StreamUpgradeError::Io(io))) => Err(InternalError::from(io).into()), - Ok(Err(StreamUpgradeError::Timeout)) => { - Err(InternalError::Io(io::Error::from(io::ErrorKind::TimedOut)).into()) - } - Ok(Err(StreamUpgradeError::Apply(upgrade_error))) => void::unreachable(upgrade_error), - Ok(Err(StreamUpgradeError::NegotiationFailed)) => { - server_no_support = true; - Err( - InternalError::Io(io::Error::new(io::ErrorKind::Other, "negotiation failed")) - .into(), - ) - } - Err(_) => Err(InternalError::InternalServer.into()), - }; - let substream = match substream_result { - Ok(substream) => substream, - Err(err) => { - let status_update = InternalStatusUpdate { - tested_addr: None, - bytes_sent: 0, - result: Err(err), - server_no_support, - }; - return status_update; - } - }; - let mut data_amount = 0; - let mut checked_addr_idx = None; - let addrs = dial_request.addrs.clone(); - assert_ne!(addrs, vec![]); - let result = handle_stream( - dial_request, - substream, - &mut data_amount, - &mut checked_addr_idx, - ) - .await - .map_err(crate::v2::client::behaviour::Error::from); - InternalStatusUpdate { - tested_addr: checked_addr_idx.and_then(|idx| addrs.get(idx).cloned()), - bytes_sent: data_amount, - result, - server_no_support, - } -} + req: DialRequest, + stream_recv: oneshot::Receiver>>, +) -> Result<(Multiaddr, usize), Error> { + let stream = stream_recv + .await + .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))? + .map_err(|e| match e { + StreamUpgradeError::NegotiationFailed => Error::UnsupportedProtocol, + StreamUpgradeError::Timeout => Error::Io(io::ErrorKind::TimedOut.into()), + StreamUpgradeError::Apply(v) => void::unreachable(v), + StreamUpgradeError::Io(e) => Error::Io(e), + })?; -async fn handle_stream( - dial_request: DialRequest, - stream: impl AsyncRead + AsyncWrite + Unpin, - data_amount: &mut usize, - checked_addr_idx: &mut Option, -) -> Result { let mut coder = Coder::new(stream); - coder.send(Request::Dial(dial_request.clone())).await?; - match coder.next().await? { + coder.send(req.clone()).await?; + + let (res, bytes_sent) = match coder.next().await? { Response::Data(DialDataRequest { addr_idx, num_bytes, }) => { - if addr_idx >= dial_request.addrs.len() { - return Err(InternalError::InvalidReferencedAddress { - index: addr_idx, - max: dial_request.addrs.len(), - }); - } - if num_bytes > DATA_LEN_UPPER_BOUND { - return Err(InternalError::DataRequestTooLarge { - len: num_bytes, - max: DATA_LEN_UPPER_BOUND, - }); + if addr_idx >= req.addrs.len() { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "address index out of bounds", + ))); } - if num_bytes < DATA_LEN_LOWER_BOUND { - return Err(InternalError::DataRequestTooSmall { - len: num_bytes, - min: DATA_LEN_LOWER_BOUND, - }); - } - *checked_addr_idx = Some(addr_idx); - send_aap_data(&mut coder, num_bytes, data_amount).await?; - if let Response::Dial(dial_response) = coder.next().await? { - *checked_addr_idx = Some(dial_response.addr_idx); - coder.close().await?; - test_end_from_dial_response(dial_request, dial_response) - } else { - Err(InternalError::InternalServer) + if num_bytes > DATA_LEN_UPPER_BOUND || num_bytes < DATA_LEN_LOWER_BOUND { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "requested bytes out of bounds", + ))); } + + send_aap_data(&mut coder, num_bytes).await?; + + let Response::Dial(dial_response) = coder.next().await? else { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "expected message", + ))); + }; + + (dial_response, num_bytes) } - Response::Dial(dial_response) => { - *checked_addr_idx = Some(dial_response.addr_idx); - coder.close().await?; - test_end_from_dial_response(dial_request, dial_response) + Response::Dial(dial_response) => (dial_response, 0), + }; + coder.close().await?; + + match res.status { + ResponseStatus::E_REQUEST_REJECTED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server rejected request", + ))) + } + ResponseStatus::E_DIAL_REFUSED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server refused dial", + ))) + } + ResponseStatus::E_INTERNAL_ERROR => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server encountered internal error", + ))) } + ResponseStatus::OK => {} } -} -fn test_end_from_dial_response( - req: DialRequest, - resp: DialResponse, -) -> Result { - if resp.addr_idx >= req.addrs.len() { - return Err(InternalError::InvalidReferencedAddress { - index: resp.addr_idx, - max: req.addrs.len(), - }); - } - match (resp.status, resp.dial_status) { - (ResponseStatus::E_REQUEST_REJECTED, _) => Err(InternalError::ServerRejectedDialRequest), - (ResponseStatus::E_DIAL_REFUSED, _) => Err(InternalError::ServerChoseNotToDialAnyAddress), - (ResponseStatus::E_INTERNAL_ERROR, _) => Err(InternalError::InternalServer), - (ResponseStatus::OK, DialStatus::UNUSED) => Err(InternalError::InvalidResponse), - (ResponseStatus::OK, DialStatus::E_DIAL_ERROR) => { - Err(InternalError::UnableToConnectOnSelectedAddress { - addr: req.addrs.get(resp.addr_idx).cloned(), - }) + let tested_address = req + .addrs + .get(res.addr_idx) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "address index out of bounds"))? + .clone(); + + match res.dial_status { + DialStatus::UNUSED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "unexpected message", + ))) } - (ResponseStatus::OK, DialStatus::E_DIAL_BACK_ERROR) => { - Err(InternalError::FailureDuringDialBack { - addr: req.addrs.get(resp.addr_idx).cloned(), + DialStatus::E_DIAL_ERROR => { + return Err(Error::AddressNotReachable { + address: tested_address, + bytes_sent, + error: DialBackError::NoConnection, }) } - (ResponseStatus::OK, DialStatus::OK) => req - .addrs - .get(resp.addr_idx) - .ok_or(InternalError::InvalidReferencedAddress { - index: resp.addr_idx, - max: req.addrs.len(), + DialStatus::E_DIAL_BACK_ERROR => { + return Err(Error::AddressNotReachable { + address: tested_address, + bytes_sent, + error: DialBackError::StreamFailed, }) - .cloned() - .map(|reachable_addr| TestEnd { - dial_request: req, - reachable_addr, - }), + } + DialStatus::OK => {} } + + Ok((tested_address, bytes_sent)) } -async fn send_aap_data( - stream: &mut Coder, - num_bytes: usize, - data_amount: &mut usize, -) -> io::Result<()> +async fn send_aap_data(stream: &mut Coder, num_bytes: usize) -> io::Result<()> where I: AsyncWrite + Unpin, { let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; - for (data_count, req) in repeat(DATA_FIELD_LEN_UPPER_BOUND) + for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) .take(count_full) .chain(once(partial_len)) .filter(|e| *e > 0) .map(|data_count| { - ( - data_count, - Request::Data( - DialDataResponse::new(data_count).expect("data count is unexpectedly too big"), - ), - ) + DialDataResponse::new(data_count).expect("data count is unexpectedly too big") }) { - *data_amount += data_count; stream.send(req).await?; } + Ok(()) } diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index ee696f3835b..b4a1cbab4f6 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -90,6 +90,32 @@ pub(crate) enum Request { Data(DialDataResponse), } +impl From for proto::Message { + fn from(val: DialRequest) -> Self { + let addrs = val.addrs.iter().map(|e| e.to_vec()).collect(); + let nonce = Some(val.nonce); + + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }), + } + } +} + +impl From for proto::Message { + fn from(val: DialDataResponse) -> Self { + debug_assert!( + val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { + data: Some(Cow::Borrowed(&DATA[..val.data_count])), + }), + } + } +} + #[derive(Debug, Clone, PartialEq)] pub struct DialRequest { pub(crate) addrs: Vec, @@ -144,35 +170,6 @@ impl TryFrom for Request { } } -impl Into for Request { - fn into(self) -> proto::Message { - match self { - Request::Dial(DialRequest { addrs, nonce }) => { - let addrs = addrs.iter().map(|e| e.to_vec()).collect(); - let nonce = Some(nonce); - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { - addrs, - nonce, - }), - } - } - Request::Data(DialDataResponse { data_count }) => { - debug_assert!( - data_count <= DATA_FIELD_LEN_UPPER_BOUND, - "data_count too large" - ); - static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(Cow::Borrowed(&DATA[..data_count])), - }), - } - } - } - } -} - #[derive(Debug, Clone)] pub(crate) enum Response { Dial(DialResponse), From 318111384310f5214258584e8499cfed73865fd5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sat, 30 Dec 2023 17:28:36 +1100 Subject: [PATCH 088/179] Reset TestStatus on errors --- protocols/autonat/src/v2/client/behaviour.rs | 89 ++++++++++++++------ 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 7d558f6434a..a7836add87b 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -10,15 +10,14 @@ use futures_timer::Delay; use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ - behaviour::{ConnectionEstablished, ExternalAddrConfirmed}, - ConnectionClosed, ConnectionDenied, ConnectionHandler, ConnectionId, FromSwarm, - NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, + behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, + ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, }; use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::v2::{global_only::IpExt, protocol::DialRequest}; +use crate::v2::{global_only::IpExt, protocol::DialRequest, Nonce}; use super::handler::{dial_back, dial_request}; @@ -60,7 +59,6 @@ pub struct Behaviour where R: RngCore + 'static, { - pending_nonces: HashMap, rng: R, config: Config, pending_events: VecDeque< @@ -111,11 +109,6 @@ where .or_default() .score += 1; } - FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - if let Some(info) = self.address_candidates.get_mut(addr) { - info.status = TestStatus::Tested; - } - } FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, @@ -157,13 +150,18 @@ where ) { let (nonce, outcome) = match event { Either::Right(nonce) => { - let Some(status) = self.pending_nonces.get_mut(&nonce) else { + let Some((_, info)) = self + .address_candidates + .iter_mut() + .find(|(_, info)| info.is_pending_with_nonce(nonce)) + else { tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); return; }; - *status = NonceStatus::Received; + info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + return; } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { @@ -178,11 +176,14 @@ where } }; - let nonce_status = self.pending_nonces.remove(&nonce); - let ((tested_addr, bytes_sent), result) = match outcome { Ok(address) => { - if nonce_status != Some(NonceStatus::Received) { + let received_dial_back = self + .address_candidates + .iter_mut() + .any(|(_, info)| info.is_received_with_nonce(nonce)); + + if !received_dial_back { tracing::warn!( %peer_id, %nonce, @@ -198,6 +199,9 @@ where .get_mut(&connection_id) .expect("inconsistent state") .supports_autonat = false; + + self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. + return; } Err(dial_request::Error::Io(e)) => { @@ -206,13 +210,20 @@ where %nonce, "Failed to complete AutoNAT probe: {e}" ); + + self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. + return; } Err(dial_request::Error::AddressNotReachable { address, bytes_sent, error, - }) => ((address, bytes_sent), Err(error)), + }) => { + self.reset_status_to(nonce, TestStatus::Failed); + + ((address, bytes_sent), Err(error)) + } }; self.pending_events.push_back(ToSwarm::GenerateEvent(Event { @@ -251,7 +262,6 @@ where { pub fn new(rng: R, config: Config) -> Self { Self { - pending_nonces: HashMap::new(), rng, next_tick: Delay::new(config.probe_interval), config, @@ -273,11 +283,10 @@ where }; let nonce = self.rng.gen(); - self.pending_nonces.insert(nonce, NonceStatus::Pending); self.address_candidates .get_mut(&addr) .expect("only emit candidates") - .status = TestStatus::Pending; + .status = TestStatus::Pending(nonce); self.pending_events.push_back(ToSwarm::NotifyHandler { peer_id, @@ -325,9 +334,22 @@ where Some((*conn_id, info.peer_id)) } + fn reset_status_to(&mut self, nonce: Nonce, new_status: TestStatus) { + let Some((_, info)) = self + .address_candidates + .iter_mut() + .find(|(_, i)| i.is_pending_with_nonce(nonce) || i.is_received_with_nonce(nonce)) + else { + return; + }; + + info.status = new_status; + } + + // FIXME: We don't want test-only APIs in our public API. pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { - info.status = TestStatus::Tested; + info.status = TestStatus::Received(self.rng.next_u64()); } } } @@ -377,12 +399,6 @@ fn addr_is_local(addr: &Multiaddr) -> bool { }) } -#[derive(Debug, PartialEq)] -enum NonceStatus { - Pending, - Received, -} - struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, @@ -395,10 +411,27 @@ struct AddressInfo { status: TestStatus, } +impl AddressInfo { + fn is_pending_with_nonce(&self, nonce: Nonce) -> bool { + match self.status { + TestStatus::Pending(c) => c == nonce, + _ => false, + } + } + + fn is_received_with_nonce(&self, nonce: Nonce) -> bool { + match self.status { + TestStatus::Received(c) => c == nonce, + _ => false, + } + } +} + #[derive(Clone, Copy, Default, PartialEq)] enum TestStatus { #[default] Untested, - Pending, - Tested, + Pending(Nonce), + Failed, + Received(Nonce), } From 4d96c33d9768c78aa65ab3cbbcc32dfcf5646f8b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 14:49:18 +0100 Subject: [PATCH 089/179] Delay and use Result --- Cargo.lock | 13 ++++++++++ protocols/autonat/Cargo.toml | 1 + .../autonat/src/v1/behaviour/as_server.rs | 1 + protocols/autonat/src/v2/client/behaviour.rs | 1 + protocols/autonat/src/v2/protocol.rs | 22 ----------------- protocols/autonat/src/v2/server/behaviour.rs | 2 +- .../src/v2/server/handler/dial_back.rs | 6 ++--- .../src/v2/server/handler/dial_request.rs | 24 ++++++++++--------- protocols/autonat/tests/autonatv2.rs | 11 +++++---- 9 files changed, 40 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1ec0acdf05..5d3de078ee3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,6 +1736,18 @@ dependencies = [ "instant", ] +[[package]] +name = "futures-time" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6404853a6824881fe5f7d662d147dc4e84ecd2259ba0378f272a71dab600758a" +dependencies = [ + "async-channel", + "async-io 1.13.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "futures-timer" version = "3.0.2" @@ -2642,6 +2654,7 @@ dependencies = [ "either", "futures", "futures-bounded", + "futures-time", "futures-timer", "instant", "libp2p-core", diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index b20fb4c596c..3a79215e2cf 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -18,6 +18,7 @@ bytes = { version = "1", optional = true } either = { version = "1.9.0", optional = true } futures = "0.3" futures-bounded = { workspace = true, optional = true } +futures-time = "3" futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index 878fd713dda..4e3cfc77891 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -135,6 +135,7 @@ impl<'a> HandleInnerEvent for AsServer<'a> { NonZeroU8::new(1).expect("1 > 0"), ) .addresses(addrs) + .allocate_new_port() .build(), }, ]) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index a7836add87b..700a567e218 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -347,6 +347,7 @@ where } // FIXME: We don't want test-only APIs in our public API. + #[doc(hidden)] pub fn validate_addr(&mut self, addr: &Multiaddr) { if let Some(info) = self.address_candidates.get_mut(addr) { info.status = TestStatus::Received(self.rng.next_u64()); diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index b4a1cbab4f6..99c1541a8e8 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -297,7 +297,6 @@ mod tests { mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; use crate::v2::protocol::{Coder, DialDataResponse, Request}; - use futures::io::Cursor; use rand::{thread_rng, Rng}; @@ -328,25 +327,4 @@ mod tests { let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); } - - #[tokio::test] - async fn write_read_request() { - let mut buf = Cursor::new(Vec::new()); - let mut coder = Coder::new(&mut buf); - let mut all_req = Vec::with_capacity(100); - for _ in 0..100 { - let data_request: Request = Request::Data(DialDataResponse { - data_count: thread_rng().gen_range(0..4000), - }); - all_req.push(data_request.clone()); - coder.send(data_request.clone()).await.unwrap(); - } - let inner = coder.inner.into_inner(); - inner.set_position(0); - let mut coder = Coder::new(inner); - for i in 0..100 { - let read_data_request: Request = coder.next().await.unwrap(); - assert_eq!(read_data_request, all_req[i]); - } - } } diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 8f84da1e18f..a808e44c2fd 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -100,7 +100,7 @@ where if let Some(DialBackCommand { back_channel, .. }) = self.dialing_dial_back.remove(&connection_id) { - let _ = back_channel.send(DialBackStatus::DialErr); + let _ = back_channel.send(Err(DialBackStatus::DialErr)); } } _ => {} diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index d9a6e61f4be..4adefe51c87 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -108,7 +108,7 @@ impl ConnectionHandler for Handler { .. }) => { if let Some(cmd) = self.requested_substream_nonce.take() { - let _ = cmd.back_channel.send(DialBackRes::DialBackErr); + let _ = cmd.back_channel.send(Err(DialBackRes::DialBackErr)); } } _ => {} @@ -124,11 +124,11 @@ async fn perform_dial_back( .. }: DialBackCommand, ) -> io::Result<()> { + futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; let res = dial_back(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) - .map(|_| DialBackRes::Ok) - .unwrap_or_else(|e| e); + .map(|_| ()); back_channel .send(res) .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index 04e61d54082..bc004d4b541 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -34,14 +34,13 @@ pub(crate) enum DialBackStatus { DialErr, /// Failure during dial back DialBackErr, - Ok, } #[derive(Debug)] pub struct DialBackCommand { pub(crate) addr: Multiaddr, pub(crate) nonce: Nonce, - pub(crate) back_channel: oneshot::Sender, + pub(crate) back_channel: oneshot::Sender>, } pub struct Handler { @@ -172,7 +171,10 @@ enum HandleFail { InternalError(usize), RequestRejected, DialRefused, - DialBack { idx: usize, err: DialBackStatus }, + DialBack { + idx: usize, + result: Result<(), DialBackStatus>, + }, } impl From for DialResponse { @@ -193,13 +195,13 @@ impl From for DialResponse { addr_idx: 0, dial_status: DialStatus::UNUSED, }, - HandleFail::DialBack { idx, err } => Self { + HandleFail::DialBack { idx, result } => Self { status: ResponseStatus::OK, addr_idx: idx, - dial_status: match err { - DialBackStatus::DialErr => DialStatus::E_DIAL_ERROR, - DialBackStatus::DialBackErr => DialStatus::E_DIAL_BACK_ERROR, - DialBackStatus::Ok => DialStatus::OK, + dial_status: match result { + Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, + Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, + Ok(()) => DialStatus::OK, }, }, } @@ -267,14 +269,14 @@ where .await .map_err(|_| HandleFail::DialBack { idx, - err: DialBackStatus::DialErr, + result: Err(DialBackStatus::DialErr), })?; let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; - if dial_back != DialBackStatus::Ok { + if let Err(err) = dial_back { return Err(HandleFail::DialBack { idx, - err: dial_back, + result: Err(err), }); } Ok(DialResponse { diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 951610d84b2..2b896f7086d 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -123,7 +123,10 @@ async fn confirm_successful() { _ => None, }) .await; - assert_eq!(tested_addr, alice_bob_external_addrs.get(0).cloned()); + assert_eq!( + tested_addr, + alice_bob_external_addrs.get(0).cloned().unwrap() + ); assert_eq!(bytes_sent, 0); assert_eq!(server, cor_server_peer); assert!(result.is_ok(), "Result is {result:?}"); @@ -215,7 +218,7 @@ async fn dial_back_to_unsupported_protocol() { let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr: Some(tested_addr), + tested_addr, bytes_sent, server, result: Err(_), @@ -310,7 +313,7 @@ async fn dial_back_to_non_libp2p() { let data_amount = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr: Some(tested_addr), + tested_addr, bytes_sent, server, result: Err(_), @@ -409,7 +412,7 @@ async fn dial_back_to_not_supporting() { let bytes_sent = bob .wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr: Some(tested_addr), + tested_addr, bytes_sent, server, result: Err(_), From 8a135ea4093e46cd7a48a082bdc3a6940aebeab9 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:08:26 +0100 Subject: [PATCH 090/179] Perform sleep correct --- protocols/autonat/src/v2/server/handler/dial_back.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index 4adefe51c87..f419c9a6fc3 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -124,11 +124,11 @@ async fn perform_dial_back( .. }: DialBackCommand, ) -> io::Result<()> { - futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; let res = dial_back(stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| ()); + futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; back_channel .send(res) .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; From fac1196473b7923edc0e2dfa840ebffc2f933cdb Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:12:19 +0100 Subject: [PATCH 091/179] Remove is_local --- protocols/autonat/src/v2.rs | 1 - protocols/autonat/src/v2/client/behaviour.rs | 16 +- protocols/autonat/src/v2/global_only.rs | 247 ------------------- 3 files changed, 3 insertions(+), 261 deletions(-) delete mode 100644 protocols/autonat/src/v2/global_only.rs diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 885c8fda4d0..d95d51b79e9 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -2,7 +2,6 @@ use libp2p_swarm::StreamProtocol; pub mod client; mod generated; -mod global_only; pub(crate) mod protocol; pub mod server; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 700a567e218..baeddc5b1f6 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -7,7 +7,7 @@ use std::{ use either::Either; use futures::FutureExt; use futures_timer::Delay; -use libp2p_core::{multiaddr::Protocol, transport::PortUse, Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, @@ -17,7 +17,7 @@ use rand::prelude::*; use rand_core::OsRng; use std::fmt::{Debug, Display, Formatter}; -use crate::v2::{global_only::IpExt, protocol::DialRequest, Nonce}; +use crate::v2::{protocol::DialRequest, Nonce}; use super::handler::{dial_back, dial_request}; @@ -112,7 +112,7 @@ where FromSwarm::ConnectionEstablished(ConnectionEstablished { peer_id, connection_id, - endpoint, + endpoint: _, .. }) => { self.peer_info.insert( @@ -120,7 +120,6 @@ where ConnectionInfo { peer_id, supports_autonat: false, - is_local: addr_is_local(endpoint.get_remote_address()), }, ); } @@ -392,18 +391,9 @@ pub struct Event { pub result: Result<(), Error>, } -fn addr_is_local(addr: &Multiaddr) -> bool { - addr.iter().any(|c| match c { - Protocol::Ip4(ip) => !IpExt::is_global(&ip), - Protocol::Ip6(ip) => !IpExt::is_global(&ip), - _ => false, - }) -} - struct ConnectionInfo { peer_id: PeerId, supports_autonat: bool, - is_local: bool, } #[derive(Copy, Clone, Default)] diff --git a/protocols/autonat/src/v2/global_only.rs b/protocols/autonat/src/v2/global_only.rs deleted file mode 100644 index af788639399..00000000000 --- a/protocols/autonat/src/v2/global_only.rs +++ /dev/null @@ -1,247 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - -pub(crate) trait Ipv4Ext { - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - fn is_reserved(&self) -> bool; - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - fn is_benchmarking(&self) -> bool; - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - fn is_shared(&self) -> bool; - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - fn is_private(&self) -> bool; -} - -impl Ipv4Ext for Ipv4Addr { - #[inline] - fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - #[inline] - fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - #[inline] - fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - #[inline] - fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if (16..=31).contains(&b) => true, - [192, 168, ..] => true, - _ => false, - } - } -} - -pub(crate) trait Ipv6Ext { - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - fn is_unicast_link_local(&self) -> bool; - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - fn is_unique_local(&self) -> bool; - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - fn is_documentation(&self) -> bool; -} - -impl Ipv6Ext for Ipv6Addr { - #[inline] - fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - #[inline] - fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - #[inline] - fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } -} - -pub(crate) trait IpExt { - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and Ipv6Addr::is_global() for more details. - #[must_use] - fn is_global(&self) -> bool; -} - -impl IpExt for Ipv4Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[inline] - fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || Ipv4Ext::is_shared(self) - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || Ipv4Ext::is_benchmarking(self) - || Ipv4Ext::is_reserved(self) - || self.is_broadcast()) - } -} - -impl IpExt for Ipv6Addr { - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[inline] - fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x2f).contains(&b)) - )) - || Ipv6Ext::is_documentation(self) - || Ipv6Ext::is_unique_local(self) - || Ipv6Ext::is_unicast_link_local(self)) - } -} - -impl IpExt for IpAddr { - #[inline] - fn is_global(&self) -> bool { - match self { - Self::V4(v4) => IpExt::is_global(v4), - Self::V6(v6) => IpExt::is_global(v6), - } - } -} From 9ed44d9c762da72ba61d5cae544b9eceea641814 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 15:16:22 +0100 Subject: [PATCH 092/179] Remove the unnecessary check for confirmation --- protocols/autonat/tests/autonatv2.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 2b896f7086d..94f1bb18e5a 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -84,12 +84,11 @@ async fn confirm_successful() { }; let bob_task = async { - let address_candidate = bob - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; + bob.wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; let incoming_conn_id = bob .wait(|event| match event { SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), @@ -116,10 +115,6 @@ async fn confirm_successful() { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { Some(status_update) } - SwarmEvent::ExternalAddrConfirmed { address } => { - assert_eq!(address, address_candidate); - None - } _ => None, }) .await; @@ -565,7 +560,6 @@ async fn bootstrap() -> (Swarm, Swarm) { bob.wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), - SwarmEvent::ExternalAddrConfirmed { .. } => None, _ => None, }) .await; From a97470706ba4abb7df312e57104a831a3c42f4a2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 30 Dec 2023 17:04:42 +0100 Subject: [PATCH 093/179] Fix deprecation --- protocols/autonat/src/v1.rs | 2 +- protocols/autonat/src/v1/behaviour/as_server.rs | 1 - protocols/autonat/src/v2/protocol.rs | 3 --- protocols/autonat/tests/test_client.rs | 3 ++- protocols/autonat/tests/test_server.rs | 3 ++- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 245748125cf..07b08310871 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -21,7 +21,7 @@ //! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![deprecated(note = "Please use `v2` module instead.")] +#![cfg_attr(not(test), deprecated(note = "Please use `v2` module instead."))] pub(crate) mod behaviour; pub(crate) mod protocol; diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index 4e3cfc77891..e309023bc75 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -17,7 +17,6 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. - use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, ResponseError, diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 99c1541a8e8..f6776093868 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -296,9 +296,6 @@ mod tests { use crate::v2::generated::structs::{ mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, }; - use crate::v2::protocol::{Coder, DialDataResponse, Request}; - - use rand::{thread_rng, Rng}; #[test] fn message_correct_max_size() { diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 8eb7939fbdc..2b1460cac85 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -17,9 +17,10 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#![allow(deprecated)] use async_std::task::JoinHandle; -use libp2p_autonat::v1::{ +use libp2p_autonat::{ Behaviour, Config, Event, NatStatus, OutboundProbeError, OutboundProbeEvent, ResponseError, }; use libp2p_core::Multiaddr; diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index f7e77ce75a7..a01c5901b8e 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -17,8 +17,9 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#![allow(deprecated)] -use libp2p_autonat::v1::{ +use libp2p_autonat::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, }; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; From 3f2476a7ede1c71114b4cd7cc3557835ffb0ada2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:24:32 +0100 Subject: [PATCH 094/179] Implement first jan suggestions --- protocols/autonat/src/v2/client/behaviour.rs | 3 + .../src/v2/client/handler/dial_request.rs | 2 +- protocols/autonat/src/v2/server.rs | 2 +- protocols/autonat/src/v2/server/behaviour.rs | 35 ++-- protocols/autonat/src/v2/server/handler.rs | 4 +- .../src/v2/server/handler/dial_back.rs | 12 +- .../src/v2/server/handler/dial_request.rs | 170 ++++++++---------- protocols/autonat/tests/autonatv2.rs | 8 +- 8 files changed, 106 insertions(+), 130 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index baeddc5b1f6..e257a4b833d 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -191,6 +191,9 @@ where return; } + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(address.0.clone())); + (address, Ok(())) } Err(dial_request::Error::UnsupportedProtocol) => { diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 4b071894215..16934c478fb 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -186,7 +186,7 @@ impl ConnectionHandler for Handler { }) => match self.queued_streams.pop_front() { Some(stream_tx) => { if stream_tx.send(Ok(protocol)).is_err() { - tracing::warn!("Failed to send stream to dead handler"); + tracing::debug!("Failed to send stream to dead handler"); } } None => { diff --git a/protocols/autonat/src/v2/server.rs b/protocols/autonat/src/v2/server.rs index e864893d73d..25819307784 100644 --- a/protocols/autonat/src/v2/server.rs +++ b/protocols/autonat/src/v2/server.rs @@ -2,4 +2,4 @@ mod behaviour; mod handler; pub use behaviour::Behaviour; -pub use behaviour::StatusUpdate; +pub use behaviour::Event; diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index a808e44c2fd..907faeec823 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -8,11 +8,11 @@ use crate::v2::server::handler::dial_request::DialBackStatus; use either::Either; use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ - dial_opts::DialOpts, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, - NetworkBehaviour, ToSwarm, + dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, + FromSwarm, NetworkBehaviour, ToSwarm, }; -use libp2p_swarm::{dial_opts::PeerCondition, ConnectionClosed}; use rand_core::{OsRng, RngCore}; use crate::v2::server::handler::{ @@ -60,7 +60,7 @@ where { type ConnectionHandler = Handler; - type ToSwarm = StatusUpdate; + type ToSwarm = Event; fn handle_established_inbound_connection( &mut self, @@ -84,19 +84,15 @@ where _role_override: Endpoint, _port_use: PortUse, ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok( - if let Some(cmd) = self.dialing_dial_back.remove(&connection_id) { - Either::Left(dial_back::Handler::new(cmd)) - } else { - Either::Left(dial_back::Handler::empty()) - }, - ) + Ok(match self.dialing_dial_back.remove(&connection_id) { + Some(cmd) => Either::Left(Either::Left(dial_back::Handler::new(cmd))), + None => Either::Left(Either::Right(dummy::ConnectionHandler)), + }) } fn on_swarm_event(&mut self, event: FromSwarm) { match event { - FromSwarm::DialFailure(DialFailure { connection_id, .. }) - | FromSwarm::ConnectionClosed(ConnectionClosed { connection_id, .. }) => { + FromSwarm::DialFailure(DialFailure { connection_id, .. }) => { if let Some(DialBackCommand { back_channel, .. }) = self.dialing_dial_back.remove(&connection_id) { @@ -114,11 +110,12 @@ where event: as ConnectionHandler>::ToBehaviour, ) { match event { - Either::Left(Ok(_)) => {} - Either::Left(Err(e)) => { + Either::Left(Either::Left(Ok(_))) => {} + Either::Left(Either::Left(Err(e))) => { tracing::debug!("dial back error: {e:?}"); } - Either::Right(Either::Left(Ok(cmd))) => { + Either::Left(Either::Right(v)) => void::unreachable(v), + Either::Right(Either::Left(cmd)) => { let addr = cmd.addr.clone(); let opts = DialOpts::peer_id(peer_id) .addresses(Vec::from([addr])) @@ -129,9 +126,6 @@ where self.dialing_dial_back.insert(conn_id, cmd); self.pending_events.push_back(ToSwarm::Dial { opts }); } - Either::Right(Either::Left(Err(e))) => { - tracing::warn!("incoming dial request failed: {}", e); - } Either::Right(Either::Right(status_update)) => self .pending_events .push_back(ToSwarm::GenerateEvent(status_update)), @@ -150,11 +144,10 @@ where } #[derive(Debug)] -pub struct StatusUpdate { +pub struct Event { /// All address that were submitted for testing. pub all_addrs: Vec, /// The address that was eventually tested. - /// This is `None` if the client send and unexpected message. pub tested_addr: Multiaddr, /// The peer id of the client that submitted addresses for testing. pub client: PeerId, diff --git a/protocols/autonat/src/v2/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs index 764e1a71f1f..ffdad69c86f 100644 --- a/protocols/autonat/src/v2/server/handler.rs +++ b/protocols/autonat/src/v2/server/handler.rs @@ -1,6 +1,8 @@ use either::Either; +use libp2p_swarm::dummy; pub(crate) mod dial_back; pub(crate) mod dial_request; -pub(crate) type Handler = Either>; +pub(crate) type Handler = + Either, dial_request::Handler>; diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index f419c9a6fc3..2aa87fd1294 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -28,16 +28,10 @@ pub struct Handler { impl Handler { pub(crate) fn new(cmd: DialBackCommand) -> Self { - let mut ret = Self::empty(); - ret.pending_nonce = Some(cmd); - ret - } - - pub(crate) fn empty() -> Self { Self { - pending_nonce: None, + pending_nonce: Some(cmd), requested_substream_nonce: None, - outbound: FuturesSet::new(Duration::from_secs(10000), 2), + outbound: FuturesSet::new(Duration::from_secs(10), 5), } } } @@ -128,6 +122,8 @@ async fn perform_dial_back( .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| ()); + // this exists to prevent a synchronization issue on the client side. Whitout this, the client + // might already receive a futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; back_channel .send(res) diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index bc004d4b541..382df27391f 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -24,7 +24,7 @@ use rand_core::RngCore; use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, - server::behaviour::StatusUpdate, + server::behaviour::Event, Nonce, DIAL_REQUEST_PROTOCOL, }; @@ -48,9 +48,7 @@ pub struct Handler { observed_multiaddr: Multiaddr, dial_back_cmd_sender: mpsc::Sender, dial_back_cmd_receiver: mpsc::Receiver, - status_update_sender: mpsc::Sender, - status_update_receiver: mpsc::Receiver, - inbound: FuturesSet>, + inbound: FuturesSet, rng: R, } @@ -60,14 +58,11 @@ where { pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); - let (status_update_sender, status_update_receiver) = mpsc::channel(10); Self { client_id, observed_multiaddr, dial_back_cmd_sender, dial_back_cmd_receiver, - status_update_sender, - status_update_receiver, inbound: FuturesSet::new(Duration::from_secs(10), 10), rng, } @@ -78,16 +73,11 @@ impl ConnectionHandler for Handler where R: RngCore + Send + Clone + 'static, { - type FromBehaviour = (); - - type ToBehaviour = Either, StatusUpdate>; - + type FromBehaviour = void::Void; + type ToBehaviour = Either; type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = DeniedUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { @@ -101,28 +91,21 @@ where ConnectionHandlerEvent, > { match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(Err(e))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( - e, - )))); + Poll::Ready(Ok(event)) => { + if let Err(e) = &event.result { + tracing::warn!("inbound request handle failed: {:?}", e); + } + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + event, + ))); } Poll::Ready(Err(e)) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Err( - io::Error::new(io::ErrorKind::TimedOut, e), - )))); + tracing::warn!("inbound request handle timed out {e:?}"); } - Poll::Ready(Ok(Ok(_))) => {} Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(Ok( - cmd, - )))); - } - if let Poll::Ready(Some(status_update)) = self.status_update_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( - status_update, - ))); + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); } Poll::Pending } @@ -144,12 +127,11 @@ where }) => { if self .inbound - .try_push(start_handle_request( + .try_push(handle_request( protocol, self.observed_multiaddr.clone(), self.client_id, self.dial_back_cmd_sender.clone(), - self.status_update_sender.clone(), self.rng.clone(), )) .is_err() @@ -208,6 +190,68 @@ impl From for DialResponse { } } +async fn handle_request( + stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + client: PeerId, + dial_back_cmd_sender: mpsc::Sender, + rng: impl RngCore, +) -> Event { + let mut coder = Coder::new(stream); + let mut all_addrs = Vec::new(); + let mut tested_addr_opt = None; + let mut data_amount = 0; + let response = handle_request_internal( + &mut coder, + observed_multiaddr.clone(), + dial_back_cmd_sender, + rng, + &mut all_addrs, + &mut tested_addr_opt, + &mut data_amount, + ) + .await + .unwrap_or_else(|e| e.into()); + if tested_addr_opt.is_none() { + return Event { + all_addrs, + tested_addr: observed_multiaddr, + client, + data_amount, + result: Err(io::Error::new( + io::ErrorKind::Other, + "client is not conformint to protocol. the tested address is not the observed address", + )), + }; + } + let tested_addr = tested_addr_opt.unwrap(); + if let Err(e) = coder.send(Response::Dial(response)).await { + return Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Err(e), + }; + } + if let Err(e) = coder.close().await { + return Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Err(e), + }; + } + Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Ok(()), + } +} + async fn handle_request_internal( coder: &mut Coder, observed_multiaddr: Multiaddr, @@ -285,65 +329,3 @@ where dial_status: DialStatus::OK, }) } - -async fn handle_request( - stream: impl AsyncRead + AsyncWrite + Unpin, - observed_multiaddr: Multiaddr, - dial_back_cmd_sender: mpsc::Sender, - rng: impl RngCore, - all_addrs: &mut Vec, - tested_addrs: &mut Option, - data_amount: &mut usize, -) -> io::Result<()> { - let mut coder = Coder::new(stream); - let response = handle_request_internal( - &mut coder, - observed_multiaddr, - dial_back_cmd_sender, - rng, - all_addrs, - tested_addrs, - data_amount, - ) - .await - .unwrap_or_else(|e| e.into()); - coder.send(Response::Dial(response)).await?; - coder.close().await?; - Ok(()) -} - -async fn start_handle_request( - stream: impl AsyncRead + AsyncWrite + Unpin, - observed_multiaddr: Multiaddr, - client: PeerId, - dial_back_cmd_sender: mpsc::Sender, - mut status_update_sender: mpsc::Sender, - rng: impl RngCore, -) -> io::Result<()> { - let mut all_addrs = Vec::new(); - let mut tested_addrs = None; - let mut data_amount = 0; - let result = handle_request( - stream, - observed_multiaddr, - dial_back_cmd_sender, - rng, - &mut all_addrs, - &mut tested_addrs, - &mut data_amount, - ) - .await; - if tested_addrs.is_none() { - tracing::warn!("client violated the protocol"); - return Err(io::Error::from(io::ErrorKind::InvalidData)); - } - let status_update = StatusUpdate { - all_addrs, - tested_addr: tested_addrs.unwrap(), - client, - data_amount, - result, - }; - let _ = status_update_sender.send(status_update).await; - Ok(()) -} diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 94f1bb18e5a..150de669362 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -61,7 +61,7 @@ async fn confirm_successful() { }) .await; - let server::StatusUpdate { + let server::Event { all_addrs, tested_addr, client, @@ -185,7 +185,7 @@ async fn dial_back_to_unsupported_protocol() { assert_eq!(outgoing_conn_error.len(), 0); let data_amount = alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, tested_addr, client, @@ -287,7 +287,7 @@ async fn dial_back_to_non_libp2p() { let data_amount = alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, tested_addr, client, @@ -378,7 +378,7 @@ async fn dial_back_to_not_supporting() { let data_amount = alice .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::StatusUpdate { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, tested_addr, client, From 9254faad6a0d41d2baa02f0c7353c5cdf499c9e3 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:30:22 +0100 Subject: [PATCH 095/179] Implement an example and a public tester --- Cargo.lock | 178 ++++++++++++++++-- examples/autonatv2/Cargo.toml | 26 +++ examples/autonatv2/Dockerfile | 20 ++ examples/autonatv2/docker-compose.yml | 16 ++ .../autonatv2/src/bin/autonatv2_client.rs | 111 +++++++++++ .../autonatv2/src/bin/autonatv2_server.rs | 86 +++++++++ examples/autonatv2/src/lib.rs | 0 examples/autonatv2/src/main.rs | 3 - 8 files changed, 423 insertions(+), 17 deletions(-) create mode 100644 examples/autonatv2/Dockerfile create mode 100644 examples/autonatv2/docker-compose.yml create mode 100644 examples/autonatv2/src/bin/autonatv2_client.rs create mode 100644 examples/autonatv2/src/bin/autonatv2_server.rs create mode 100644 examples/autonatv2/src/lib.rs delete mode 100644 examples/autonatv2/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index d739c7c2420..0ffe0f0214f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,6 +478,19 @@ dependencies = [ [[package]] name = "autonatv2" version = "0.1.0" +dependencies = [ + "cfg-if", + "clap", + "libp2p", + "opentelemetry 0.21.0", + "opentelemetry-jaeger", + "opentelemetry_sdk 0.21.2", + "rand 0.8.5", + "tokio", + "tracing", + "tracing-opentelemetry 0.22.0", + "tracing-subscriber", +] [[package]] name = "axum" @@ -904,9 +917,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -914,9 +927,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.16" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -2378,6 +2391,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + [[package]] name = "interceptor" version = "0.10.0" @@ -3723,13 +3742,13 @@ dependencies = [ "futures", "hyper 0.14.27", "libp2p", - "opentelemetry", + "opentelemetry 0.20.0", "opentelemetry-otlp", "opentelemetry_api", "prometheus-client", "tokio", "tracing", - "tracing-opentelemetry", + "tracing-opentelemetry 0.21.0", "tracing-subscriber", ] @@ -4108,7 +4127,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk", + "opentelemetry_sdk 0.20.0", +] + +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.0.0", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry-jaeger" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1" +dependencies = [ + "async-trait", + "futures-core", + "futures-util", + "opentelemetry 0.21.0", + "opentelemetry-semantic-conventions 0.13.0", + "opentelemetry_sdk 0.21.2", + "thrift", + "tokio", ] [[package]] @@ -4121,9 +4172,9 @@ dependencies = [ "futures-core", "http 0.2.9", "opentelemetry-proto", - "opentelemetry-semantic-conventions", + "opentelemetry-semantic-conventions 0.12.0", "opentelemetry_api", - "opentelemetry_sdk", + "opentelemetry_sdk 0.20.0", "prost", "thiserror", "tokio", @@ -4137,7 +4188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk", + "opentelemetry_sdk 0.20.0", "prost", "tonic", ] @@ -4148,7 +4199,16 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" dependencies = [ - "opentelemetry", + "opentelemetry 0.20.0", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" +dependencies = [ + "opentelemetry 0.21.0", ] [[package]] @@ -4180,7 +4240,7 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", - "ordered-float", + "ordered-float 3.9.2", "percent-encoding", "rand 0.8.5", "regex", @@ -4190,6 +4250,37 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "opentelemetry_sdk" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry 0.21.0", + "ordered-float 4.2.0", + "percent-encoding", + "rand 0.8.5", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-float" version = "3.9.2" @@ -4199,6 +4290,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -5836,6 +5936,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float 2.10.1", + "threadpool", +] + [[package]] name = "time" version = "0.3.23" @@ -6122,8 +6244,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" dependencies = [ "once_cell", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.20.0", + "opentelemetry_sdk 0.20.0", "smallvec", "tracing", "tracing-core", @@ -6131,6 +6253,24 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry 0.21.0", + "opentelemetry_sdk 0.21.2", + "smallvec", + "tracing", + "tracing-core", + "tracing-log 0.2.0", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -6528,6 +6668,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webdriver" version = "0.46.0" diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index ccefc57c1f7..9a0c0f6ebcb 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -6,7 +6,33 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "autonatv2_client" + +[[bin]] +name = "autonatv2_server" + [dependencies] +libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", + "identify"]} +clap = { version = "4.4.18", features = ["derive"] } +tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +rand = "0.8.5" +opentelemetry = { version = "0.21.0", optional = true } +opentelemetry_sdk = { version = "0.21.1", optional = true, features = ["rt-tokio"] } +tracing-opentelemetry = { version = "0.22.0", optional = true } +opentelemetry-jaeger = { version = "0.20.0", optional = true, features = ["rt-tokio"] } +cfg-if = "1.0.0" + +[features] +jaeger = ["opentelemetry", "opentelemetry_sdk", "tracing-opentelemetry", "opentelemetry-jaeger"] +opentelemetry = ["dep:opentelemetry"] +opentelemetry_sdk = ["dep:opentelemetry_sdk"] +tracing-opentelemetry = ["dep:tracing-opentelemetry"] +opentelemetry-jaeger = ["dep:opentelemetry-jaeger"] [lints] workspace = true diff --git a/examples/autonatv2/Dockerfile b/examples/autonatv2/Dockerfile new file mode 100644 index 00000000000..5a523649d80 --- /dev/null +++ b/examples/autonatv2/Dockerfile @@ -0,0 +1,20 @@ +FROM rust:1.75-alpine as builder + +RUN apk add musl-dev + +WORKDIR /workspace +COPY . . +RUN --mount=type=cache,target=./target \ + --mount=type=cache,target=/usr/local/cargo/registry \ + cargo build --release --package autonatv2 --bin autonatv2_server -F jaeger + +RUN --mount=type=cache,target=./target \ + mv ./target/release/autonatv2_server /usr/local/bin/autonatv2_server + +FROM alpine:latest + +COPY --from=builder /usr/local/bin/autonatv2_server /app/autonatv2_server + +EXPOSE 4884 + +ENTRYPOINT [ "/app/autonatv2_server", "-l", "4884" ] diff --git a/examples/autonatv2/docker-compose.yml b/examples/autonatv2/docker-compose.yml new file mode 100644 index 00000000000..75f44e7e6f9 --- /dev/null +++ b/examples/autonatv2/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + autonatv2: + build: + context: ../.. + dockerfile: examples/autonatv2/Dockerfile + ports: + - 4884:4884 + jaeger: + image: jaegertracing/all-in-one + ports: + - 6831:6831/udp + - 6832:6832/udp + - 16686:16686 + - 14268:14268 diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs new file mode 100644 index 00000000000..10f3f03fb6b --- /dev/null +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -0,0 +1,111 @@ +use std::{error::Error, net::Ipv4Addr, time::Duration}; + +use clap::Parser; +use libp2p::{ + autonat, + futures::StreamExt, + identify, identity, + multiaddr::Protocol, + noise, + swarm::{ + dial_opts::DialOpts, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, SwarmEvent, + }, + tcp, yamux, Multiaddr, SwarmBuilder, +}; +use rand::rngs::OsRng; +use tracing_subscriber::EnvFilter; + +#[derive(Debug, Parser)] +#[clap(name = "libp2p autonatv2 client")] +struct Opt { + /// Port where the client will listen for incoming connections. + #[clap(short = 'p', long, default_value_t = 0)] + listen_port: u16, + + /// Address of the server where want to connect to. + #[clap(short = 'a', long)] + server_address: Multiaddr, + + /// Probe interval in seconds. + #[clap(short = 't', long, default_value = "2")] + probe_interval: u64, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let opt = Opt::parse(); + + let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) + .build(); + + swarm.listen_on( + Multiaddr::empty() + .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) + .with(Protocol::Tcp(opt.listen_port)), + )?; + + swarm.dial( + DialOpts::unknown_peer_id() + .address(opt.server_address) + .build(), + )?; + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => { + println!("Listening on {address:?}"); + } + SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { + server, + tested_addr, + bytes_sent, + result: Ok(()), + })) => { + println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Everything Ok and verified."); + } + SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { + server, + tested_addr, + bytes_sent, + result: Err(e), + })) => { + println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Failed with {e:?}."); + } + SwarmEvent::ExternalAddrConfirmed { address } => { + println!("External address confirmed: {address}"); + } + _ => {} + } + } +} + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + autonat: autonat::v2::client::Behaviour, + identify: identify::Behaviour, +} + +impl Behaviour { + pub fn new(key: identity::PublicKey, probe_interval: u64) -> Self { + Self { + autonat: autonat::v2::client::Behaviour::new( + OsRng, + autonat::v2::client::Config::default() + .with_probe_interval(Duration::from_secs(probe_interval)), + ), + identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), + } + } +} diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs new file mode 100644 index 00000000000..46815d810e2 --- /dev/null +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -0,0 +1,86 @@ +use std::{error::Error, net::Ipv4Addr, time::Duration}; + +use cfg_if::cfg_if; +use clap::Parser; +use libp2p::{ + autonat, + futures::{task::waker, StreamExt}, + identify, identity, + multiaddr::Protocol, + noise, + swarm::{NetworkBehaviour, SwarmEvent}, + tcp, yamux, Multiaddr, SwarmBuilder, +}; +use rand::rngs::OsRng; +use tracing_subscriber::{util::SubscriberInitExt, EnvFilter}; + +#[derive(Debug, Parser)] +#[clap(name = "libp2p autonatv2 server")] +struct Opt { + #[clap(short, long, default_value_t = 0)] + listen_port: u16, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + cfg_if! { + if #[cfg(feature = "jaeger")] { + use tracing_subscriber::layer::SubscriberExt; + use opentelemetry_sdk::runtime::Tokio; + let tracer = opentelemetry_jaeger::new_agent_pipeline() + .with_endpoint("jaeger:6831") + .with_service_name("autonatv2") + .install_batch(Tokio)?; + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + let subscriber = tracing_subscriber::Registry::default() + .with(telemetry); + } else { + let subscriber = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .finish(); + } + } + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + let opt = Opt::parse(); + + let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_behaviour(|key| Behaviour::new(key.public()))? + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) + .build(); + + swarm.listen_on( + Multiaddr::empty() + .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) + .with(Protocol::Tcp(opt.listen_port)), + )?; + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), + SwarmEvent::Behaviour(event) => println!("{event:?}"), + e => println!("{e:?}"), + } + } +} + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + autonat: autonat::v2::server::Behaviour, + identify: identify::Behaviour, +} + +impl Behaviour { + pub fn new(key: identity::PublicKey) -> Self { + Self { + autonat: autonat::v2::server::Behaviour::new(OsRng), + identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), + } + } +} diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/autonatv2/src/main.rs b/examples/autonatv2/src/main.rs deleted file mode 100644 index e7a11a969c0..00000000000 --- a/examples/autonatv2/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} From d723c50dac8829e3f1b07249cef43c6a414234f6 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:33:57 +0100 Subject: [PATCH 096/179] Port quic --- Cargo.lock | 2 +- transports/quic/src/transport.rs | 201 ++++++++++++++++--------------- 2 files changed, 107 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd8f347e37e..818da4fa2e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4139,7 +4139,7 @@ checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ "futures-core", "futures-sink", - "indexmap 2.0.0", + "indexmap 2.2.1", "js-sys", "once_cell", "pin-project-lite", diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index aea3c91093f..f2656ed505d 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -31,6 +31,8 @@ use futures::{prelude::*, stream::SelectAll}; use if_watch::IfEvent; +use libp2p_core::transport::{DialOpts, PortUse}; +use libp2p_core::Endpoint; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, @@ -256,110 +258,119 @@ impl Transport for GenTransport

{ Some(observed.clone()) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let (socket_addr, version, _peer_id) = self.remote_multiaddr_to_socketaddr(addr, true)?; - - let endpoint = match self.eligible_listener(&socket_addr) { - None => { - // No listener. Get or create an explicit dialer. - let socket_family = socket_addr.ip().into(); - let dialer = match self.dialer.entry(socket_family) { - Entry::Occupied(occupied) => occupied.get().clone(), - Entry::Vacant(vacant) => { - if let Some(waker) = self.waker.take() { - waker.wake(); - } - let listen_socket_addr = match socket_family { - SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), - }; - let socket = - UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; - let endpoint_config = self.quinn_config.endpoint_config.clone(); - let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; - - vacant.insert(endpoint.clone()); - endpoint - } - }; - dialer - } - Some(listener) => listener.endpoint.clone(), - }; - let handshake_timeout = self.handshake_timeout; - let mut client_config = self.quinn_config.client_config.clone(); - if version == ProtocolVersion::Draft29 { - client_config.version(0xff00_001d); - } - Ok(Box::pin(async move { - // This `"l"` seems necessary because an empty string is an invalid domain - // name. While we don't use domain names, the underlying rustls library - // is based upon the assumption that we do. - let connecting = endpoint - .connect_with(client_config, socket_addr, "l") - .map_err(ConnectError)?; - Connecting::new(connecting, handshake_timeout).await - })) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - let (socket_addr, _version, peer_id) = + let (socket_addr, version, peer_id) = self.remote_multiaddr_to_socketaddr(addr.clone(), true)?; - let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?; - - let socket = self - .eligible_listener(&socket_addr) - .ok_or(TransportError::Other( - Error::NoActiveListenerForDialAsListener, - ))? - .try_clone_socket() - .map_err(Self::Error::from)?; - - tracing::debug!("Preparing for hole-punch from {addr}"); - - let hole_puncher = hole_puncher::

(socket, socket_addr, self.handshake_timeout); - - let (sender, receiver) = oneshot::channel(); - - match self.hole_punch_attempts.entry(socket_addr) { - Entry::Occupied(mut sender_entry) => { - // Stale senders, i.e. from failed hole punches are not removed. - // Thus, we can just overwrite a stale sender. - if !sender_entry.get().is_canceled() { - return Err(TransportError::Other(Error::HolePunchInProgress( - socket_addr, - ))); + + match (dial_opts.role, dial_opts.port_use) { + (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::New) => { + let endpoint = match self.eligible_listener(&socket_addr) { + None => { + // No listener. Get or create an explicit dialer. + let socket_family = socket_addr.ip().into(); + let dialer = match self.dialer.entry(socket_family) { + Entry::Occupied(occupied) => occupied.get().clone(), + Entry::Vacant(vacant) => { + if let Some(waker) = self.waker.take() { + waker.wake(); + } + let listen_socket_addr = match socket_family { + SocketFamily::Ipv4 => { + SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0) + } + SocketFamily::Ipv6 => { + SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0) + } + }; + let socket = UdpSocket::bind(listen_socket_addr) + .map_err(Self::Error::from)?; + let endpoint_config = self.quinn_config.endpoint_config.clone(); + let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; + + vacant.insert(endpoint.clone()); + endpoint + } + }; + dialer + } + Some(listener) => listener.endpoint.clone(), + }; + let handshake_timeout = self.handshake_timeout; + let mut client_config = self.quinn_config.client_config.clone(); + if version == ProtocolVersion::Draft29 { + client_config.version(0xff00_001d); } - sender_entry.insert(sender); + Ok(Box::pin(async move { + // This `"l"` seems necessary because an empty string is an invalid domain + // name. While we don't use domain names, the underlying rustls library + // is based upon the assumption that we do. + let connecting = endpoint + .connect_with(client_config, socket_addr, "l") + .map_err(ConnectError)?; + Connecting::new(connecting, handshake_timeout).await + })) } - Entry::Vacant(entry) => { - entry.insert(sender); - } - }; + (Endpoint::Listener, _) => { + let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?; + + let socket = self + .eligible_listener(&socket_addr) + .ok_or(TransportError::Other( + Error::NoActiveListenerForDialAsListener, + ))? + .try_clone_socket() + .map_err(Self::Error::from)?; + + tracing::debug!("Preparing for hole-punch from {addr}"); + + let hole_puncher = hole_puncher::

(socket, socket_addr, self.handshake_timeout); + + let (sender, receiver) = oneshot::channel(); + + match self.hole_punch_attempts.entry(socket_addr) { + Entry::Occupied(mut sender_entry) => { + // Stale senders, i.e. from failed hole punches are not removed. + // Thus, we can just overwrite a stale sender. + if !sender_entry.get().is_canceled() { + return Err(TransportError::Other(Error::HolePunchInProgress( + socket_addr, + ))); + } + sender_entry.insert(sender); + } + Entry::Vacant(entry) => { + entry.insert(sender); + } + }; - Ok(Box::pin(async move { - futures::pin_mut!(hole_puncher); - match futures::future::select(receiver, hole_puncher).await { - Either::Left((message, _)) => { - let (inbound_peer_id, connection) = message - .expect("hole punch connection sender is never dropped before receiver") - .await?; - if inbound_peer_id != peer_id { - tracing::warn!( - peer=%peer_id, - inbound_peer=%inbound_peer_id, - socket_address=%socket_addr, - "expected inbound connection from socket_address to resolve to peer but got inbound peer" - ); + Ok(Box::pin(async move { + futures::pin_mut!(hole_puncher); + match futures::future::select(receiver, hole_puncher).await { + Either::Left((message, _)) => { + let (inbound_peer_id, connection) = message + .expect( + "hole punch connection sender is never dropped before receiver", + ) + .await?; + if inbound_peer_id != peer_id { + tracing::warn!( + peer=%peer_id, + inbound_peer=%inbound_peer_id, + socket_address=%socket_addr, + "expected inbound connection from socket_address to resolve to peer but got inbound peer" + ); + } + Ok((inbound_peer_id, connection)) + } + Either::Right((hole_punch_err, _)) => Err(hole_punch_err), } - Ok((inbound_peer_id, connection)) - } - Either::Right((hole_punch_err, _)) => Err(hole_punch_err), + })) } - })) + } } fn poll( From daf2a0b6a379ce2d9bbec3a973734321e76a57ec Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:42:54 +0100 Subject: [PATCH 097/179] Make it compile --- misc/memory-connection-limits/src/lib.rs | 3 +- misc/metrics/src/bandwidth.rs | 17 ++----- protocols/dcutr/src/behaviour.rs | 3 ++ protocols/floodsub/src/layer.rs | 2 + protocols/gossipsub/src/behaviour.rs | 5 +- protocols/mdns/src/behaviour.rs | 2 + protocols/perf/src/client/behaviour.rs | 3 +- protocols/perf/src/server/behaviour.rs | 2 + protocols/relay/src/behaviour.rs | 3 ++ protocols/relay/src/priv_client.rs | 2 + protocols/relay/src/priv_client/transport.rs | 30 +++++------ protocols/rendezvous/src/client.rs | 11 +++- protocols/rendezvous/src/server.rs | 4 +- protocols/stream/src/behaviour.rs | 3 +- protocols/upnp/src/behaviour.rs | 3 +- transports/webrtc-websys/src/transport.rs | 18 ++++--- transports/webrtc/src/tokio/transport.rs | 23 ++++----- transports/websocket-websys/src/lib.rs | 18 ++++--- .../webtransport-websys/src/transport.rs | 21 ++++---- wasm-tests/webtransport-tests/src/lib.rs | 51 +++++++++++++++++-- 20 files changed, 144 insertions(+), 80 deletions(-) diff --git a/misc/memory-connection-limits/src/lib.rs b/misc/memory-connection-limits/src/lib.rs index ac911654979..943ce916b70 100644 --- a/misc/memory-connection-limits/src/lib.rs +++ b/misc/memory-connection-limits/src/lib.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ dummy, ConnectionDenied, ConnectionId, FromSwarm, NetworkBehaviour, THandler, THandlerInEvent, @@ -174,6 +174,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/misc/metrics/src/bandwidth.rs b/misc/metrics/src/bandwidth.rs index 2792e00612c..39ee1be4854 100644 --- a/misc/metrics/src/bandwidth.rs +++ b/misc/metrics/src/bandwidth.rs @@ -7,7 +7,7 @@ use futures::{ }; use libp2p_core::{ muxing::{StreamMuxer, StreamMuxerEvent}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, Multiaddr, }; use libp2p_identity::PeerId; @@ -84,24 +84,15 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let metrics = ConnectionMetrics::from_family_and_addr(&self.metrics, &addr); - Ok(self - .transport - .dial(addr.clone())? - .map_ok(Box::new(|(peer_id, stream_muxer)| { - (peer_id, Muxer::new(stream_muxer, metrics)) - }))) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { let metrics = ConnectionMetrics::from_family_and_addr(&self.metrics, &addr); Ok(self .transport - .dial_as_listener(addr.clone())? + .dial(addr.clone(), dial_opts)? .map_ok(Box::new(|(peer_id, stream_muxer)| { (peer_id, Muxer::new(stream_muxer, metrics)) }))) diff --git a/protocols/dcutr/src/behaviour.rs b/protocols/dcutr/src/behaviour.rs index 3742eb512f5..5ec5fe5568e 100644 --- a/protocols/dcutr/src/behaviour.rs +++ b/protocols/dcutr/src/behaviour.rs @@ -24,6 +24,7 @@ use crate::{handler, protocol}; use either::Either; use libp2p_core::connection::ConnectedPoint; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, DialFailure, FromSwarm}; @@ -206,12 +207,14 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { if is_relayed(addr) { return Ok(Either::Left(handler::relayed::Handler::new( ConnectedPoint::Dialer { address: addr.clone(), role_override, + port_use, }, self.observed_addresses(), ))); // TODO: We could make two `handler::relayed::Handler` here, one inbound one outbound. diff --git a/protocols/floodsub/src/layer.rs b/protocols/floodsub/src/layer.rs index 35711408a8d..1a70d2213b2 100644 --- a/protocols/floodsub/src/layer.rs +++ b/protocols/floodsub/src/layer.rs @@ -27,6 +27,7 @@ use crate::FloodsubConfig; use bytes::Bytes; use cuckoofilter::{CuckooError, CuckooFilter}; use fnv::FnvHashSet; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, FromSwarm}; @@ -346,6 +347,7 @@ impl NetworkBehaviour for Floodsub { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Default::default()) } diff --git a/protocols/gossipsub/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index 24a32de4cc7..a981f25c851 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -35,7 +35,9 @@ use prometheus_client::registry::Registry; use rand::{seq::SliceRandom, thread_rng}; use instant::Instant; -use libp2p_core::{multiaddr::Protocol::Ip4, multiaddr::Protocol::Ip6, Endpoint, Multiaddr}; +use libp2p_core::{ + multiaddr::Protocol::Ip4, multiaddr::Protocol::Ip6, transport::PortUse, Endpoint, Multiaddr, +}; use libp2p_identity::Keypair; use libp2p_identity::PeerId; use libp2p_swarm::{ @@ -3011,6 +3013,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new(self.config.protocol_config())) } diff --git a/protocols/mdns/src/behaviour.rs b/protocols/mdns/src/behaviour.rs index 4e3533f26ab..5bcbaaeff8d 100644 --- a/protocols/mdns/src/behaviour.rs +++ b/protocols/mdns/src/behaviour.rs @@ -28,6 +28,7 @@ use crate::Config; use futures::channel::mpsc; use futures::{Stream, StreamExt}; use if_watch::IfEvent; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::FromSwarm; @@ -262,6 +263,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/protocols/perf/src/client/behaviour.rs b/protocols/perf/src/client/behaviour.rs index 880bcdd9c83..7dd0559010c 100644 --- a/protocols/perf/src/client/behaviour.rs +++ b/protocols/perf/src/client/behaviour.rs @@ -25,7 +25,7 @@ use std::{ task::{Context, Poll}, }; -use libp2p_core::Multiaddr; +use libp2p_core::{transport::PortUse, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ derive_prelude::ConnectionEstablished, ConnectionClosed, ConnectionId, FromSwarm, @@ -92,6 +92,7 @@ impl NetworkBehaviour for Behaviour { _peer: PeerId, _addr: &Multiaddr, _role_override: libp2p_core::Endpoint, + _port_use: PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(Handler::default()) } diff --git a/protocols/perf/src/server/behaviour.rs b/protocols/perf/src/server/behaviour.rs index da24d763606..5408029e85d 100644 --- a/protocols/perf/src/server/behaviour.rs +++ b/protocols/perf/src/server/behaviour.rs @@ -25,6 +25,7 @@ use std::{ task::{Context, Poll}, }; +use libp2p_core::transport::PortUse; use libp2p_identity::PeerId; use libp2p_swarm::{ ConnectionId, FromSwarm, NetworkBehaviour, THandlerInEvent, THandlerOutEvent, ToSwarm, @@ -71,6 +72,7 @@ impl NetworkBehaviour for Behaviour { _peer: PeerId, _addr: &libp2p_core::Multiaddr, _role_override: libp2p_core::Endpoint, + _port_use: PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(Handler::default()) } diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index df8443e8359..6282280d21c 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -29,6 +29,7 @@ use crate::protocol::{inbound_hop, outbound_stop}; use either::Either; use instant::Instant; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::{ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, FromSwarm}; @@ -328,6 +329,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { if addr.is_relayed() { // Deny all substreams on relayed connection. @@ -343,6 +345,7 @@ impl NetworkBehaviour for Behaviour { ConnectedPoint::Dialer { address: addr.clone(), role_override, + port_use, }, ))) } diff --git a/protocols/relay/src/priv_client.rs b/protocols/relay/src/priv_client.rs index e414852ef81..f8d1d9c9eb2 100644 --- a/protocols/relay/src/priv_client.rs +++ b/protocols/relay/src/priv_client.rs @@ -34,6 +34,7 @@ use futures::io::{AsyncRead, AsyncWrite}; use futures::ready; use futures::stream::StreamExt; use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, FromSwarm}; @@ -178,6 +179,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { if addr.is_relayed() { return Ok(Either::Right(dummy::ConnectionHandler)); diff --git a/protocols/relay/src/priv_client/transport.rs b/protocols/relay/src/priv_client/transport.rs index 7147f0b5e55..c4cc4b6a6d0 100644 --- a/protocols/relay/src/priv_client/transport.rs +++ b/protocols/relay/src/priv_client/transport.rs @@ -32,7 +32,7 @@ use futures::sink::SinkExt; use futures::stream::SelectAll; use futures::stream::{Stream, StreamExt}; use libp2p_core::multiaddr::{Multiaddr, Protocol}; -use libp2p_core::transport::{ListenerId, TransportError, TransportEvent}; +use libp2p_core::transport::{DialOpts, ListenerId, TransportError, TransportEvent}; use libp2p_identity::PeerId; use std::collections::VecDeque; use std::pin::Pin; @@ -165,7 +165,19 @@ impl libp2p_core::Transport for Transport { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + // [`Endpoint::Listener`] is used for NAT and firewall + // traversal. One would coordinate such traversal via a previously + // established relayed connection, but never using a relayed connection + // itself. + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let RelayedMultiaddr { relay_peer_id, relay_addr, @@ -198,20 +210,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> - where - Self: Sized, - { - // [`Transport::dial_as_listener`] is used for NAT and firewall - // traversal. One would coordinate such traversal via a previously - // established relayed connection, but never using a relayed connection - // itself. - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { None } diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index 92d7884758b..a794252ff0b 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -24,6 +24,7 @@ use futures::future::BoxFuture; use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::StreamExt; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr, PeerRecord}; use libp2p_identity::{Keypair, PeerId, SigningError}; use libp2p_request_response::{OutboundRequestId, ProtocolSupport}; @@ -208,9 +209,15 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_connection_handler_event( diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 667c71e20e3..60dc067cbba 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -24,6 +24,7 @@ use bimap::BiMap; use futures::future::BoxFuture; use futures::stream::FuturesUnordered; use futures::{FutureExt, StreamExt}; +use libp2p_core::transport::PortUse; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_request_response::ProtocolSupport; @@ -140,9 +141,10 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override) + .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) } fn on_connection_handler_event( diff --git a/protocols/stream/src/behaviour.rs b/protocols/stream/src/behaviour.rs index e02aca884b7..07549ccef54 100644 --- a/protocols/stream/src/behaviour.rs +++ b/protocols/stream/src/behaviour.rs @@ -5,7 +5,7 @@ use std::{ }; use futures::{channel::mpsc, StreamExt}; -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ self as swarm, dial_opts::DialOpts, ConnectionDenied, ConnectionId, FromSwarm, @@ -82,6 +82,7 @@ impl NetworkBehaviour for Behaviour { peer: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new( peer, diff --git a/protocols/upnp/src/behaviour.rs b/protocols/upnp/src/behaviour.rs index a94ef9526dd..70d6afbffcc 100644 --- a/protocols/upnp/src/behaviour.rs +++ b/protocols/upnp/src/behaviour.rs @@ -36,7 +36,7 @@ use crate::tokio::{is_addr_global, Gateway}; use futures::{channel::oneshot, Future, StreamExt}; use futures_timer::Delay; use igd_next::PortMappingProtocol; -use libp2p_core::{multiaddr, transport::ListenerId, Endpoint, Multiaddr}; +use libp2p_core::{multiaddr, transport::{ListenerId, PortUse}, Endpoint, Multiaddr}; use libp2p_swarm::{ derive_prelude::PeerId, dummy, ConnectionDenied, ConnectionId, ExpiredListenAddr, FromSwarm, NetworkBehaviour, NewListenAddr, ToSwarm, @@ -248,6 +248,7 @@ impl NetworkBehaviour for Behaviour { _peer: PeerId, _addr: &Multiaddr, _role_override: Endpoint, + _port_use: PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/transports/webrtc-websys/src/transport.rs b/transports/webrtc-websys/src/transport.rs index ecf137eab8a..16faa95fb14 100644 --- a/transports/webrtc-websys/src/transport.rs +++ b/transports/webrtc-websys/src/transport.rs @@ -4,6 +4,7 @@ use super::Error; use futures::future::FutureExt; use libp2p_core::multiaddr::Multiaddr; use libp2p_core::muxing::StreamMuxerBox; +use libp2p_core::transport::DialOpts; use libp2p_core::transport::{Boxed, ListenerId, Transport as _, TransportError, TransportEvent}; use libp2p_identity::{Keypair, PeerId}; use std::future::Future; @@ -62,7 +63,15 @@ impl libp2p_core::Transport for Transport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + if maybe_local_firefox() { return Err(TransportError::Other( "Firefox does not support WebRTC over localhost or 127.0.0.1" @@ -89,13 +98,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/transports/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index 02cfa6f7296..70e8e10b9d8 100644 --- a/transports/webrtc/src/tokio/transport.rs +++ b/transports/webrtc/src/tokio/transport.rs @@ -22,7 +22,7 @@ use futures::{future::BoxFuture, prelude::*, stream::SelectAll, stream::Stream}; use if_watch::{tokio::IfWatcher, IfEvent}; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; use libp2p_identity as identity; use libp2p_identity::PeerId; @@ -118,7 +118,15 @@ impl libp2p_core::Transport for Transport { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + _: DialOpts, + ) -> Result> { + // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the + // `addr`. See DCUtR specification below. + // + // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol let (sock_addr, server_fingerprint) = libp2p_webrtc_utils::parse_webrtc_dial_addr(&addr) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if sock_addr.port() == 0 || sock_addr.ip().is_unspecified() { @@ -151,17 +159,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the - // `addr`. See DCUtR specification below. - // - // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol - self.dial(addr) - } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { libp2p_core::address_translation(server, observed) } diff --git a/transports/websocket-websys/src/lib.rs b/transports/websocket-websys/src/lib.rs index 5c1a6ebf1c4..87403365abd 100644 --- a/transports/websocket-websys/src/lib.rs +++ b/transports/websocket-websys/src/lib.rs @@ -26,6 +26,7 @@ use bytes::BytesMut; use futures::task::AtomicWaker; use futures::{future::Ready, io, prelude::*}; use js_sys::Array; +use libp2p_core::transport::DialOpts; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, transport::{ListenerId, TransportError, TransportEvent}, @@ -86,7 +87,15 @@ impl libp2p_core::Transport for Transport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let url = extract_websocket_url(&addr) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr))?; @@ -101,13 +110,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/transports/webtransport-websys/src/transport.rs b/transports/webtransport-websys/src/transport.rs index cb556ffef99..9f828922182 100644 --- a/transports/webtransport-websys/src/transport.rs +++ b/transports/webtransport-websys/src/transport.rs @@ -1,6 +1,8 @@ use futures::future::FutureExt; use libp2p_core::muxing::StreamMuxerBox; -use libp2p_core::transport::{Boxed, ListenerId, Transport as _, TransportError, TransportEvent}; +use libp2p_core::transport::{ + Boxed, DialOpts, ListenerId, Transport as _, TransportError, TransportEvent, +}; use libp2p_identity::{Keypair, PeerId}; use multiaddr::Multiaddr; use std::future::Future; @@ -62,7 +64,15 @@ impl libp2p_core::Transport for Transport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { + if dial_opts.role.is_listener() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let endpoint = Endpoint::from_multiaddr(&addr).map_err(|e| match e { e @ Error::InvalidMultiaddr(_) => { tracing::warn!("{}", e); @@ -83,13 +93,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - fn poll( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index 1f420cd6671..ad160f19d91 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -1,7 +1,8 @@ use futures::channel::oneshot; use futures::{AsyncReadExt, AsyncWriteExt}; use getrandom::getrandom; -use libp2p_core::{StreamMuxer, Transport as _}; +use libp2p_core::transport::{DialOpts, PortUse}; +use libp2p_core::{Endpoint, StreamMuxer, Transport as _}; use libp2p_identity::{Keypair, PeerId}; use libp2p_noise as noise; use libp2p_webtransport_websys::{Config, Connection, Error, Stream, Transport}; @@ -263,7 +264,17 @@ async fn connect_without_peer_id() { addr.pop(); let mut transport = Transport::new(Config::new(&keypair)); - transport.dial(addr).unwrap().await.unwrap(); + transport + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); } #[wasm_bindgen_test] @@ -278,7 +289,17 @@ async fn error_on_unknown_peer_id() { addr.push(Protocol::P2p(PeerId::random())); let mut transport = Transport::new(Config::new(&keypair)); - let e = transport.dial(addr.clone()).unwrap().await.unwrap_err(); + let e = transport + .dial( + addr.clone(), + DialOpts { + role: Endpoint::Listener, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap_err(); assert!(matches!(e, Error::UnknownRemotePeerId)); } @@ -297,7 +318,17 @@ async fn error_on_unknown_certhash() { addr.push(peer_id); let mut transport = Transport::new(Config::new(&keypair)); - let e = transport.dial(addr.clone()).unwrap().await.unwrap_err(); + let e = transport + .dial( + addr.clone(), + DialOpts { + role: Endpoint::Listener, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap_err(); assert!(matches!( e, Error::Noise(noise::Error::UnknownWebTransportCerthashes(..)) @@ -310,7 +341,17 @@ async fn new_connection_to_echo_server() -> Connection { let mut transport = Transport::new(Config::new(&keypair)); - let (_peer_id, conn) = transport.dial(addr).unwrap().await.unwrap(); + let (_peer_id, conn) = transport + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); conn } From 6b85b82006cecbc01a697c06228ba118a4d4ba26 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:53:44 +0100 Subject: [PATCH 098/179] Allow deprecated items --- examples/autonat/src/bin/autonat_client.rs | 4 +++- examples/autonat/src/bin/autonat_server.rs | 2 ++ examples/autonatv2/src/bin/autonatv2_client.rs | 4 +--- examples/autonatv2/src/bin/autonatv2_server.rs | 5 ++--- examples/dcutr/src/main.rs | 2 +- hole-punching-tests/src/main.rs | 2 +- misc/server/src/behaviour.rs | 7 +++++-- misc/server/src/main.rs | 2 +- transports/uds/src/lib.rs | 11 ++--------- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 3fb25aa6222..42b3995b57b 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -20,6 +20,8 @@ #![doc = include_str!("../../README.md")] +#![allow(deprecated)] + use clap::Parser; use futures::StreamExt; use libp2p::core::multiaddr::Protocol; @@ -86,7 +88,7 @@ async fn main() -> Result<(), Box> { #[derive(NetworkBehaviour)] struct Behaviour { identify: identify::Behaviour, - auto_nat: autonat::Behaviour, + auto_nat: autonat::v1::Behaviour, } impl Behaviour { diff --git a/examples/autonat/src/bin/autonat_server.rs b/examples/autonat/src/bin/autonat_server.rs index 44a53f0d17f..7545b8dac06 100644 --- a/examples/autonat/src/bin/autonat_server.rs +++ b/examples/autonat/src/bin/autonat_server.rs @@ -20,6 +20,8 @@ #![doc = include_str!("../../README.md")] +#![allow(deprecated)] + use clap::Parser; use futures::StreamExt; use libp2p::core::{multiaddr::Protocol, Multiaddr}; diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs index 10f3f03fb6b..8f9ce9e5eee 100644 --- a/examples/autonatv2/src/bin/autonatv2_client.rs +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -7,9 +7,7 @@ use libp2p::{ identify, identity, multiaddr::Protocol, noise, - swarm::{ - dial_opts::DialOpts, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, SwarmEvent, - }, + swarm::{dial_opts::DialOpts, NetworkBehaviour, SwarmEvent}, tcp, yamux, Multiaddr, SwarmBuilder, }; use rand::rngs::OsRng; diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs index 46815d810e2..cf1798031ec 100644 --- a/examples/autonatv2/src/bin/autonatv2_server.rs +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -4,7 +4,7 @@ use cfg_if::cfg_if; use clap::Parser; use libp2p::{ autonat, - futures::{task::waker, StreamExt}, + futures::StreamExt, identify, identity, multiaddr::Protocol, noise, @@ -12,7 +12,6 @@ use libp2p::{ tcp, yamux, Multiaddr, SwarmBuilder, }; use rand::rngs::OsRng; -use tracing_subscriber::{util::SubscriberInitExt, EnvFilter}; #[derive(Debug, Parser)] #[clap(name = "libp2p autonatv2 server")] @@ -36,7 +35,7 @@ async fn main() -> Result<(), Box> { .with(telemetry); } else { let subscriber = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .finish(); } } diff --git a/examples/dcutr/src/main.rs b/examples/dcutr/src/main.rs index 51df670f8a7..630d4b2b1f3 100644 --- a/examples/dcutr/src/main.rs +++ b/examples/dcutr/src/main.rs @@ -89,7 +89,7 @@ async fn main() -> Result<(), Box> { libp2p::SwarmBuilder::with_existing_identity(generate_ed25519(opts.secret_key_seed)) .with_tokio() .with_tcp( - tcp::Config::default().port_reuse(true).nodelay(true), + tcp::Config::default().nodelay(true), noise::Config::new, yamux::Config::default, )? diff --git a/hole-punching-tests/src/main.rs b/hole-punching-tests/src/main.rs index 4f81cd65480..ec8a57763f2 100644 --- a/hole-punching-tests/src/main.rs +++ b/hole-punching-tests/src/main.rs @@ -64,7 +64,7 @@ async fn main() -> Result<()> { let mut swarm = libp2p::SwarmBuilder::with_new_identity() .with_tokio() .with_tcp( - tcp::Config::new().port_reuse(true).nodelay(true), + tcp::Config::new().nodelay(true), noise::Config::new, yamux::Config::default, )? diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index ec025e02129..811b103c9c7 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use libp2p::autonat; use libp2p::identify; use libp2p::kad; @@ -8,6 +10,7 @@ use libp2p::{identity, swarm::NetworkBehaviour, Multiaddr, PeerId}; use std::str::FromStr; use std::time::Duration; + const BOOTNODES: [&str; 4] = [ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", @@ -21,7 +24,7 @@ pub(crate) struct Behaviour { ping: ping::Behaviour, identify: identify::Behaviour, pub(crate) kademlia: Toggle>, - autonat: Toggle, + autonat: Toggle, } impl Behaviour { @@ -54,7 +57,7 @@ impl Behaviour { .into(); let autonat = if enable_autonat { - Some(autonat::Behaviour::new( + Some(autonat::v1::Behaviour::new( PeerId::from(pub_key.clone()), Default::default(), )) diff --git a/misc/server/src/main.rs b/misc/server/src/main.rs index 16e6530e946..205466c8e16 100644 --- a/misc/server/src/main.rs +++ b/misc/server/src/main.rs @@ -76,7 +76,7 @@ async fn main() -> Result<(), Box> { let mut swarm = libp2p::SwarmBuilder::with_existing_identity(local_keypair) .with_tokio() .with_tcp( - tcp::Config::default().port_reuse(true).nodelay(true), + tcp::Config::default().nodelay(true), noise::Config::new, yamux::Config::default, )? diff --git a/transports/uds/src/lib.rs b/transports/uds/src/lib.rs index 075cbadb80a..d8acabd008a 100644 --- a/transports/uds/src/lib.rs +++ b/transports/uds/src/lib.rs @@ -46,7 +46,7 @@ use futures::{ use libp2p_core::transport::ListenerId; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{TransportError, TransportEvent}, + transport::{TransportError, TransportEvent, DialOpts}, Transport, }; use std::collections::VecDeque; @@ -159,7 +159,7 @@ macro_rules! codegen { } } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial(&mut self, addr: Multiaddr, _dial_opts: DialOpts) -> Result> { // TODO: Should we dial at all? if let Ok(path) = multiaddr_to_path(&addr) { tracing::debug!(address=%addr, "Dialing address"); @@ -169,13 +169,6 @@ macro_rules! codegen { } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - fn address_translation( &self, _server: &Multiaddr, From 0b94a0126c785f489c0ffb043136cbb54506dd31 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:22:56 +0100 Subject: [PATCH 099/179] Make the tests run --- misc/connection-limits/src/lib.rs | 1 + misc/memory-connection-limits/tests/util.rs | 3 +- protocols/gossipsub/src/behaviour/tests.rs | 6 ++ protocols/kad/src/behaviour/test.rs | 3 + protocols/ping/src/protocol.rs | 11 ++- swarm/tests/connection_close.rs | 2 + swarm/tests/swarm_derive.rs | 1 + transports/dns/src/lib.rs | 37 +++++----- transports/quic/src/transport.rs | 8 ++- transports/quic/tests/smoke.rs | 77 ++++++++++++++++----- transports/quic/tests/stream_compliance.rs | 14 +++- transports/tcp/src/lib.rs | 1 - transports/uds/src/lib.rs | 18 +++-- transports/webrtc/tests/smoke.rs | 16 ++++- transports/websocket/src/lib.rs | 14 +++- 15 files changed, 160 insertions(+), 52 deletions(-) diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index e31f50b83cb..b02e52f25a1 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -570,6 +570,7 @@ mod tests { _peer: PeerId, _addr: &Multiaddr, _role_override: Endpoint, + _port_use: PortUse, ) -> Result, ConnectionDenied> { Err(ConnectionDenied::new(std::io::Error::new( std::io::ErrorKind::Other, diff --git a/misc/memory-connection-limits/tests/util.rs b/misc/memory-connection-limits/tests/util.rs index f40ce319929..d18aa78fd22 100644 --- a/misc/memory-connection-limits/tests/util.rs +++ b/misc/memory-connection-limits/tests/util.rs @@ -20,7 +20,7 @@ use std::task::{Context, Poll}; -use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::{ dummy, ConnectionDenied, ConnectionId, FromSwarm, NetworkBehaviour, THandler, THandlerInEvent, @@ -102,6 +102,7 @@ impl NetworkBehaviour _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { self.handle_established(); Ok(dummy::ConnectionHandler) diff --git a/protocols/gossipsub/src/behaviour/tests.rs b/protocols/gossipsub/src/behaviour/tests.rs index 2bf1c90c5c8..49da7b3f27d 100644 --- a/protocols/gossipsub/src/behaviour/tests.rs +++ b/protocols/gossipsub/src/behaviour/tests.rs @@ -211,6 +211,7 @@ where ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, } } else { ConnectedPoint::Listener { @@ -261,6 +262,7 @@ where let fake_endpoint = ConnectedPoint::Dialer { address: Multiaddr::empty(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }; // this is not relevant // peer_connections.connections should never be empty. @@ -566,6 +568,7 @@ fn test_join() { endpoint: &ConnectedPoint::Dialer { address: "/ip4/127.0.0.1".parse::().unwrap(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 0, @@ -4056,6 +4059,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 0, @@ -4077,6 +4081,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr2.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 1, @@ -4107,6 +4112,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 2, diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 522eebcba92..48a4c76c5aa 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -1302,6 +1302,7 @@ fn network_behaviour_on_address_change() { let endpoint = ConnectedPoint::Dialer { address: old_address.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }; // Mimick a connection being established. @@ -1352,10 +1353,12 @@ fn network_behaviour_on_address_change() { old: &ConnectedPoint::Dialer { address: old_address, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, new: &ConnectedPoint::Dialer { address: new_address.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, })); diff --git a/protocols/ping/src/protocol.rs b/protocols/ping/src/protocol.rs index 28549e1c198..38535e55621 100644 --- a/protocols/ping/src/protocol.rs +++ b/protocols/ping/src/protocol.rs @@ -87,7 +87,8 @@ mod tests { use futures::StreamExt; use libp2p_core::{ multiaddr::multiaddr, - transport::{memory::MemoryTransport, ListenerId, Transport}, + transport::{memory::MemoryTransport, DialOpts, ListenerId, PortUse, Transport}, + Endpoint, }; use rand::{thread_rng, Rng}; use std::time::Duration; @@ -113,7 +114,13 @@ mod tests { async_std::task::block_on(async move { let c = MemoryTransport::new() - .dial(listener_addr) + .dial( + listener_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) .unwrap() .await .unwrap(); diff --git a/swarm/tests/connection_close.rs b/swarm/tests/connection_close.rs index 4efe8d17e49..4d530f47684 100644 --- a/swarm/tests/connection_close.rs +++ b/swarm/tests/connection_close.rs @@ -1,3 +1,4 @@ +use libp2p_core::transport::PortUse; use libp2p_core::upgrade::DeniedUpgrade; use libp2p_core::{Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -66,6 +67,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(HandlerWithState { precious_state: self.state, diff --git a/swarm/tests/swarm_derive.rs b/swarm/tests/swarm_derive.rs index 6663cf906b2..a0e6662ed19 100644 --- a/swarm/tests/swarm_derive.rs +++ b/swarm/tests/swarm_derive.rs @@ -404,6 +404,7 @@ fn with_generics_constrained() { _: libp2p_identity::PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index d5a61972d1c..a3701e5b537 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -624,8 +624,8 @@ mod tests { use futures::future::BoxFuture; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{TransportError, TransportEvent}, - Transport, + transport::{PortUse, TransportError, TransportEvent}, + Endpoint, Transport, }; use libp2p_identity::PeerId; @@ -656,7 +656,11 @@ mod tests { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + _: DialOpts, + ) -> Result> { // Check that all DNS components have been resolved, i.e. replaced. assert!(!addr.iter().any(|p| matches!( p, @@ -665,13 +669,6 @@ mod tests { Ok(Box::pin(future::ready(Ok(())))) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - fn address_translation(&self, _: &Multiaddr, _: &Multiaddr) -> Option { None } @@ -691,30 +688,34 @@ mod tests { T::Dial: Send, R: Clone + Send + Sync + Resolver + 'static, { + let dial_opts = DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }; // Success due to existing A record for example.com. let _ = transport - .dial("/dns4/example.com/tcp/20000".parse().unwrap()) + .dial("/dns4/example.com/tcp/20000".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); // Success due to existing AAAA record for example.com. let _ = transport - .dial("/dns6/example.com/tcp/20000".parse().unwrap()) + .dial("/dns6/example.com/tcp/20000".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); // Success due to pass-through, i.e. nothing to resolve. let _ = transport - .dial("/ip4/1.2.3.4/tcp/20000".parse().unwrap()) + .dial("/ip4/1.2.3.4/tcp/20000".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); // Success due to the DNS TXT records at _dnsaddr.bootstrap.libp2p.io. let _ = transport - .dial("/dnsaddr/bootstrap.libp2p.io".parse().unwrap()) + .dial("/dnsaddr/bootstrap.libp2p.io".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); @@ -723,7 +724,7 @@ mod tests { // an entry with suffix `/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN`, // i.e. a bootnode with such a peer ID. let _ = transport - .dial("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN".parse().unwrap()) + .dial("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN".parse().unwrap(), dial_opts) .unwrap() .await .unwrap(); @@ -735,6 +736,7 @@ mod tests { format!("/dnsaddr/bootstrap.libp2p.io/p2p/{}", PeerId::random()) .parse() .unwrap(), + dial_opts, ) .unwrap() .await @@ -746,7 +748,10 @@ mod tests { // Failure due to no records. match transport - .dial("/dns4/example.invalid/tcp/20000".parse().unwrap()) + .dial( + "/dns4/example.invalid/tcp/20000".parse().unwrap(), + dial_opts, + ) .unwrap() .await { diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index f2656ed505d..f2c00d7d2a3 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -924,7 +924,13 @@ mod tests { let mut transport = crate::tokio::Transport::new(config); let _dial = transport - .dial("/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap()) + .dial( + "/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap(), + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap(); assert!(transport.dialer.contains_key(&SocketFamily::Ipv4)); diff --git a/transports/quic/tests/smoke.rs b/transports/quic/tests/smoke.rs index 36fb72a5ee7..a95f23748b1 100644 --- a/transports/quic/tests/smoke.rs +++ b/transports/quic/tests/smoke.rs @@ -7,8 +7,9 @@ use futures::stream::StreamExt; use futures::{future, AsyncReadExt, AsyncWriteExt, FutureExt, SinkExt}; use futures_timer::Delay; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt, SubstreamBox}; -use libp2p_core::transport::{Boxed, OrTransport, TransportEvent}; +use libp2p_core::transport::{Boxed, DialOpts, OrTransport, PortUse, TransportEvent}; use libp2p_core::transport::{ListenerId, TransportError}; +use libp2p_core::Endpoint; use libp2p_core::{multiaddr::Protocol, upgrade, Multiaddr, Transport}; use libp2p_identity::PeerId; use libp2p_noise as noise; @@ -90,6 +91,8 @@ async fn ipv4_dial_ipv6() { #[cfg(feature = "async-std")] #[async_std::test] async fn wrapped_with_delay() { + use libp2p_core::transport::DialOpts; + let _ = tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .try_init(); @@ -125,7 +128,11 @@ async fn wrapped_with_delay() { /// Delayed dial, i.e. calling [`Transport::dial`] on the inner [`Transport`] not within the /// synchronous [`Transport::dial`] method, but within the [`Future`] returned by the outer /// [`Transport::dial`]. - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + dial_opts: DialOpts, + ) -> Result> { let t = self.0.clone(); Ok(async move { // Simulate DNS lookup. Giving the `Transport::poll` the chance to return @@ -133,24 +140,21 @@ async fn wrapped_with_delay() { // on the inner transport below. Delay::new(Duration::from_millis(100)).await; - let dial = t.lock().unwrap().dial(addr).map_err(|e| match e { - TransportError::MultiaddrNotSupported(_) => { - panic!() - } - TransportError::Other(e) => e, - })?; + let dial = t + .lock() + .unwrap() + .dial(addr, dial_opts) + .map_err(|e| match e { + TransportError::MultiaddrNotSupported(_) => { + panic!() + } + TransportError::Other(e) => e, + })?; dial.await } .boxed()) } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.0.lock().unwrap().dial_as_listener(addr) - } - fn poll( self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -183,7 +187,15 @@ async fn wrapped_with_delay() { // Note that the dial is spawned on a different task than the transport allowing the transport // task to poll the transport once and then suspend, waiting for the wakeup from the dial. let dial = async_std::task::spawn({ - let dial = b_transport.dial(a_addr).unwrap(); + let dial = b_transport + .dial( + a_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(); async { dial.await.unwrap().0 } }); async_std::task::spawn(async move { b_transport.next().await }); @@ -315,7 +327,13 @@ async fn draft_29_support() { let (_, mut c_transport) = create_transport::(|cfg| cfg.support_draft_29 = false); assert!(matches!( - c_transport.dial(a_quic_addr), + c_transport.dial( + a_quic_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New + } + ), Err(TransportError::MultiaddrNotSupported(_)) )); @@ -331,7 +349,15 @@ async fn draft_29_support() { )); let d_quic_v1_addr = start_listening(&mut d_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await; let d_quic_addr_mapped = swap_protocol!(d_quic_v1_addr, QuicV1 => Quic); - let dial = b_transport.dial(d_quic_addr_mapped).unwrap(); + let dial = b_transport + .dial( + d_quic_addr_mapped, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(); let drive_transports = poll_fn::<(), _>(|cx| { let _ = b_transport.poll_next_unpin(cx); let _ = d_transport.poll_next_unpin(cx); @@ -765,7 +791,20 @@ async fn dial( transport: &mut Boxed<(PeerId, StreamMuxerBox)>, addr: Multiaddr, ) -> io::Result<(PeerId, StreamMuxerBox)> { - match future::select(transport.dial(addr).unwrap(), transport.next()).await { + match future::select( + transport + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(), + transport.next(), + ) + .await + { Either::Left((conn, _)) => conn, Either::Right((event, _)) => { panic!("Unexpected event: {event:?}") diff --git a/transports/quic/tests/stream_compliance.rs b/transports/quic/tests/stream_compliance.rs index 0eff0584588..b0536473215 100644 --- a/transports/quic/tests/stream_compliance.rs +++ b/transports/quic/tests/stream_compliance.rs @@ -1,7 +1,7 @@ use futures::channel::oneshot; use futures::StreamExt; -use libp2p_core::transport::ListenerId; -use libp2p_core::Transport; +use libp2p_core::transport::{DialOpts, ListenerId, PortUse}; +use libp2p_core::{Endpoint, Transport}; use libp2p_quic as quic; use std::time::Duration; @@ -47,7 +47,15 @@ async fn connected_peers() -> (quic::Connection, quic::Connection) { listener.next().await; } }); - let dial_fut = dialer.dial(listen_address).unwrap(); + let dial_fut = dialer + .dial( + listen_address, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap(); async_std::task::spawn(async move { let connection = dial_fut.await.unwrap().1; diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index fec7b0802c5..dc4f75f9011 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -1092,7 +1092,6 @@ mod tests { #[test] fn port_reuse_listening() { - env_logger::try_init().ok(); let _ = tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .try_init(); diff --git a/transports/uds/src/lib.rs b/transports/uds/src/lib.rs index d8acabd008a..90d0c8ef135 100644 --- a/transports/uds/src/lib.rs +++ b/transports/uds/src/lib.rs @@ -46,7 +46,7 @@ use futures::{ use libp2p_core::transport::ListenerId; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::{TransportError, TransportEvent, DialOpts}, + transport::{DialOpts, TransportError, TransportEvent}, Transport, }; use std::collections::VecDeque; @@ -253,8 +253,8 @@ mod tests { use futures::{channel::oneshot, prelude::*}; use libp2p_core::{ multiaddr::{Multiaddr, Protocol}, - transport::ListenerId, - Transport, + transport::{DialOpts, ListenerId, PortUse}, + Endpoint, Transport, }; use std::{self, borrow::Cow, path::Path}; @@ -311,7 +311,17 @@ mod tests { async_std::task::block_on(async move { let mut uds = UdsConfig::new(); let addr = rx.await.unwrap(); - let mut socket = uds.dial(addr).unwrap().await.unwrap(); + let mut socket = uds + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); let _ = socket.write(&[1, 2, 3]).await.unwrap(); }); } diff --git a/transports/webrtc/tests/smoke.rs b/transports/webrtc/tests/smoke.rs index 76e168edfd6..d606d66c41f 100644 --- a/transports/webrtc/tests/smoke.rs +++ b/transports/webrtc/tests/smoke.rs @@ -23,8 +23,8 @@ use futures::future::{BoxFuture, Either}; use futures::stream::StreamExt; use futures::{future, ready, AsyncReadExt, AsyncWriteExt, FutureExt, SinkExt}; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt}; -use libp2p_core::transport::{Boxed, ListenerId, TransportEvent}; -use libp2p_core::{Multiaddr, Transport}; +use libp2p_core::transport::{Boxed, DialOpts, ListenerId, PortUse, TransportEvent}; +use libp2p_core::{Endpoint, Multiaddr, Transport}; use libp2p_identity::PeerId; use libp2p_webrtc as webrtc; use rand::{thread_rng, RngCore}; @@ -322,7 +322,17 @@ struct Dial<'a> { impl<'a> Dial<'a> { fn new(dialer: &'a mut Boxed<(PeerId, StreamMuxerBox)>, addr: Multiaddr) -> Self { Self { - dial_task: dialer.dial(addr).unwrap().map(|r| r.unwrap()).boxed(), + dial_task: dialer + .dial( + addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .map(|r| r.unwrap()) + .boxed(), dialer, } } diff --git a/transports/websocket/src/lib.rs b/transports/websocket/src/lib.rs index 3ead3184a5a..639cab71d31 100644 --- a/transports/websocket/src/lib.rs +++ b/transports/websocket/src/lib.rs @@ -289,7 +289,11 @@ where mod tests { use super::WsConfig; use futures::prelude::*; - use libp2p_core::{multiaddr::Protocol, transport::ListenerId, Multiaddr, Transport}; + use libp2p_core::{ + multiaddr::Protocol, + transport::{DialOpts, ListenerId, PortUse}, + Endpoint, Multiaddr, Transport, + }; use libp2p_identity::PeerId; use libp2p_tcp as tcp; @@ -336,7 +340,13 @@ mod tests { let outbound = new_ws_config() .boxed() - .dial(addr.with(Protocol::P2p(PeerId::random()))) + .dial( + addr.with(Protocol::P2p(PeerId::random())), + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap(); let (a, b) = futures::join!(inbound, outbound); From a09e26d71999a0b0a27f8411f7ebd7fa5a6a091a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:13:59 +0100 Subject: [PATCH 100/179] Implement the latest changes to the protocol --- .../src/v2/client/handler/dial_back.rs | 13 ++-- .../autonat/src/v2/generated/structs.proto | 8 +++ protocols/autonat/src/v2/generated/structs.rs | 66 +++++++++++++++++++ protocols/autonat/src/v2/protocol.rs | 46 +++++++++++-- .../src/v2/server/handler/dial_back.rs | 22 +++++-- protocols/autonat/tests/autonatv2.rs | 4 +- 6 files changed, 140 insertions(+), 19 deletions(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 18bfee792f1..052fe391e39 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -4,6 +4,7 @@ use std::{ time::Duration, }; +use futures::{AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -75,11 +76,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { protocol, .. }) => { - if self - .inbound - .try_push(protocol::recv_dial_back(protocol)) - .is_err() - { + if self.inbound.try_push(perform_dial_back(protocol)).is_err() { tracing::warn!("Dial back request dropped, too many requests in flight"); } } @@ -90,3 +87,9 @@ impl ConnectionHandler for Handler { } } } + +async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { + let nonce = protocol::recv_dial_back(&mut stream).await?; + protocol::dial_back_response(stream).await?; + Ok(nonce) +} diff --git a/protocols/autonat/src/v2/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto index 7088e888052..31791463956 100644 --- a/protocols/autonat/src/v2/generated/structs.proto +++ b/protocols/autonat/src/v2/generated/structs.proto @@ -44,3 +44,11 @@ message DialResponse { message DialDataResponse { bytes data = 1; } message DialBack { fixed64 nonce = 1; } + +message DialBackResponse { + enum DialBackStatus { + OK = 0; + } + + DialBackStatus status = 1; +} diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs index 2bb613458e8..e3388012ce7 100644 --- a/protocols/autonat/src/v2/generated/structs.rs +++ b/protocols/autonat/src/v2/generated/structs.rs @@ -336,3 +336,69 @@ impl MessageWrite for DialBack { Ok(()) } } + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct DialBackResponse { + pub(crate) status: Option, +} + +impl<'a> MessageRead<'a> for DialBackResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialBackResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + Ok(()) + } +} + +pub(crate) mod mod_DialBackResponse { + + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum DialBackStatus { + OK = 0, +} + +impl Default for DialBackStatus { + fn default() -> Self { + DialBackStatus::OK + } +} + +impl From for DialBackStatus { + fn from(i: i32) -> Self { + match i { + 0 => DialBackStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for DialBackStatus { + fn from(s: &'a str) -> Self { + match s { + "OK" => DialBackStatus::OK, + _ => Self::default(), + } + } +} + +} diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index f6776093868..5c211b353c8 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, io}; use asynchronous_codec::{Framed, FramedRead, FramedWrite}; -use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; use libp2p_core::Multiaddr; use quick_protobuf_codec::Codec; @@ -273,14 +273,11 @@ pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> .send(msg) .await .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - framed.close().await?; Ok(()) } -pub(crate) async fn recv_dial_back( - stream: impl AsyncRead + AsyncWrite + Unpin, -) -> io::Result { +pub(crate) async fn recv_dial_back(stream: impl AsyncRead + Unpin) -> io::Result { let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); let proto::DialBack { nonce } = framed .next() @@ -291,6 +288,45 @@ pub(crate) async fn recv_dial_back( Ok(nonce) } +pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::Result<()> { + let msg = proto::DialBackResponse { + status: Some(proto::mod_DialBackResponse::DialBackStatus::OK), + }; + let mut framed = FramedWrite::new( + stream, + Codec::::new(DIAL_BACK_MAX_SIZE), + ); + framed + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + framed.close().await?; + + Ok(()) +} + +pub(crate) async fn recv_dial_back_response( + stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result<()> { + let framed = &mut FramedRead::new( + stream, + Codec::::new(DIAL_BACK_MAX_SIZE), + ); + let proto::DialBackResponse { status } = framed + .next() + .await + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; + framed.close().await?; + if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid dial back response", + )) + } +} + #[cfg(test)] mod tests { use crate::v2::generated::structs::{ diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs index 2aa87fd1294..3cacd4ff32b 100644 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use futures::AsyncWrite; +use futures::{AsyncRead, AsyncWrite}; use futures_bounded::FuturesSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ @@ -14,7 +14,10 @@ use libp2p_swarm::{ SubstreamProtocol, }; -use crate::v2::{protocol::dial_back, DIAL_BACK_PROTOCOL}; +use crate::v2::{ + protocol::{dial_back, recv_dial_back_response}, + DIAL_BACK_PROTOCOL, +}; use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; @@ -111,20 +114,25 @@ impl ConnectionHandler for Handler { } async fn perform_dial_back( - stream: impl AsyncWrite + Unpin, + mut stream: impl AsyncRead + AsyncWrite + Unpin, DialBackCommand { nonce, back_channel, .. }: DialBackCommand, ) -> io::Result<()> { - let res = dial_back(stream, nonce) + let res = dial_back(&mut stream, nonce) .await .map_err(|_| DialBackRes::DialBackErr) .map(|_| ()); - // this exists to prevent a synchronization issue on the client side. Whitout this, the client - // might already receive a - futures_time::task::sleep(futures_time::time::Duration::from_millis(100)).await; + + let res = match res { + Ok(()) => recv_dial_back_response(stream) + .await + .map_err(|_| DialBackRes::DialBackErr) + .map(|_| ()), + Err(e) => Err(e), + }; back_channel .send(res) .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 150de669362..237be45deaf 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -238,7 +238,7 @@ async fn dial_back_to_non_libp2p() { for addr_str in [ "/ip4/169.150.247.38/tcp/32", - "/ip6/2400:52e0:1e02::1187:1/tcp/1000", + "/ip6/::1/tcp/1000", ] { let addr: Multiaddr = addr_str.parse().unwrap(); let bob_addr = addr.clone(); @@ -279,7 +279,7 @@ async fn dial_back_to_non_libp2p() { let error_string = o.to_string(); assert!( error_string.contains("Connection refused"), - "Coorect error string: {error_string}" + "Correct error string: {error_string} for {addr_str}" ); } else { panic!("No outgoing connection errors"); From 61ae32ef95636ef20ef4e6a495eaf3bba7b20ae5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:27:38 +0100 Subject: [PATCH 101/179] Fix minor nits --- transports/quic/src/transport.rs | 2 +- transports/webrtc/src/tokio/transport.rs | 14 +++++++++----- wasm-tests/webtransport-tests/src/lib.rs | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index f2c00d7d2a3..66093874e19 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -267,7 +267,7 @@ impl Transport for GenTransport

{ self.remote_multiaddr_to_socketaddr(addr.clone(), true)?; match (dial_opts.role, dial_opts.port_use) { - (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::New) => { + (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::Reuse) => { let endpoint = match self.eligible_listener(&socket_addr) { None => { // No listener. Get or create an explicit dialer. diff --git a/transports/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index 70e8e10b9d8..b30ac278728 100644 --- a/transports/webrtc/src/tokio/transport.rs +++ b/transports/webrtc/src/tokio/transport.rs @@ -121,12 +121,16 @@ impl libp2p_core::Transport for Transport { fn dial( &mut self, addr: Multiaddr, - _: DialOpts, + dial_opts: DialOpts, ) -> Result> { - // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the - // `addr`. See DCUtR specification below. - // - // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol + if dial_opts.role.is_listener() { + // TODO: As the listener of a WebRTC hole punch, we need to send a random UDP packet to the + // `addr`. See DCUtR specification below. + // + // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol + tracing::warn!("WebRTC hole punch is not yet supported"); + } + let (sock_addr, server_fingerprint) = libp2p_webrtc_utils::parse_webrtc_dial_addr(&addr) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if sock_addr.port() == 0 || sock_addr.ip().is_unspecified() { diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index ad160f19d91..b4d795fa5fe 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -268,8 +268,8 @@ async fn connect_without_peer_id() { .dial( addr, DialOpts { - role: Endpoint::Dialer, - port_use: PortUse::Reuse, + role: Endpoint::Listener, + port_use: PortUse::New, }, ) .unwrap() From ad6ad8fbfa01a02bb7a2892273c4dd3e67d089f5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:54:33 +0100 Subject: [PATCH 102/179] Implement DialBackOk correctly --- protocols/autonat/src/v2/client/behaviour.rs | 15 +++- .../src/v2/client/handler/dial_back.rs | 76 ++++++++++++++----- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index e257a4b833d..f0648808c37 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -19,7 +19,10 @@ use std::fmt::{Debug, Display, Formatter}; use crate::v2::{protocol::DialRequest, Nonce}; -use super::handler::{dial_back, dial_request}; +use super::handler::{ + dial_back::{self, IncomingNonce}, + dial_request, +}; #[derive(Debug, Clone, Copy)] pub struct Config { @@ -148,19 +151,27 @@ where event: ::ToBehaviour, ) { let (nonce, outcome) = match event { - Either::Right(nonce) => { + Either::Right(IncomingNonce { nonce, sender }) => { let Some((_, info)) = self .address_candidates .iter_mut() .find(|(_, info)| info.is_pending_with_nonce(nonce)) else { tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); + let _ = sender.send(Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Received unexpected nonce", + ))); return; }; info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + if let Err(e) = sender.send(Ok(())) { + tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e:?}"); + } + return; } Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 052fe391e39..d395e0cf925 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -4,8 +4,8 @@ use std::{ time::Duration, }; -use futures::{AsyncRead, AsyncWrite}; -use futures_bounded::FuturesSet; +use futures::{channel::oneshot, AsyncWriteExt}; +use futures_bounded::StreamSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, @@ -16,20 +16,20 @@ use void::Void; use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; pub struct Handler { - inbound: FuturesSet>, + inbound: StreamSet>, } impl Handler { pub(crate) fn new() -> Self { Self { - inbound: FuturesSet::new(Duration::from_secs(5), 2), + inbound: StreamSet::new(Duration::from_secs(5), 2), } } } impl ConnectionHandler for Handler { type FromBehaviour = Void; - type ToBehaviour = Nonce; + type ToBehaviour = IncomingNonce; type InboundProtocol = ReadyUpgrade; type OutboundProtocol = DeniedUpgrade; type InboundOpenInfo = (); @@ -45,19 +45,20 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(Ok(nonce))) => { - Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(nonce)) + match self.inbound.poll_next_unpin(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Pending, + Poll::Ready(Some(Err(err))) => { + tracing::debug!("Dial back handler timed out with: {err:?}"); + Poll::Pending } - Poll::Ready(Ok(Err(err))) => { + Poll::Ready(Some(Ok(Err(err)))) => { tracing::debug!("Dial back handler failed with: {err:?}"); Poll::Pending } - Poll::Ready(Err(err)) => { - tracing::debug!("Dial back handler timed out with: {err:?}"); - Poll::Pending + Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)) } - Poll::Pending => Poll::Pending, } } @@ -88,8 +89,49 @@ impl ConnectionHandler for Handler { } } -async fn perform_dial_back(mut stream: impl AsyncRead + AsyncWrite + Unpin) -> io::Result { - let nonce = protocol::recv_dial_back(&mut stream).await?; - protocol::dial_back_response(stream).await?; - Ok(nonce) +struct State { + stream: libp2p_swarm::Stream, + oneshot: Option>>, +} + +#[derive(Debug)] +pub struct IncomingNonce { + pub nonce: Nonce, + pub sender: oneshot::Sender>, +} + +fn perform_dial_back( + stream: libp2p_swarm::Stream, +) -> impl futures::Stream> { + let state = State { + stream, + oneshot: None, + }; + futures::stream::unfold(state, |mut state| async move { + if let Some(ref mut receiver) = state.oneshot { + if receiver.await.is_err() { + return Some((Err(io::Error::from(io::ErrorKind::Other)), state)); + } + if let Err(e) = protocol::dial_back_response(&mut state.stream).await { + let _ = state.stream.close().await; + return Some((Err(e), state)); + } + if let Err(e) = state.stream.close().await { + return Some((Err(e), state)); + } + return None; + } + + let nonce = match protocol::recv_dial_back(&mut state.stream).await { + Ok(nonce) => nonce, + Err(err) => { + let _ = state.stream.close().await; + return Some((Err(err), state)); + } + }; + + let (sender, receiver) = oneshot::channel(); + state.oneshot = Some(receiver); + Some((Ok(IncomingNonce { nonce, sender }), state)) + }) } From fbdc85fc85db6a46267a56b3f4805319c062d0e8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:57:27 +0100 Subject: [PATCH 103/179] Format --- examples/autonat/src/bin/autonat_client.rs | 1 - examples/autonat/src/bin/autonat_server.rs | 1 - examples/autonatv2/src/lib.rs | 1 + misc/server/src/behaviour.rs | 1 - protocols/autonat/tests/autonatv2.rs | 5 +---- protocols/rendezvous/src/server.rs | 9 +++++++-- protocols/upnp/src/behaviour.rs | 6 +++++- 7 files changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 42b3995b57b..6ef8b90f448 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -19,7 +19,6 @@ // DEALINGS IN THE SOFTWARE. #![doc = include_str!("../../README.md")] - #![allow(deprecated)] use clap::Parser; diff --git a/examples/autonat/src/bin/autonat_server.rs b/examples/autonat/src/bin/autonat_server.rs index 7545b8dac06..4a1a3c18401 100644 --- a/examples/autonat/src/bin/autonat_server.rs +++ b/examples/autonat/src/bin/autonat_server.rs @@ -19,7 +19,6 @@ // DEALINGS IN THE SOFTWARE. #![doc = include_str!("../../README.md")] - #![allow(deprecated)] use clap::Parser; diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs index e69de29bb2d..8b137891791 100644 --- a/examples/autonatv2/src/lib.rs +++ b/examples/autonatv2/src/lib.rs @@ -0,0 +1 @@ + diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index 811b103c9c7..dd5a24b465e 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -10,7 +10,6 @@ use libp2p::{identity, swarm::NetworkBehaviour, Multiaddr, PeerId}; use std::str::FromStr; use std::time::Duration; - const BOOTNODES: [&str; 4] = [ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 237be45deaf..cd80cfa9a26 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -236,10 +236,7 @@ async fn dial_back_to_non_libp2p() { let (mut alice, mut bob) = bootstrap().await; let alice_peer_id = *alice.local_peer_id(); - for addr_str in [ - "/ip4/169.150.247.38/tcp/32", - "/ip6/::1/tcp/1000", - ] { + for addr_str in ["/ip4/169.150.247.38/tcp/32", "/ip6/::1/tcp/1000"] { let addr: Multiaddr = addr_str.parse().unwrap(); let bob_addr = addr.clone(); bob.behaviour_mut() diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 60dc067cbba..92ff95650ba 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -143,8 +143,13 @@ impl NetworkBehaviour for Behaviour { role_override: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_connection_handler_event( diff --git a/protocols/upnp/src/behaviour.rs b/protocols/upnp/src/behaviour.rs index 70d6afbffcc..29a7fbf84a4 100644 --- a/protocols/upnp/src/behaviour.rs +++ b/protocols/upnp/src/behaviour.rs @@ -36,7 +36,11 @@ use crate::tokio::{is_addr_global, Gateway}; use futures::{channel::oneshot, Future, StreamExt}; use futures_timer::Delay; use igd_next::PortMappingProtocol; -use libp2p_core::{multiaddr, transport::{ListenerId, PortUse}, Endpoint, Multiaddr}; +use libp2p_core::{ + multiaddr, + transport::{ListenerId, PortUse}, + Endpoint, Multiaddr, +}; use libp2p_swarm::{ derive_prelude::PeerId, dummy, ConnectionDenied, ConnectionId, ExpiredListenAddr, FromSwarm, NetworkBehaviour, NewListenAddr, ToSwarm, From 553511c4494fc4f2dffc7bac902b5d35720d3558 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:00:23 +0100 Subject: [PATCH 104/179] Fix issues raised by clippy --- .../autonat/src/v2/client/handler/dial_request.rs | 2 +- protocols/autonat/src/v2/protocol.rs | 10 +++++----- protocols/autonat/src/v2/server/behaviour.rs | 13 +++++-------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 16934c478fb..5191382734f 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -234,7 +234,7 @@ async fn start_stream_handle( "address index out of bounds", ))); } - if num_bytes > DATA_LEN_UPPER_BOUND || num_bytes < DATA_LEN_LOWER_BOUND { + if !(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND).contains(&num_bytes) { return Err(Error::Io(io::Error::new( io::ErrorKind::InvalidInput, "requested bytes out of bounds", diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 5c211b353c8..68f3c3b1cf9 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -226,10 +226,10 @@ impl TryFrom for Response { } } -impl Into for Response { - fn into(self) -> proto::Message { - match self { - Self::Dial(DialResponse { +impl From for proto::Message { + fn from(val: Response) -> Self { + match val { + Response::Dial(DialResponse { status, addr_idx, dial_status, @@ -240,7 +240,7 @@ impl Into for Response { dialStatus: Some(dial_status), }), }, - Self::Data(DialDataRequest { + Response::Data(DialDataRequest { addr_idx, num_bytes, }) => proto::Message { diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 907faeec823..97446c6263f 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -91,15 +91,12 @@ where } fn on_swarm_event(&mut self, event: FromSwarm) { - match event { - FromSwarm::DialFailure(DialFailure { connection_id, .. }) => { - if let Some(DialBackCommand { back_channel, .. }) = - self.dialing_dial_back.remove(&connection_id) - { - let _ = back_channel.send(Err(DialBackStatus::DialErr)); - } + if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { + if let Some(DialBackCommand { back_channel, .. }) = + self.dialing_dial_back.remove(&connection_id) + { + let _ = back_channel.send(Err(DialBackStatus::DialErr)); } - _ => {} } } From 9bf52d454cff5774d530745ff671578377134533 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:54:16 +0100 Subject: [PATCH 105/179] Update protocols/autonat/src/v2/client/behaviour.rs Co-authored-by: Thomas Eizinger --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index f0648808c37..cffa9b3f29b 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -169,7 +169,7 @@ where tracing::debug!(%peer_id, %nonce, "Successful dial-back"); if let Err(e) = sender.send(Ok(())) { - tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e:?}"); + tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e}"); } return; From 70f41f325b2e2a567b450755198f4fcd4e923931 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:54:34 +0100 Subject: [PATCH 106/179] Update protocols/autonat/src/v2/client/handler/dial_back.rs Co-authored-by: Thomas Eizinger --- protocols/autonat/src/v2/client/handler/dial_back.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index d395e0cf925..8fb77013d86 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -49,7 +49,7 @@ impl ConnectionHandler for Handler { Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Pending, Poll::Ready(Some(Err(err))) => { - tracing::debug!("Dial back handler timed out with: {err:?}"); + tracing::debug!("Stream timed out: {err}"); Poll::Pending } Poll::Ready(Some(Ok(Err(err)))) => { From 077369478bdf7e916b703c86cad53bb9ae281572 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:54:48 +0100 Subject: [PATCH 107/179] Update protocols/autonat/src/v2/client/handler/dial_back.rs Co-authored-by: Thomas Eizinger --- protocols/autonat/src/v2/client/handler/dial_back.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 8fb77013d86..08ef7d3576e 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -113,7 +113,6 @@ fn perform_dial_back( return Some((Err(io::Error::from(io::ErrorKind::Other)), state)); } if let Err(e) = protocol::dial_back_response(&mut state.stream).await { - let _ = state.stream.close().await; return Some((Err(e), state)); } if let Err(e) = state.stream.close().await { From 89acd28ebebb3b088a4610b712e6e394c0d893ea Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:57:55 +0100 Subject: [PATCH 108/179] Make error richer and trace it at a different point --- protocols/autonat/src/v2/client/behaviour.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index cffa9b3f29b..58f1910d6e5 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -157,10 +157,9 @@ where .iter_mut() .find(|(_, info)| info.is_pending_with_nonce(nonce)) else { - tracing::warn!(%peer_id, %nonce, "Received unexpected nonce"); let _ = sender.send(Err(std::io::Error::new( std::io::ErrorKind::InvalidData, - "Received unexpected nonce", + format!("Received unexpected nonce: {nonce} from {peer_id}"), ))); return; }; From d571e518f83c13679e87363f64ef4eda06c693be Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:59:10 +0100 Subject: [PATCH 109/179] Avoid stream close --- protocols/autonat/src/v2/client/handler/dial_back.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 08ef7d3576e..3e8bec9dbe1 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -124,7 +124,6 @@ fn perform_dial_back( let nonce = match protocol::recv_dial_back(&mut state.stream).await { Ok(nonce) => nonce, Err(err) => { - let _ = state.stream.close().await; return Some((Err(err), state)); } }; From 10e4ceefed26384ca6bcbef2ba7158fa1e28b80f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:05:57 +0100 Subject: [PATCH 110/179] Better error handling --- protocols/autonat/src/v2/client/behaviour.rs | 2 +- protocols/autonat/src/v2/client/handler/dial_back.rs | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 58f1910d6e5..06aed2d544c 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -167,7 +167,7 @@ where info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); - if let Err(e) = sender.send(Ok(())) { + if let Err(Err(e)) = sender.send(Ok(())) { tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e}"); } diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 3e8bec9dbe1..b1923bfd023 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -109,8 +109,15 @@ fn perform_dial_back( }; futures::stream::unfold(state, |mut state| async move { if let Some(ref mut receiver) = state.oneshot { - if receiver.await.is_err() { - return Some((Err(io::Error::from(io::ErrorKind::Other)), state)); + match receiver.await { + Ok(Ok(())) => {} + Ok(Err(e)) => return Some((Err(e), state)), + Err(_) => { + return Some(( + Err(io::Error::new(io::ErrorKind::Other, "Sender got cancelled")), + state, + )); + } } if let Err(e) = protocol::dial_back_response(&mut state.stream).await { return Some((Err(e), state)); From d4d671ecaed511dce6946b09a4271d3b65f7a599 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:22:46 +0100 Subject: [PATCH 111/179] Correct one failing CI and change the license --- examples/autonat/Cargo.toml | 2 +- examples/autonatv2/Cargo.toml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/autonat/Cargo.toml b/examples/autonat/Cargo.toml index 038c1800308..761e1a67c56 100644 --- a/examples/autonat/Cargo.toml +++ b/examples/autonat/Cargo.toml @@ -3,7 +3,7 @@ name = "autonat-example" version = "0.1.0" edition = "2021" publish = false -license = "MIT" +license = "MIT or Apache-2.0" [package.metadata.release] release = false diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index 9a0c0f6ebcb..767f7a013df 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -2,10 +2,11 @@ name = "autonatv2" version = "0.1.0" edition = "2021" -rust-version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +publish = false +license = "MIT" +[package.metadata.release] +release = false [[bin]] name = "autonatv2_client" From 330fc518c309564b125bce9dc264707ca0339ac0 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:30:52 +0100 Subject: [PATCH 112/179] Correct minor things --- protocols/autonat/src/v2/client/behaviour.rs | 4 +-- .../src/v2/client/handler/dial_back.rs | 31 +++++++++---------- .../src/v2/server/handler/dial_request.rs | 29 ++++++++--------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs index 06aed2d544c..97509c05443 100644 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -167,9 +167,7 @@ where info.status = TestStatus::Received(nonce); tracing::debug!(%peer_id, %nonce, "Successful dial-back"); - if let Err(Err(e)) = sender.send(Ok(())) { - tracing::warn!(%peer_id, %nonce, "Failed to send dial-back response ok to client handler: {e}"); - } + let _ = sender.send(Ok(())); return; } diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index b1923bfd023..25a97880e6d 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -45,19 +45,21 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - match self.inbound.poll_next_unpin(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(None) => Poll::Pending, - Poll::Ready(Some(Err(err))) => { - tracing::debug!("Stream timed out: {err}"); - Poll::Pending - } - Poll::Ready(Some(Ok(Err(err)))) => { - tracing::debug!("Dial back handler failed with: {err:?}"); - Poll::Pending - } - Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { - Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)) + loop { + match self.inbound.poll_next_unpin(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => continue, + Poll::Ready(Some(Err(err))) => { + tracing::debug!("Stream timed out: {err}"); + continue; + } + Poll::Ready(Some(Ok(Err(err)))) => { + tracing::debug!("Dial back handler failed with: {err:?}"); + continue; + } + Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)); + } } } } @@ -122,9 +124,6 @@ fn perform_dial_back( if let Err(e) = protocol::dial_back_response(&mut state.stream).await { return Some((Err(e), state)); } - if let Err(e) = state.stream.close().await { - return Some((Err(e), state)); - } return None; } diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index 382df27391f..f8c3f6b654d 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -90,19 +90,21 @@ where ) -> Poll< ConnectionHandlerEvent, > { - match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(event)) => { - if let Err(e) = &event.result { - tracing::warn!("inbound request handle failed: {:?}", e); + loop { + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(event)) => { + if let Err(e) = &event.result { + tracing::warn!("inbound request handle failed: {:?}", e); + } + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + event, + ))); } - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( - event, - ))); - } - Poll::Ready(Err(e)) => { - tracing::warn!("inbound request handle timed out {e:?}"); + Poll::Ready(Err(e)) => { + tracing::warn!("inbound request handle timed out {e:?}"); + } + Poll::Pending => break, } - Poll::Pending => {} } if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); @@ -212,7 +214,7 @@ async fn handle_request( ) .await .unwrap_or_else(|e| e.into()); - if tested_addr_opt.is_none() { + let Some(tested_addr) = tested_addr_opt else { return Event { all_addrs, tested_addr: observed_multiaddr, @@ -223,8 +225,7 @@ async fn handle_request( "client is not conformint to protocol. the tested address is not the observed address", )), }; - } - let tested_addr = tested_addr_opt.unwrap(); + }; if let Err(e) = coder.send(Response::Dial(response)).await { return Event { all_addrs, From 367273a6e43789fdcc777b275a3f94913706f160 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:05:21 +0100 Subject: [PATCH 113/179] Add quic and dns to the example --- examples/autonatv2/Cargo.toml | 3 +-- examples/autonatv2/src/bin/autonatv2_client.rs | 2 ++ examples/autonatv2/src/bin/autonatv2_server.rs | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index 767f7a013df..7fd9b41f05c 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -15,8 +15,7 @@ name = "autonatv2_client" name = "autonatv2_server" [dependencies] -libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", - "identify"]} +libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", "identify", "dns", "quic"] } clap = { version = "4.4.18", features = ["derive"] } tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } tracing = "0.1.40" diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs index 8f9ce9e5eee..de902514dd8 100644 --- a/examples/autonatv2/src/bin/autonatv2_client.rs +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -44,6 +44,8 @@ async fn main() -> Result<(), Box> { noise::Config::new, yamux::Config::default, )? + .with_quic() + .with_dns()? .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) .build(); diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs index cf1798031ec..849ed3b3b0a 100644 --- a/examples/autonatv2/src/bin/autonatv2_server.rs +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -50,6 +50,8 @@ async fn main() -> Result<(), Box> { noise::Config::new, yamux::Config::default, )? + .with_quic() + .with_dns()? .with_behaviour(|key| Behaviour::new(key.public()))? .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) .build(); From d79bca3b3ea4c374d0b0cbb9129478a8639fa807 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:22:02 +0100 Subject: [PATCH 114/179] Remove unused import --- protocols/autonat/src/v2/client/handler/dial_back.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs index 25a97880e6d..b94580e69ba 100644 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -4,7 +4,7 @@ use std::{ time::Duration, }; -use futures::{channel::oneshot, AsyncWriteExt}; +use futures::channel::oneshot; use futures_bounded::StreamSet; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ From 8df0183e206e3581d8168fffe016bcd5c9847dd0 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:15:34 +0100 Subject: [PATCH 115/179] Change dial opts to make wasm tests pass --- wasm-tests/webtransport-tests/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index b4d795fa5fe..75d7112e02d 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -268,8 +268,8 @@ async fn connect_without_peer_id() { .dial( addr, DialOpts { - role: Endpoint::Listener, - port_use: PortUse::New, + role: Endpoint::Dialer, + port_use: PortUse::Reuse, }, ) .unwrap() @@ -322,8 +322,8 @@ async fn error_on_unknown_certhash() { .dial( addr.clone(), DialOpts { - role: Endpoint::Listener, - port_use: PortUse::New, + role: Endpoint::Dialer, + port_use: PortUse::Reuse, }, ) .unwrap() @@ -346,7 +346,7 @@ async fn new_connection_to_echo_server() -> Connection { addr, DialOpts { role: Endpoint::Dialer, - port_use: PortUse::New, + port_use: PortUse::Reuse, }, ) .unwrap() From 5d24d03ff6878b67b25b29811f7298f2a71e8522 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:22:06 +0100 Subject: [PATCH 116/179] Remove futures-time to compile on wasm --- Cargo.lock | 13 ------------- protocols/autonat/Cargo.toml | 1 - 2 files changed, 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 401b6114aa6..cc710b88a4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1707,18 +1707,6 @@ dependencies = [ "instant", ] -[[package]] -name = "futures-time" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6404853a6824881fe5f7d662d147dc4e84ecd2259ba0378f272a71dab600758a" -dependencies = [ - "async-channel", - "async-io 1.13.0", - "futures-core", - "pin-project-lite", -] - [[package]] name = "futures-timer" version = "3.0.3" @@ -2628,7 +2616,6 @@ dependencies = [ "either", "futures", "futures-bounded", - "futures-time", "futures-timer", "instant", "libp2p-core", diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 3a79215e2cf..b20fb4c596c 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -18,7 +18,6 @@ bytes = { version = "1", optional = true } either = { version = "1.9.0", optional = true } futures = "0.3" futures-bounded = { workspace = true, optional = true } -futures-time = "3" futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } From 41804be4ebfbffc8d976e2e2e320b94a74cc74c3 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:43:48 +0100 Subject: [PATCH 117/179] Fix concerns by clippy --- muxers/mplex/benches/split_send_size.rs | 13 +++++- protocols/autonat/tests/autonatv2.rs | 61 ++++++++++++------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/muxers/mplex/benches/split_send_size.rs b/muxers/mplex/benches/split_send_size.rs index 0125d49dcef..44eafa884ac 100644 --- a/muxers/mplex/benches/split_send_size.rs +++ b/muxers/mplex/benches/split_send_size.rs @@ -28,6 +28,7 @@ use futures::prelude::*; use futures::{channel::oneshot, future::join}; use libp2p_core::muxing::StreamMuxerExt; use libp2p_core::transport::ListenerId; +use libp2p_core::Endpoint; use libp2p_core::{multiaddr::multiaddr, muxing, transport, upgrade, Multiaddr, Transport}; use libp2p_identity as identity; use libp2p_identity::PeerId; @@ -146,7 +147,17 @@ fn run( // Spawn and block on the sender, i.e. until all data is sent. let sender = async move { let addr = addr_receiver.await.unwrap(); - let (_peer, mut conn) = sender_trans.dial(addr).unwrap().await.unwrap(); + let (_peer, mut conn) = sender_trans + .dial( + addr, + transport::DialOpts { + role: Endpoint::Dialer, + port_use: transport::PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); // Just calling `poll_outbound` without `poll` is fine here because mplex makes progress through all `poll_` functions. It is hacky though. let mut stream = poll_fn(|cx| conn.poll_outbound_unpin(cx)).await.unwrap(); let mut off = 0; diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index cd80cfa9a26..4b070297c94 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -19,8 +19,8 @@ async fn confirm_successful() { .try_init(); let (mut alice, mut bob) = start_and_connect().await; - let cor_server_peer = alice.local_peer_id().clone(); - let cor_client_peer = bob.local_peer_id().clone(); + let cor_server_peer = *alice.local_peer_id(); + let cor_client_peer = *bob.local_peer_id(); let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); let alice_bob_external_addrs = bob_external_addrs.clone(); @@ -76,7 +76,7 @@ async fn confirm_successful() { }) .await; - assert_eq!(tested_addr, bob_external_addrs.get(0).cloned().unwrap()); + assert_eq!(tested_addr, bob_external_addrs.first().cloned().unwrap()); assert_eq!(data_amount, 0); assert_eq!(client, cor_client_peer); assert_eq!(&all_addrs[..], &bob_external_addrs[..]); @@ -96,14 +96,16 @@ async fn confirm_successful() { }) .await; - let _ = bob.wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }); + let _ = bob + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }) + .await; let client::Event { tested_addr, @@ -120,7 +122,7 @@ async fn confirm_successful() { .await; assert_eq!( tested_addr, - alice_bob_external_addrs.get(0).cloned().unwrap() + alice_bob_external_addrs.first().cloned().unwrap() ); assert_eq!(bytes_sent, 0); assert_eq!(server, cor_server_peer); @@ -282,7 +284,7 @@ async fn dial_back_to_non_libp2p() { panic!("No outgoing connection errors"); } - let data_amount = alice + alice .wait(|event| match event { SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { all_addrs, @@ -298,22 +300,19 @@ async fn dial_back_to_non_libp2p() { } _ => None, }) - .await; - data_amount + .await }; let bob_task = async { - let data_amount = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), - _ => None, - }) - .await; - data_amount + bob.wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { + tested_addr, + bytes_sent, + server, + result: Err(_), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), + _ => None, + }) + .await }; let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); @@ -392,7 +391,7 @@ async fn dial_back_to_not_supporting() { .await; tokio::select! { _ = bob_done_rx => { - return data_amount; + data_amount } _ = alice.loop_on_next() => { unreachable!(); @@ -487,8 +486,8 @@ async fn start_and_connect() -> (Swarm, Swarm) { async fn bootstrap() -> (Swarm, Swarm) { let (mut alice, mut bob) = start_and_connect().await; - let cor_server_peer = alice.local_peer_id().clone(); - let cor_client_peer = bob.local_peer_id().clone(); + let cor_server_peer = *alice.local_peer_id(); + let cor_client_peer = *bob.local_peer_id(); let alice_task = async { let _ = alice @@ -553,7 +552,7 @@ async fn bootstrap() -> (Swarm, Swarm) { .. } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), _ => None, - }); + }).await; bob.wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), From bd495f925288b889b770c924d8b55f09ac6a5c9f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:44:52 +0100 Subject: [PATCH 118/179] Format --- protocols/autonat/tests/autonatv2.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs index 4b070297c94..abd0c4bd8eb 100644 --- a/protocols/autonat/tests/autonatv2.rs +++ b/protocols/autonat/tests/autonatv2.rs @@ -545,14 +545,16 @@ async fn bootstrap() -> (Swarm, Swarm) { }) .await; - let _ = bob.wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }).await; + let _ = bob + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }) + .await; bob.wait(|event| match event { SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), From 38be2b457862714d021f569d352133ad897f0b8a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:00:07 +0100 Subject: [PATCH 119/179] Use workaround to make generated code CI pass --- protocols/autonat/src/v2.rs | 6 +- protocols/autonat/src/v2/generated/mod.rs | 2 +- protocols/autonat/src/v2/generated/structs.rs | 55 +++++++++---------- protocols/autonat/src/v2/protocol.rs | 4 +- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index d95d51b79e9..8cab10a1d14 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -1,10 +1,14 @@ use libp2p_swarm::StreamProtocol; pub mod client; -mod generated; pub(crate) mod protocol; pub mod server; +pub(crate) mod generated { + #![allow(unreachable_pub)] + include!("v2/generated/mod.rs"); +} + pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = StreamProtocol::new("/libp2p/autonat/2/dial-request"); pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = diff --git a/protocols/autonat/src/v2/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs index 74078a88020..e52c5a80bc0 100644 --- a/protocols/autonat/src/v2/generated/mod.rs +++ b/protocols/autonat/src/v2/generated/mod.rs @@ -1,2 +1,2 @@ // Automatically generated mod.rs -pub(crate) mod structs; +pub mod structs; diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs index e3388012ce7..12568dd0364 100644 --- a/protocols/autonat/src/v2/generated/structs.rs +++ b/protocols/autonat/src/v2/generated/structs.rs @@ -9,14 +9,12 @@ #![cfg_attr(rustfmt, rustfmt_skip)] -use std::borrow::Cow; - use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum DialStatus { +pub enum DialStatus { UNUSED = 0, E_DIAL_ERROR = 100, E_DIAL_BACK_ERROR = 101, @@ -55,8 +53,8 @@ impl<'a> From<&'a str> for DialStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct Message { - pub(crate) msg: structs::mod_Message::OneOfmsg, +pub struct Message { + pub msg: structs::mod_Message::OneOfmsg, } impl<'a> MessageRead<'a> for Message { @@ -97,12 +95,12 @@ impl MessageWrite for Message { } } -pub(crate) mod mod_Message { +pub mod mod_Message { use super::*; #[derive(Debug, PartialEq, Clone)] -pub(crate) enum OneOfmsg { +pub enum OneOfmsg { dialRequest(structs::DialRequest), dialResponse(structs::DialResponse), dialDataRequest(structs::DialDataRequest), @@ -120,9 +118,9 @@ impl Default for OneOfmsg { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialRequest { - pub(crate) addrs: Vec>, - pub(crate) nonce: Option, +pub struct DialRequest { + pub addrs: Vec>, + pub nonce: Option, } impl<'a> MessageRead<'a> for DialRequest { @@ -156,9 +154,9 @@ impl MessageWrite for DialRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialDataRequest { - pub(crate) addrIdx: Option, - pub(crate) numBytes: Option, +pub struct DialDataRequest { + pub addrIdx: Option, + pub numBytes: Option, } impl<'a> MessageRead<'a> for DialDataRequest { @@ -192,10 +190,10 @@ impl MessageWrite for DialDataRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialResponse { - pub(crate) status: Option, - pub(crate) addrIdx: Option, - pub(crate) dialStatus: Option, +pub struct DialResponse { + pub status: Option, + pub addrIdx: Option, + pub dialStatus: Option, } impl<'a> MessageRead<'a> for DialResponse { @@ -230,11 +228,11 @@ impl MessageWrite for DialResponse { } } -pub(crate) mod mod_DialResponse { +pub mod mod_DialResponse { #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum ResponseStatus { +pub enum ResponseStatus { E_INTERNAL_ERROR = 0, E_REQUEST_REJECTED = 100, E_DIAL_REFUSED = 101, @@ -275,8 +273,8 @@ impl<'a> From<&'a str> for ResponseStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialDataResponse { - pub(crate) data: Option>, +pub struct DialDataResponse { + pub data: Option>, } impl<'a> MessageRead<'a> for DialDataResponse { @@ -284,7 +282,7 @@ impl<'a> MessageRead<'a> for DialDataResponse { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned().into()), + Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned()), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -307,8 +305,8 @@ impl MessageWrite for DialDataResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialBack { - pub(crate) nonce: Option, +pub struct DialBack { + pub nonce: Option, } impl<'a> MessageRead<'a> for DialBack { @@ -339,8 +337,8 @@ impl MessageWrite for DialBack { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct DialBackResponse { - pub(crate) status: Option, +pub struct DialBackResponse { + pub status: Option, } impl<'a> MessageRead<'a> for DialBackResponse { @@ -369,11 +367,11 @@ impl MessageWrite for DialBackResponse { } } -pub(crate) mod mod_DialBackResponse { +pub mod mod_DialBackResponse { #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) enum DialBackStatus { +pub enum DialBackStatus { OK = 0, } @@ -402,3 +400,4 @@ impl<'a> From<&'a str> for DialBackStatus { } } + diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 68f3c3b1cf9..4cfb17df057 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -1,7 +1,7 @@ // change to quick-protobuf-codec +use std::io; use std::io::ErrorKind; -use std::{borrow::Cow, io}; use asynchronous_codec::{Framed, FramedRead, FramedWrite}; @@ -110,7 +110,7 @@ impl From for proto::Message { static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; proto::Message { msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(Cow::Borrowed(&DATA[..val.data_count])), + data: Some(DATA[..val.data_count].to_vec()), // Once could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI }), } } From ce7596ccbf3051f79f8aafd73eabe206e1f565ae Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:05:30 +0100 Subject: [PATCH 120/179] Correct webtransport test --- wasm-tests/webtransport-tests/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index 75d7112e02d..938cdf0b3e1 100644 --- a/wasm-tests/webtransport-tests/src/lib.rs +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -293,8 +293,8 @@ async fn error_on_unknown_peer_id() { .dial( addr.clone(), DialOpts { - role: Endpoint::Listener, - port_use: PortUse::New, + role: Endpoint::Dialer, + port_use: PortUse::Reuse, }, ) .unwrap() From 0506c7b07c6be5a1902c894d7d857522412ddec8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:10:50 +0100 Subject: [PATCH 121/179] Handle strange variable unused on windows --- transports/tcp/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index dc4f75f9011..228cdbd7a30 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -280,6 +280,10 @@ where if port_use == PortUse::Reuse { socket.set_reuse_port(true)?; } + + #[cfg(not(all(unix, not(any(target_os = "solaris", target_os = "illumos")))))] + let _ = port_use; // silence the unused warning on non-unix platforms (i.e. Windows) + Ok(socket) } From 0b6dbb1aa685c7eb70829404de062914e7b50cfb Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 20:11:45 +0100 Subject: [PATCH 122/179] Remove clippy nit --- protocols/autonat/src/v2/protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 4cfb17df057..6cd6bb61e39 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -337,7 +337,7 @@ mod tests { fn message_correct_max_size() { let message_bytes = quick_protobuf::serialize_into_vec(&Message { msg: OneOfmsg::dialDataResponse(GenDialDataResponse { - data: Some(vec![0; 4096].into()), + data: Some(vec![0; 4096]), }), }) .unwrap(); From d2fddd5a27d4078c496c8330c672079eaec61f5e Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Thu, 7 Mar 2024 22:04:47 +0100 Subject: [PATCH 123/179] Simplify a little bit and get rid of every performance opt --- protocols/autonat/src/v2/protocol.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 6cd6bb61e39..d0e2fc30ca6 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -107,10 +107,9 @@ impl From for proto::Message { val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, "data_count too large" ); - static DATA: &[u8] = &[0u8; DATA_FIELD_LEN_UPPER_BOUND]; proto::Message { msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(DATA[..val.data_count].to_vec()), // Once could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI + data: Some(vec![0; val.data_count]), // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI }), } } From 8f1fcbb57f9b00d89e47cf6e2f717d77f64874af Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:37:42 +0100 Subject: [PATCH 124/179] Version bumps and added author --- Cargo.lock | 2 +- Cargo.toml | 2 +- core/Cargo.toml | 2 +- examples/autonatv2/Cargo.toml | 2 +- protocols/autonat/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc710b88a4c..3e9143496e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2654,7 +2654,7 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.41.2" +version = "0.42.0" dependencies = [ "async-std", "either", diff --git a/Cargo.toml b/Cargo.toml index 277214def1e..2cd545f5473 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ libp2p = { version = "0.53.2", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } -libp2p-core = { version = "0.41.2", path = "core" } +libp2p-core = { version = "0.42.0", path = "core" } libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } libp2p-dns = { version = "0.41.1", path = "transports/dns" } libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } diff --git a/core/Cargo.toml b/core/Cargo.toml index 619cd357744..9a3db3de743 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-core" edition = "2021" rust-version = { workspace = true } description = "Core traits and structs of libp2p" -version = "0.41.2" +version = "0.42.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml index 7fd9b41f05c..6c862ee22e4 100644 --- a/examples/autonatv2/Cargo.toml +++ b/examples/autonatv2/Cargo.toml @@ -3,7 +3,7 @@ name = "autonatv2" version = "0.1.0" edition = "2021" publish = false -license = "MIT" +license = "MIT or Apache-2.0" [package.metadata.release] release = false diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index b20fb4c596c..a981eaf35af 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" version = "0.13.0" -authors = ["David Craven ", "Elena Frank "] +authors = ["David Craven ", "Elena Frank ", "Hannes Furmans "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] From 8095e84d32942854b3eed132cdd92687d6bbb359 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:58:17 +0100 Subject: [PATCH 125/179] Upstream transport changes --- Cargo.lock | 184 +----- Cargo.toml | 3 +- examples/autonat/src/bin/autonat_client.rs | 2 +- examples/autonatv2/Cargo.toml | 38 -- examples/autonatv2/Dockerfile | 20 - examples/autonatv2/docker-compose.yml | 16 - .../autonatv2/src/bin/autonatv2_client.rs | 111 ---- .../autonatv2/src/bin/autonatv2_server.rs | 87 --- examples/autonatv2/src/lib.rs | 1 - misc/server/src/behaviour.rs | 4 +- protocols/autonat/Cargo.toml | 33 +- protocols/autonat/src/{v1 => }/behaviour.rs | 13 +- .../src/{v1 => }/behaviour/as_client.rs | 0 .../src/{v1 => }/behaviour/as_server.rs | 1 + .../autonat/src/{v1 => }/generated/mod.rs | 0 .../src/{v1 => }/generated/structs.proto | 0 .../autonat/src/{v1 => }/generated/structs.rs | 0 protocols/autonat/src/lib.rs | 46 +- protocols/autonat/src/{v1 => }/protocol.rs | 0 protocols/autonat/src/v1.rs | 42 -- protocols/autonat/src/v2.rs | 17 - protocols/autonat/src/v2/client.rs | 5 - protocols/autonat/src/v2/client/behaviour.rs | 439 -------------- protocols/autonat/src/v2/client/handler.rs | 2 - .../src/v2/client/handler/dial_back.rs | 141 ----- .../src/v2/client/handler/dial_request.rs | 332 ---------- protocols/autonat/src/v2/generated/mod.rs | 2 - .../autonat/src/v2/generated/structs.proto | 54 -- protocols/autonat/src/v2/generated/structs.rs | 403 ------------- protocols/autonat/src/v2/protocol.rs | 362 ----------- protocols/autonat/src/v2/server.rs | 5 - protocols/autonat/src/v2/server/behaviour.rs | 155 ----- protocols/autonat/src/v2/server/handler.rs | 8 - .../src/v2/server/handler/dial_back.rs | 140 ----- .../src/v2/server/handler/dial_request.rs | 332 ---------- protocols/autonat/tests/autonatv2.rs | 568 ------------------ protocols/autonat/tests/test_client.rs | 1 - protocols/autonat/tests/test_server.rs | 1 - 38 files changed, 68 insertions(+), 3500 deletions(-) delete mode 100644 examples/autonatv2/Cargo.toml delete mode 100644 examples/autonatv2/Dockerfile delete mode 100644 examples/autonatv2/docker-compose.yml delete mode 100644 examples/autonatv2/src/bin/autonatv2_client.rs delete mode 100644 examples/autonatv2/src/bin/autonatv2_server.rs delete mode 100644 examples/autonatv2/src/lib.rs rename protocols/autonat/src/{v1 => }/behaviour.rs (99%) rename protocols/autonat/src/{v1 => }/behaviour/as_client.rs (100%) rename protocols/autonat/src/{v1 => }/behaviour/as_server.rs (99%) rename protocols/autonat/src/{v1 => }/generated/mod.rs (100%) rename protocols/autonat/src/{v1 => }/generated/structs.proto (100%) rename protocols/autonat/src/{v1 => }/generated/structs.rs (100%) rename protocols/autonat/src/{v1 => }/protocol.rs (100%) delete mode 100644 protocols/autonat/src/v1.rs delete mode 100644 protocols/autonat/src/v2.rs delete mode 100644 protocols/autonat/src/v2/client.rs delete mode 100644 protocols/autonat/src/v2/client/behaviour.rs delete mode 100644 protocols/autonat/src/v2/client/handler.rs delete mode 100644 protocols/autonat/src/v2/client/handler/dial_back.rs delete mode 100644 protocols/autonat/src/v2/client/handler/dial_request.rs delete mode 100644 protocols/autonat/src/v2/generated/mod.rs delete mode 100644 protocols/autonat/src/v2/generated/structs.proto delete mode 100644 protocols/autonat/src/v2/generated/structs.rs delete mode 100644 protocols/autonat/src/v2/protocol.rs delete mode 100644 protocols/autonat/src/v2/server.rs delete mode 100644 protocols/autonat/src/v2/server/behaviour.rs delete mode 100644 protocols/autonat/src/v2/server/handler.rs delete mode 100644 protocols/autonat/src/v2/server/handler/dial_back.rs delete mode 100644 protocols/autonat/src/v2/server/handler/dial_request.rs delete mode 100644 protocols/autonat/tests/autonatv2.rs diff --git a/Cargo.lock b/Cargo.lock index 3e9143496e7..571bee5096e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -476,23 +476,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "autonatv2" -version = "0.1.0" -dependencies = [ - "cfg-if", - "clap", - "libp2p", - "opentelemetry 0.21.0", - "opentelemetry-jaeger", - "opentelemetry_sdk 0.21.2", - "rand 0.8.5", - "tokio", - "tracing", - "tracing-opentelemetry 0.22.0", - "tracing-subscriber", -] - [[package]] name = "axum" version = "0.6.20" @@ -2345,12 +2328,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - [[package]] name = "interceptor" version = "0.10.0" @@ -2607,19 +2584,15 @@ dependencies = [ [[package]] name = "libp2p-autonat" -version = "0.13.0" +version = "0.12.0" dependencies = [ "async-std", "async-trait", "asynchronous-codec", - "bytes", - "either", "futures", - "futures-bounded", "futures-timer", "instant", "libp2p-core", - "libp2p-identify", "libp2p-identity", "libp2p-request-response", "libp2p-swarm", @@ -2627,12 +2600,8 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", - "rand_core 0.6.4", - "thiserror", - "tokio", "tracing", "tracing-subscriber", - "void", ] [[package]] @@ -3695,13 +3664,13 @@ dependencies = [ "futures", "hyper 0.14.27", "libp2p", - "opentelemetry 0.20.0", + "opentelemetry", "opentelemetry-otlp", "opentelemetry_api", "prometheus-client", "tokio", "tracing", - "tracing-opentelemetry 0.21.0", + "tracing-opentelemetry", "tracing-subscriber", ] @@ -4080,39 +4049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk 0.20.0", -] - -[[package]] -name = "opentelemetry" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" -dependencies = [ - "futures-core", - "futures-sink", - "indexmap 2.2.1", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", -] - -[[package]] -name = "opentelemetry-jaeger" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1" -dependencies = [ - "async-trait", - "futures-core", - "futures-util", - "opentelemetry 0.21.0", - "opentelemetry-semantic-conventions 0.13.0", - "opentelemetry_sdk 0.21.2", - "thrift", - "tokio", + "opentelemetry_sdk", ] [[package]] @@ -4125,9 +4062,9 @@ dependencies = [ "futures-core", "http 0.2.9", "opentelemetry-proto", - "opentelemetry-semantic-conventions 0.12.0", + "opentelemetry-semantic-conventions", "opentelemetry_api", - "opentelemetry_sdk 0.20.0", + "opentelemetry_sdk", "prost", "thiserror", "tokio", @@ -4141,7 +4078,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e3f814aa9f8c905d0ee4bde026afd3b2577a97c10e1699912e3e44f0c4cbeb" dependencies = [ "opentelemetry_api", - "opentelemetry_sdk 0.20.0", + "opentelemetry_sdk", "prost", "tonic", ] @@ -4152,16 +4089,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" dependencies = [ - "opentelemetry 0.20.0", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" -dependencies = [ - "opentelemetry 0.21.0", + "opentelemetry", ] [[package]] @@ -4193,7 +4121,7 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", - "ordered-float 3.9.2", + "ordered-float", "percent-encoding", "rand 0.8.5", "regex", @@ -4203,37 +4131,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "opentelemetry_sdk" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "once_cell", - "opentelemetry 0.21.0", - "ordered-float 4.2.0", - "percent-encoding", - "rand 0.8.5", - "thiserror", - "tokio", - "tokio-stream", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-float" version = "3.9.2" @@ -4243,15 +4140,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-float" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" -dependencies = [ - "num-traits", -] - [[package]] name = "overload" version = "0.1.1" @@ -5883,28 +5771,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "thrift" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" -dependencies = [ - "byteorder", - "integer-encoding", - "log", - "ordered-float 2.10.1", - "threadpool", -] - [[package]] name = "time" version = "0.3.23" @@ -6190,8 +6056,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" dependencies = [ "once_cell", - "opentelemetry 0.20.0", - "opentelemetry_sdk 0.20.0", + "opentelemetry", + "opentelemetry_sdk", "smallvec", "tracing", "tracing-core", @@ -6199,24 +6065,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" -dependencies = [ - "js-sys", - "once_cell", - "opentelemetry 0.21.0", - "opentelemetry_sdk 0.21.2", - "smallvec", - "tracing", - "tracing-core", - "tracing-log 0.2.0", - "tracing-subscriber", - "web-time", -] - [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -6608,16 +6456,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webpki-roots" version = "0.25.2" diff --git a/Cargo.toml b/Cargo.toml index 2cd545f5473..55cdf4cf4f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "core", "examples/autonat", - "examples/autonatv2", "examples/browser-webrtc", "examples/chat", "examples/dcutr", @@ -76,7 +75,7 @@ asynchronous-codec = { version = "0.7.0" } futures-bounded = { version = "0.2.3" } libp2p = { version = "0.53.2", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } -libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } +libp2p-autonat = { version = "0.12.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } libp2p-core = { version = "0.42.0", path = "core" } libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 6ef8b90f448..2e85053749f 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -87,7 +87,7 @@ async fn main() -> Result<(), Box> { #[derive(NetworkBehaviour)] struct Behaviour { identify: identify::Behaviour, - auto_nat: autonat::v1::Behaviour, + auto_nat: autonat::Behaviour, } impl Behaviour { diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml deleted file mode 100644 index 6c862ee22e4..00000000000 --- a/examples/autonatv2/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "autonatv2" -version = "0.1.0" -edition = "2021" -publish = false -license = "MIT or Apache-2.0" - -[package.metadata.release] -release = false - -[[bin]] -name = "autonatv2_client" - -[[bin]] -name = "autonatv2_server" - -[dependencies] -libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", "identify", "dns", "quic"] } -clap = { version = "4.4.18", features = ["derive"] } -tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } -tracing = "0.1.40" -tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -rand = "0.8.5" -opentelemetry = { version = "0.21.0", optional = true } -opentelemetry_sdk = { version = "0.21.1", optional = true, features = ["rt-tokio"] } -tracing-opentelemetry = { version = "0.22.0", optional = true } -opentelemetry-jaeger = { version = "0.20.0", optional = true, features = ["rt-tokio"] } -cfg-if = "1.0.0" - -[features] -jaeger = ["opentelemetry", "opentelemetry_sdk", "tracing-opentelemetry", "opentelemetry-jaeger"] -opentelemetry = ["dep:opentelemetry"] -opentelemetry_sdk = ["dep:opentelemetry_sdk"] -tracing-opentelemetry = ["dep:tracing-opentelemetry"] -opentelemetry-jaeger = ["dep:opentelemetry-jaeger"] - -[lints] -workspace = true diff --git a/examples/autonatv2/Dockerfile b/examples/autonatv2/Dockerfile deleted file mode 100644 index 5a523649d80..00000000000 --- a/examples/autonatv2/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM rust:1.75-alpine as builder - -RUN apk add musl-dev - -WORKDIR /workspace -COPY . . -RUN --mount=type=cache,target=./target \ - --mount=type=cache,target=/usr/local/cargo/registry \ - cargo build --release --package autonatv2 --bin autonatv2_server -F jaeger - -RUN --mount=type=cache,target=./target \ - mv ./target/release/autonatv2_server /usr/local/bin/autonatv2_server - -FROM alpine:latest - -COPY --from=builder /usr/local/bin/autonatv2_server /app/autonatv2_server - -EXPOSE 4884 - -ENTRYPOINT [ "/app/autonatv2_server", "-l", "4884" ] diff --git a/examples/autonatv2/docker-compose.yml b/examples/autonatv2/docker-compose.yml deleted file mode 100644 index 75f44e7e6f9..00000000000 --- a/examples/autonatv2/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3' - -services: - autonatv2: - build: - context: ../.. - dockerfile: examples/autonatv2/Dockerfile - ports: - - 4884:4884 - jaeger: - image: jaegertracing/all-in-one - ports: - - 6831:6831/udp - - 6832:6832/udp - - 16686:16686 - - 14268:14268 diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs deleted file mode 100644 index de902514dd8..00000000000 --- a/examples/autonatv2/src/bin/autonatv2_client.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::{error::Error, net::Ipv4Addr, time::Duration}; - -use clap::Parser; -use libp2p::{ - autonat, - futures::StreamExt, - identify, identity, - multiaddr::Protocol, - noise, - swarm::{dial_opts::DialOpts, NetworkBehaviour, SwarmEvent}, - tcp, yamux, Multiaddr, SwarmBuilder, -}; -use rand::rngs::OsRng; -use tracing_subscriber::EnvFilter; - -#[derive(Debug, Parser)] -#[clap(name = "libp2p autonatv2 client")] -struct Opt { - /// Port where the client will listen for incoming connections. - #[clap(short = 'p', long, default_value_t = 0)] - listen_port: u16, - - /// Address of the server where want to connect to. - #[clap(short = 'a', long)] - server_address: Multiaddr, - - /// Probe interval in seconds. - #[clap(short = 't', long, default_value = "2")] - probe_interval: u64, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - - let opt = Opt::parse(); - - let mut swarm = SwarmBuilder::with_new_identity() - .with_tokio() - .with_tcp( - tcp::Config::default(), - noise::Config::new, - yamux::Config::default, - )? - .with_quic() - .with_dns()? - .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? - .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) - .build(); - - swarm.listen_on( - Multiaddr::empty() - .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) - .with(Protocol::Tcp(opt.listen_port)), - )?; - - swarm.dial( - DialOpts::unknown_peer_id() - .address(opt.server_address) - .build(), - )?; - - loop { - match swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } => { - println!("Listening on {address:?}"); - } - SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { - server, - tested_addr, - bytes_sent, - result: Ok(()), - })) => { - println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Everything Ok and verified."); - } - SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { - server, - tested_addr, - bytes_sent, - result: Err(e), - })) => { - println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Failed with {e:?}."); - } - SwarmEvent::ExternalAddrConfirmed { address } => { - println!("External address confirmed: {address}"); - } - _ => {} - } - } -} - -#[derive(NetworkBehaviour)] -pub struct Behaviour { - autonat: autonat::v2::client::Behaviour, - identify: identify::Behaviour, -} - -impl Behaviour { - pub fn new(key: identity::PublicKey, probe_interval: u64) -> Self { - Self { - autonat: autonat::v2::client::Behaviour::new( - OsRng, - autonat::v2::client::Config::default() - .with_probe_interval(Duration::from_secs(probe_interval)), - ), - identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), - } - } -} diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs deleted file mode 100644 index 849ed3b3b0a..00000000000 --- a/examples/autonatv2/src/bin/autonatv2_server.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::{error::Error, net::Ipv4Addr, time::Duration}; - -use cfg_if::cfg_if; -use clap::Parser; -use libp2p::{ - autonat, - futures::StreamExt, - identify, identity, - multiaddr::Protocol, - noise, - swarm::{NetworkBehaviour, SwarmEvent}, - tcp, yamux, Multiaddr, SwarmBuilder, -}; -use rand::rngs::OsRng; - -#[derive(Debug, Parser)] -#[clap(name = "libp2p autonatv2 server")] -struct Opt { - #[clap(short, long, default_value_t = 0)] - listen_port: u16, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - cfg_if! { - if #[cfg(feature = "jaeger")] { - use tracing_subscriber::layer::SubscriberExt; - use opentelemetry_sdk::runtime::Tokio; - let tracer = opentelemetry_jaeger::new_agent_pipeline() - .with_endpoint("jaeger:6831") - .with_service_name("autonatv2") - .install_batch(Tokio)?; - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - let subscriber = tracing_subscriber::Registry::default() - .with(telemetry); - } else { - let subscriber = tracing_subscriber::fmt() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .finish(); - } - } - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); - - let opt = Opt::parse(); - - let mut swarm = SwarmBuilder::with_new_identity() - .with_tokio() - .with_tcp( - tcp::Config::default(), - noise::Config::new, - yamux::Config::default, - )? - .with_quic() - .with_dns()? - .with_behaviour(|key| Behaviour::new(key.public()))? - .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) - .build(); - - swarm.listen_on( - Multiaddr::empty() - .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) - .with(Protocol::Tcp(opt.listen_port)), - )?; - - loop { - match swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), - SwarmEvent::Behaviour(event) => println!("{event:?}"), - e => println!("{e:?}"), - } - } -} - -#[derive(NetworkBehaviour)] -pub struct Behaviour { - autonat: autonat::v2::server::Behaviour, - identify: identify::Behaviour, -} - -impl Behaviour { - pub fn new(key: identity::PublicKey) -> Self { - Self { - autonat: autonat::v2::server::Behaviour::new(OsRng), - identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), - } - } -} diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs deleted file mode 100644 index 8b137891791..00000000000 --- a/examples/autonatv2/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index a6d08689dfb..c5bcb7e9d9a 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -26,7 +26,7 @@ pub(crate) struct Behaviour { ping: ping::Behaviour, identify: identify::Behaviour, pub(crate) kademlia: Toggle>, - autonat: Toggle, + autonat: Toggle, } impl Behaviour { @@ -59,7 +59,7 @@ impl Behaviour { .into(); let autonat = if enable_autonat { - Some(autonat::v1::Behaviour::new( + Some(autonat::Behaviour::new( PeerId::from(pub_key.clone()), Default::default(), )) diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index a981eaf35af..fce64ad0c12 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -3,50 +3,32 @@ name = "libp2p-autonat" edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" -version = "0.13.0" -authors = ["David Craven ", "Elena Frank ", "Hannes Furmans "] +authors = ["David Craven ", "Elena Frank "] +version = "0.12.0" license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] - [dependencies] async-trait = "0.1" -asynchronous-codec = { workspace = true } -bytes = { version = "1", optional = true } -either = { version = "1.9.0", optional = true } futures = "0.3" -futures-bounded = { workspace = true, optional = true } futures-timer = "3.0" instant = "0.1" libp2p-core = { workspace = true } -libp2p-identity = { workspace = true } -libp2p-request-response = { workspace = true, optional = true } libp2p-swarm = { workspace = true } +libp2p-request-response = { workspace = true } +libp2p-identity = { workspace = true } quick-protobuf = "0.8" -quick-protobuf-codec = { workspace = true } rand = "0.8" -rand_core = { version = "0.6", optional = true } -thiserror = { version = "1.0.52", optional = true } tracing = "0.1.37" -void = { version = "1", optional = true } +quick-protobuf-codec = { workspace = true } +asynchronous-codec = { workspace = true } [dev-dependencies] -tokio = { version = "1", features = ["macros", "rt", "sync"]} async-std = { version = "1.10", features = ["attributes"] } libp2p-swarm-test = { path = "../../swarm-test" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } -libp2p-identify = { workspace = true } -libp2p-swarm = { workspace = true, features = ["macros"]} - -[features] -default = ["v1", "v2"] -v1 = ["dep:libp2p-request-response"] -v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] - -[lints] -workspace = true # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling @@ -54,3 +36,6 @@ workspace = true all-features = true rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/protocols/autonat/src/v1/behaviour.rs b/protocols/autonat/src/behaviour.rs similarity index 99% rename from protocols/autonat/src/v1/behaviour.rs rename to protocols/autonat/src/behaviour.rs index 6e2c36a467f..bf36080a721 100644 --- a/protocols/autonat/src/v1/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -339,7 +339,7 @@ impl Behaviour { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, - port_use: _, + .. } => { if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { self.pending_actions @@ -349,7 +349,7 @@ impl Behaviour { ConnectedPoint::Dialer { address: _, role_override: Endpoint::Listener, - port_use: _, + .. } => { // Outgoing connection was dialed as a listener. In other words outgoing connection // was dialed as part of a hole punch. `libp2p-autonat` never attempts to hole @@ -517,13 +517,8 @@ impl NetworkBehaviour for Behaviour { role_override: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner.handle_established_outbound_connection( - connection_id, - peer, - addr, - role_override, - port_use, - ) + self.inner + .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/protocols/autonat/src/v1/behaviour/as_client.rs b/protocols/autonat/src/behaviour/as_client.rs similarity index 100% rename from protocols/autonat/src/v1/behaviour/as_client.rs rename to protocols/autonat/src/behaviour/as_client.rs diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/behaviour/as_server.rs similarity index 99% rename from protocols/autonat/src/v1/behaviour/as_server.rs rename to protocols/autonat/src/behaviour/as_server.rs index e309023bc75..4e3cfc77891 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/behaviour/as_server.rs @@ -17,6 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. + use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, ResponseError, diff --git a/protocols/autonat/src/v1/generated/mod.rs b/protocols/autonat/src/generated/mod.rs similarity index 100% rename from protocols/autonat/src/v1/generated/mod.rs rename to protocols/autonat/src/generated/mod.rs diff --git a/protocols/autonat/src/v1/generated/structs.proto b/protocols/autonat/src/generated/structs.proto similarity index 100% rename from protocols/autonat/src/v1/generated/structs.proto rename to protocols/autonat/src/generated/structs.proto diff --git a/protocols/autonat/src/v1/generated/structs.rs b/protocols/autonat/src/generated/structs.rs similarity index 100% rename from protocols/autonat/src/v1/generated/structs.rs rename to protocols/autonat/src/generated/structs.rs diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index a6fc66b28d1..10c87b1e984 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1,9 +1,41 @@ -#[cfg(feature = "v1")] -pub mod v1; +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. -#[cfg(feature = "v2")] -pub mod v2; +//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. -#[cfg(feature = "v1")] -#[allow(deprecated)] -pub use v1::*; +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod behaviour; +mod protocol; + +pub use self::{ + behaviour::{ + Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, + OutboundProbeError, OutboundProbeEvent, ProbeId, + }, + protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, +}; +pub use libp2p_request_response::{InboundFailure, OutboundFailure}; + +mod proto { + #![allow(unreachable_pub)] + include!("generated/mod.rs"); + pub(crate) use self::structs::{mod_Message::*, Message}; +} diff --git a/protocols/autonat/src/v1/protocol.rs b/protocols/autonat/src/protocol.rs similarity index 100% rename from protocols/autonat/src/v1/protocol.rs rename to protocols/autonat/src/protocol.rs diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs deleted file mode 100644 index 07b08310871..00000000000 --- a/protocols/autonat/src/v1.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. - -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![cfg_attr(not(test), deprecated(note = "Please use `v2` module instead."))] - -pub(crate) mod behaviour; -pub(crate) mod protocol; - -pub use self::{ - behaviour::{ - Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, - OutboundProbeError, OutboundProbeEvent, ProbeId, - }, - protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, -}; -pub use libp2p_request_response::{InboundFailure, OutboundFailure}; - -pub(crate) mod proto { - #![allow(unreachable_pub)] - include!("v1/generated/mod.rs"); - pub(crate) use self::structs::{mod_Message::*, Message}; -} diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs deleted file mode 100644 index 8cab10a1d14..00000000000 --- a/protocols/autonat/src/v2.rs +++ /dev/null @@ -1,17 +0,0 @@ -use libp2p_swarm::StreamProtocol; - -pub mod client; -pub(crate) mod protocol; -pub mod server; - -pub(crate) mod generated { - #![allow(unreachable_pub)] - include!("v2/generated/mod.rs"); -} - -pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = - StreamProtocol::new("/libp2p/autonat/2/dial-request"); -pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = - StreamProtocol::new("/libp2p/autonat/2/dial-back"); - -type Nonce = u64; diff --git a/protocols/autonat/src/v2/client.rs b/protocols/autonat/src/v2/client.rs deleted file mode 100644 index d3272512f35..00000000000 --- a/protocols/autonat/src/v2/client.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod behaviour; -mod handler; - -pub use behaviour::Event; -pub use behaviour::{Behaviour, Config}; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs deleted file mode 100644 index 97509c05443..00000000000 --- a/protocols/autonat/src/v2/client/behaviour.rs +++ /dev/null @@ -1,439 +0,0 @@ -use std::{ - collections::{HashMap, VecDeque}, - task::{Context, Poll}, - time::Duration, -}; - -use either::Either; -use futures::FutureExt; -use futures_timer::Delay; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, - ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, -}; -use rand::prelude::*; -use rand_core::OsRng; -use std::fmt::{Debug, Display, Formatter}; - -use crate::v2::{protocol::DialRequest, Nonce}; - -use super::handler::{ - dial_back::{self, IncomingNonce}, - dial_request, -}; - -#[derive(Debug, Clone, Copy)] -pub struct Config { - /// How many candidates we will test at most. - pub(crate) max_candidates: usize, - - /// The interval at which we will attempt to confirm candidates as external addresses. - pub(crate) probe_interval: Duration, -} - -impl Config { - pub fn with_max_candidates(self, max_candidates: usize) -> Self { - Self { - max_candidates, - ..self - } - } - - pub fn with_probe_interval(self, probe_interval: Duration) -> Self { - Self { - probe_interval, - ..self - } - } -} - -impl Default for Config { - fn default() -> Self { - Self { - max_candidates: 10, - probe_interval: Duration::from_secs(5), - } - } -} - -pub struct Behaviour -where - R: RngCore + 'static, -{ - rng: R, - config: Config, - pending_events: VecDeque< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - >, - address_candidates: HashMap, - next_tick: Delay, - peer_info: HashMap, -} - -impl NetworkBehaviour for Behaviour -where - R: RngCore + 'static, -{ - type ConnectionHandler = Either; - - type ToSwarm = Event; - - fn handle_established_inbound_connection( - &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: &Multiaddr, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Right(dial_back::Handler::new())) - } - - fn handle_established_outbound_connection( - &mut self, - _: ConnectionId, - _: PeerId, - _: &Multiaddr, - _: Endpoint, - _: PortUse, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Left(dial_request::Handler::new())) - } - - fn on_swarm_event(&mut self, event: FromSwarm) { - match event { - FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { - self.address_candidates - .entry(addr.clone()) - .or_default() - .score += 1; - } - FromSwarm::ConnectionEstablished(ConnectionEstablished { - peer_id, - connection_id, - endpoint: _, - .. - }) => { - self.peer_info.insert( - connection_id, - ConnectionInfo { - peer_id, - supports_autonat: false, - }, - ); - } - FromSwarm::ConnectionClosed(ConnectionClosed { - peer_id, - connection_id, - .. - }) => { - let info = self - .peer_info - .remove(&connection_id) - .expect("inconsistent state"); - - if info.supports_autonat { - tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); - } - } - _ => {} - } - } - - fn on_connection_handler_event( - &mut self, - peer_id: PeerId, - connection_id: ConnectionId, - event: ::ToBehaviour, - ) { - let (nonce, outcome) = match event { - Either::Right(IncomingNonce { nonce, sender }) => { - let Some((_, info)) = self - .address_candidates - .iter_mut() - .find(|(_, info)| info.is_pending_with_nonce(nonce)) - else { - let _ = sender.send(Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!("Received unexpected nonce: {nonce} from {peer_id}"), - ))); - return; - }; - - info.status = TestStatus::Received(nonce); - tracing::debug!(%peer_id, %nonce, "Successful dial-back"); - - let _ = sender.send(Ok(())); - - return; - } - Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = true; - return; - } - Either::Left(dial_request::ToBehaviour::TestOutcome { nonce, outcome }) => { - (nonce, outcome) - } - }; - - let ((tested_addr, bytes_sent), result) = match outcome { - Ok(address) => { - let received_dial_back = self - .address_candidates - .iter_mut() - .any(|(_, info)| info.is_received_with_nonce(nonce)); - - if !received_dial_back { - tracing::warn!( - %peer_id, - %nonce, - "Server reported reachbility but we never received a dial-back" - ); - return; - } - - self.pending_events - .push_back(ToSwarm::ExternalAddrConfirmed(address.0.clone())); - - (address, Ok(())) - } - Err(dial_request::Error::UnsupportedProtocol) => { - self.peer_info - .get_mut(&connection_id) - .expect("inconsistent state") - .supports_autonat = false; - - self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. - - return; - } - Err(dial_request::Error::Io(e)) => { - tracing::debug!( - %peer_id, - %nonce, - "Failed to complete AutoNAT probe: {e}" - ); - - self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. - - return; - } - Err(dial_request::Error::AddressNotReachable { - address, - bytes_sent, - error, - }) => { - self.reset_status_to(nonce, TestStatus::Failed); - - ((address, bytes_sent), Err(error)) - } - }; - - self.pending_events.push_back(ToSwarm::GenerateEvent(Event { - tested_addr, - bytes_sent, - server: peer_id, - result: result.map_err(|e| Error { inner: e }), - })); - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll::FromBehaviour>> - { - loop { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - - if self.next_tick.poll_unpin(cx).is_ready() { - self.next_tick.reset(self.config.probe_interval); - - self.issue_dial_requests_for_untested_candidates(); - continue; - } - - return Poll::Pending; - } - } -} - -impl Behaviour -where - R: RngCore + 'static, -{ - pub fn new(rng: R, config: Config) -> Self { - Self { - rng, - next_tick: Delay::new(config.probe_interval), - config, - pending_events: VecDeque::new(), - address_candidates: HashMap::new(), - peer_info: HashMap::new(), - } - } - - /// Issues dial requests to random AutoNAT servers for the most frequently reported, untested candidates. - /// - /// In the current implementation, we only send a single address to each AutoNAT server. - /// This spreads our candidates out across all servers we are connected to which should give us pretty fast feedback on all of them. - fn issue_dial_requests_for_untested_candidates(&mut self) { - for addr in self.untested_candidates() { - let Some((conn_id, peer_id)) = self.random_autonat_server() else { - tracing::debug!("Not connected to any AutoNAT servers"); - return; - }; - - let nonce = self.rng.gen(); - self.address_candidates - .get_mut(&addr) - .expect("only emit candidates") - .status = TestStatus::Pending(nonce); - - self.pending_events.push_back(ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(conn_id), - event: Either::Left(DialRequest { - nonce, - addrs: vec![addr], - }), - }); - } - } - - /// Returns all untested candidates, sorted by the frequency they were reported at. - /// - /// More frequently reported candidates are considered to more likely be external addresses and thus tested first. - fn untested_candidates(&self) -> impl Iterator { - let mut entries = self - .address_candidates - .iter() - .filter(|(_, info)| info.status == TestStatus::Untested) - .map(|(addr, count)| (addr.clone(), *count)) - .collect::>(); - - entries.sort_unstable_by_key(|(_, info)| info.score); - - if entries.is_empty() { - tracing::debug!("No untested address candidates"); - } - - entries - .into_iter() - .rev() // `sort_unstable` is ascending - .take(self.config.max_candidates) - .map(|(addr, _)| addr) - } - - /// Chooses an active connection to one of our peers that reported support for the [`DIAL_REQUEST_PROTOCOL`](crate::v2::DIAL_REQUEST_PROTOCOL) protocol. - fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { - let (conn_id, info) = self - .peer_info - .iter() - .filter(|(_, info)| info.supports_autonat) - .choose(&mut self.rng)?; - - Some((*conn_id, info.peer_id)) - } - - fn reset_status_to(&mut self, nonce: Nonce, new_status: TestStatus) { - let Some((_, info)) = self - .address_candidates - .iter_mut() - .find(|(_, i)| i.is_pending_with_nonce(nonce) || i.is_received_with_nonce(nonce)) - else { - return; - }; - - info.status = new_status; - } - - // FIXME: We don't want test-only APIs in our public API. - #[doc(hidden)] - pub fn validate_addr(&mut self, addr: &Multiaddr) { - if let Some(info) = self.address_candidates.get_mut(addr) { - info.status = TestStatus::Received(self.rng.next_u64()); - } - } -} - -impl Default for Behaviour { - fn default() -> Self { - Self::new(OsRng, Config::default()) - } -} - -pub struct Error { - pub(crate) inner: dial_request::DialBackError, -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Debug for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self.inner, f) - } -} - -#[derive(Debug)] -pub struct Event { - /// The address that was selected for testing. - pub tested_addr: Multiaddr, - /// The amount of data that was sent to the server. - /// Is 0 if it wasn't necessary to send any data. - /// Otherwise it's a number between 30.000 and 100.000. - pub bytes_sent: usize, - /// The peer id of the server that was selected for testing. - pub server: PeerId, - /// The result of the test. If the test was successful, this is `Ok(())`. - /// Otherwise it's an error. - pub result: Result<(), Error>, -} - -struct ConnectionInfo { - peer_id: PeerId, - supports_autonat: bool, -} - -#[derive(Copy, Clone, Default)] -struct AddressInfo { - score: usize, - status: TestStatus, -} - -impl AddressInfo { - fn is_pending_with_nonce(&self, nonce: Nonce) -> bool { - match self.status { - TestStatus::Pending(c) => c == nonce, - _ => false, - } - } - - fn is_received_with_nonce(&self, nonce: Nonce) -> bool { - match self.status { - TestStatus::Received(c) => c == nonce, - _ => false, - } - } -} - -#[derive(Clone, Copy, Default, PartialEq)] -enum TestStatus { - #[default] - Untested, - Pending(Nonce), - Failed, - Received(Nonce), -} diff --git a/protocols/autonat/src/v2/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs deleted file mode 100644 index e526c2fb44c..00000000000 --- a/protocols/autonat/src/v2/client/handler.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod dial_back; -pub(crate) mod dial_request; diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs deleted file mode 100644 index b94580e69ba..00000000000 --- a/protocols/autonat/src/v2/client/handler/dial_back.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::{ - io, - task::{Context, Poll}, - time::Duration, -}; - -use futures::channel::oneshot; -use futures_bounded::StreamSet; -use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; -use libp2p_swarm::{ - handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, -}; -use void::Void; - -use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; - -pub struct Handler { - inbound: StreamSet>, -} - -impl Handler { - pub(crate) fn new() -> Self { - Self { - inbound: StreamSet::new(Duration::from_secs(5), 2), - } - } -} - -impl ConnectionHandler for Handler { - type FromBehaviour = Void; - type ToBehaviour = IncomingNonce; - type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = DeniedUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - loop { - match self.inbound.poll_next_unpin(cx) { - Poll::Pending => return Poll::Pending, - Poll::Ready(None) => continue, - Poll::Ready(Some(Err(err))) => { - tracing::debug!("Stream timed out: {err}"); - continue; - } - Poll::Ready(Some(Ok(Err(err)))) => { - tracing::debug!("Dial back handler failed with: {err:?}"); - continue; - } - Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)); - } - } - } - } - - fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - if self.inbound.try_push(perform_dial_back(protocol)).is_err() { - tracing::warn!("Dial back request dropped, too many requests in flight"); - } - } - ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { - void::unreachable(error); - } - _ => {} - } - } -} - -struct State { - stream: libp2p_swarm::Stream, - oneshot: Option>>, -} - -#[derive(Debug)] -pub struct IncomingNonce { - pub nonce: Nonce, - pub sender: oneshot::Sender>, -} - -fn perform_dial_back( - stream: libp2p_swarm::Stream, -) -> impl futures::Stream> { - let state = State { - stream, - oneshot: None, - }; - futures::stream::unfold(state, |mut state| async move { - if let Some(ref mut receiver) = state.oneshot { - match receiver.await { - Ok(Ok(())) => {} - Ok(Err(e)) => return Some((Err(e), state)), - Err(_) => { - return Some(( - Err(io::Error::new(io::ErrorKind::Other, "Sender got cancelled")), - state, - )); - } - } - if let Err(e) = protocol::dial_back_response(&mut state.stream).await { - return Some((Err(e), state)); - } - return None; - } - - let nonce = match protocol::recv_dial_back(&mut state.stream).await { - Ok(nonce) => nonce, - Err(err) => { - return Some((Err(err), state)); - } - }; - - let (sender, receiver) = oneshot::channel(); - state.oneshot = Some(receiver); - Some((Ok(IncomingNonce { nonce, sender }), state)) - }) -} diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs deleted file mode 100644 index 5191382734f..00000000000 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ /dev/null @@ -1,332 +0,0 @@ -use futures::{channel::oneshot, AsyncWrite}; -use futures_bounded::FuturesMap; -use libp2p_core::{ - upgrade::{DeniedUpgrade, ReadyUpgrade}, - Multiaddr, -}; - -use libp2p_swarm::{ - handler::{ - ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, - ProtocolsChange, - }, - ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, - SubstreamProtocol, -}; -use std::{ - collections::VecDeque, - io, - iter::{once, repeat}, - task::{Context, Poll}, - time::Duration, -}; - -use crate::v2::{ - generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - protocol::{ - Coder, DialDataRequest, DialDataResponse, DialRequest, Response, - DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, - }, - Nonce, DIAL_REQUEST_PROTOCOL, -}; - -#[derive(Debug)] -pub enum ToBehaviour { - TestOutcome { - nonce: Nonce, - outcome: Result<(Multiaddr, usize), Error>, - }, - PeerHasServerSupport, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Address is not reachable: {error}")] - AddressNotReachable { - address: Multiaddr, - bytes_sent: usize, - error: DialBackError, - }, - #[error("Peer does not support AutoNAT dial-request protocol")] - UnsupportedProtocol, - #[error("IO error: {0}")] - Io(io::Error), -} - -impl From for Error { - fn from(value: io::Error) -> Self { - Self::Io(value) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum DialBackError { - #[error("server failed to establish a connection")] - NoConnection, - #[error("dial back stream failed")] - StreamFailed, -} - -pub struct Handler { - queued_events: VecDeque< - ConnectionHandlerEvent< - ::OutboundProtocol, - ::OutboundOpenInfo, - ::ToBehaviour, - >, - >, - outbound: FuturesMap>, - queued_streams: VecDeque< - oneshot::Sender< - Result< - Stream, - StreamUpgradeError< as OutboundUpgradeSend>::Error>, - >, - >, - >, -} - -impl Handler { - pub(crate) fn new() -> Self { - Self { - queued_events: VecDeque::new(), - outbound: FuturesMap::new(Duration::from_secs(10), 10), - queued_streams: VecDeque::default(), - } - } - - fn perform_request(&mut self, req: DialRequest) { - let (tx, rx) = oneshot::channel(); - self.queued_streams.push_back(tx); - self.queued_events - .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()), - }); - if self - .outbound - .try_push(req.nonce, start_stream_handle(req, rx)) - .is_err() - { - tracing::debug!("Dial request dropped, too many requests in flight"); - } - } -} - -impl ConnectionHandler for Handler { - type FromBehaviour = DialRequest; - type ToBehaviour = ToBehaviour; - type InboundProtocol = DeniedUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(DeniedUpgrade, ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - if let Some(event) = self.queued_events.pop_front() { - return Poll::Ready(event); - } - - match self.outbound.poll_unpin(cx) { - Poll::Ready((nonce, Ok(outcome))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestOutcome { nonce, outcome }, - )) - } - Poll::Ready((nonce, Err(_))) => { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::TestOutcome { - nonce, - outcome: Err(Error::Io(io::ErrorKind::TimedOut.into())), - }, - )); - } - Poll::Pending => {} - } - - Poll::Pending - } - - fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { - self.perform_request(event); - } - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { - tracing::debug!("Dial request failed: {}", error); - match self.queued_streams.pop_front() { - Some(stream_tx) => { - let _ = stream_tx.send(Err(error)); - } - None => { - tracing::warn!( - "Opened unexpected substream without a pending dial request" - ); - } - } - } - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, .. - }) => match self.queued_streams.pop_front() { - Some(stream_tx) => { - if stream_tx.send(Ok(protocol)).is_err() { - tracing::debug!("Failed to send stream to dead handler"); - } - } - None => { - tracing::warn!("Opened unexpected substream without a pending dial request"); - } - }, - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { - if added.any(|p| p.as_ref() == DIAL_REQUEST_PROTOCOL) { - self.queued_events - .push_back(ConnectionHandlerEvent::NotifyBehaviour( - ToBehaviour::PeerHasServerSupport, - )); - } - } - _ => {} - } - } -} - -async fn start_stream_handle( - req: DialRequest, - stream_recv: oneshot::Receiver>>, -) -> Result<(Multiaddr, usize), Error> { - let stream = stream_recv - .await - .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))? - .map_err(|e| match e { - StreamUpgradeError::NegotiationFailed => Error::UnsupportedProtocol, - StreamUpgradeError::Timeout => Error::Io(io::ErrorKind::TimedOut.into()), - StreamUpgradeError::Apply(v) => void::unreachable(v), - StreamUpgradeError::Io(e) => Error::Io(e), - })?; - - let mut coder = Coder::new(stream); - coder.send(req.clone()).await?; - - let (res, bytes_sent) = match coder.next().await? { - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => { - if addr_idx >= req.addrs.len() { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "address index out of bounds", - ))); - } - if !(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND).contains(&num_bytes) { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "requested bytes out of bounds", - ))); - } - - send_aap_data(&mut coder, num_bytes).await?; - - let Response::Dial(dial_response) = coder.next().await? else { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "expected message", - ))); - }; - - (dial_response, num_bytes) - } - Response::Dial(dial_response) => (dial_response, 0), - }; - coder.close().await?; - - match res.status { - ResponseStatus::E_REQUEST_REJECTED => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "server rejected request", - ))) - } - ResponseStatus::E_DIAL_REFUSED => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "server refused dial", - ))) - } - ResponseStatus::E_INTERNAL_ERROR => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::Other, - "server encountered internal error", - ))) - } - ResponseStatus::OK => {} - } - - let tested_address = req - .addrs - .get(res.addr_idx) - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "address index out of bounds"))? - .clone(); - - match res.dial_status { - DialStatus::UNUSED => { - return Err(Error::Io(io::Error::new( - io::ErrorKind::InvalidInput, - "unexpected message", - ))) - } - DialStatus::E_DIAL_ERROR => { - return Err(Error::AddressNotReachable { - address: tested_address, - bytes_sent, - error: DialBackError::NoConnection, - }) - } - DialStatus::E_DIAL_BACK_ERROR => { - return Err(Error::AddressNotReachable { - address: tested_address, - bytes_sent, - error: DialBackError::StreamFailed, - }) - } - DialStatus::OK => {} - } - - Ok((tested_address, bytes_sent)) -} - -async fn send_aap_data(stream: &mut Coder, num_bytes: usize) -> io::Result<()> -where - I: AsyncWrite + Unpin, -{ - let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; - let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; - for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) - .take(count_full) - .chain(once(partial_len)) - .filter(|e| *e > 0) - .map(|data_count| { - DialDataResponse::new(data_count).expect("data count is unexpectedly too big") - }) - { - stream.send(req).await?; - } - - Ok(()) -} diff --git a/protocols/autonat/src/v2/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs deleted file mode 100644 index e52c5a80bc0..00000000000 --- a/protocols/autonat/src/v2/generated/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Automatically generated mod.rs -pub mod structs; diff --git a/protocols/autonat/src/v2/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto deleted file mode 100644 index 31791463956..00000000000 --- a/protocols/autonat/src/v2/generated/structs.proto +++ /dev/null @@ -1,54 +0,0 @@ -syntax = "proto2"; - -package structs; - -message Message { - oneof msg { - DialRequest dialRequest = 1; - DialResponse dialResponse = 2; - DialDataRequest dialDataRequest = 3; - DialDataResponse dialDataResponse = 4; - } -} - -message DialRequest { - repeated bytes addrs = 1; - fixed64 nonce = 2; -} - -message DialDataRequest { - uint32 addrIdx = 1; - uint64 numBytes = 2; -} - -enum DialStatus { - UNUSED = 0; - E_DIAL_ERROR = 100; - E_DIAL_BACK_ERROR = 101; - OK = 200; -} - -message DialResponse { - enum ResponseStatus { - E_INTERNAL_ERROR = 0; - E_REQUEST_REJECTED = 100; - E_DIAL_REFUSED = 101; - OK = 200; - } - - ResponseStatus status = 1; - uint32 addrIdx = 2; - DialStatus dialStatus = 3; -} - -message DialDataResponse { bytes data = 1; } - -message DialBack { fixed64 nonce = 1; } - -message DialBackResponse { - enum DialBackStatus { - OK = 0; - } - - DialBackStatus status = 1; -} diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs deleted file mode 100644 index 12568dd0364..00000000000 --- a/protocols/autonat/src/v2/generated/structs.rs +++ /dev/null @@ -1,403 +0,0 @@ -// Automatically generated rust module for 'structs.proto' file - -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(unused_imports)] -#![allow(unknown_lints)] -#![allow(clippy::all)] -#![cfg_attr(rustfmt, rustfmt_skip)] - - -use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; -use quick_protobuf::sizeofs::*; -use super::*; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DialStatus { - UNUSED = 0, - E_DIAL_ERROR = 100, - E_DIAL_BACK_ERROR = 101, - OK = 200, -} - -impl Default for DialStatus { - fn default() -> Self { - DialStatus::UNUSED - } -} - -impl From for DialStatus { - fn from(i: i32) -> Self { - match i { - 0 => DialStatus::UNUSED, - 100 => DialStatus::E_DIAL_ERROR, - 101 => DialStatus::E_DIAL_BACK_ERROR, - 200 => DialStatus::OK, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for DialStatus { - fn from(s: &'a str) -> Self { - match s { - "UNUSED" => DialStatus::UNUSED, - "E_DIAL_ERROR" => DialStatus::E_DIAL_ERROR, - "E_DIAL_BACK_ERROR" => DialStatus::E_DIAL_BACK_ERROR, - "OK" => DialStatus::OK, - _ => Self::default(), - } - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct Message { - pub msg: structs::mod_Message::OneOfmsg, -} - -impl<'a> MessageRead<'a> for Message { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.msg = structs::mod_Message::OneOfmsg::dialRequest(r.read_message::(bytes)?), - Ok(18) => msg.msg = structs::mod_Message::OneOfmsg::dialResponse(r.read_message::(bytes)?), - Ok(26) => msg.msg = structs::mod_Message::OneOfmsg::dialDataRequest(r.read_message::(bytes)?), - Ok(34) => msg.msg = structs::mod_Message::OneOfmsg::dialDataResponse(r.read_message::(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for Message { - fn get_size(&self) -> usize { - 0 - + match self.msg { - structs::mod_Message::OneOfmsg::dialRequest(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::dialResponse(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => 1 + sizeof_len((m).get_size()), - structs::mod_Message::OneOfmsg::None => 0, - } } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - match self.msg { structs::mod_Message::OneOfmsg::dialRequest(ref m) => { w.write_with_tag(10, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::dialResponse(ref m) => { w.write_with_tag(18, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => { w.write_with_tag(26, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => { w.write_with_tag(34, |w| w.write_message(m))? }, - structs::mod_Message::OneOfmsg::None => {}, - } Ok(()) - } -} - -pub mod mod_Message { - -use super::*; - -#[derive(Debug, PartialEq, Clone)] -pub enum OneOfmsg { - dialRequest(structs::DialRequest), - dialResponse(structs::DialResponse), - dialDataRequest(structs::DialDataRequest), - dialDataResponse(structs::DialDataResponse), - None, -} - -impl Default for OneOfmsg { - fn default() -> Self { - OneOfmsg::None - } -} - -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialRequest { - pub addrs: Vec>, - pub nonce: Option, -} - -impl<'a> MessageRead<'a> for DialRequest { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), - Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialRequest { - fn get_size(&self) -> usize { - 0 - + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() - + self.nonce.as_ref().map_or(0, |_| 1 + 8) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - for s in &self.addrs { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } - if let Some(ref s) = self.nonce { w.write_with_tag(17, |w| w.write_fixed64(*s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialDataRequest { - pub addrIdx: Option, - pub numBytes: Option, -} - -impl<'a> MessageRead<'a> for DialDataRequest { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.addrIdx = Some(r.read_uint32(bytes)?), - Ok(16) => msg.numBytes = Some(r.read_uint64(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialDataRequest { - fn get_size(&self) -> usize { - 0 - + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.numBytes.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.addrIdx { w.write_with_tag(8, |w| w.write_uint32(*s))?; } - if let Some(ref s) = self.numBytes { w.write_with_tag(16, |w| w.write_uint64(*s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialResponse { - pub status: Option, - pub addrIdx: Option, - pub dialStatus: Option, -} - -impl<'a> MessageRead<'a> for DialResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), - Ok(16) => msg.addrIdx = Some(r.read_uint32(bytes)?), - Ok(24) => msg.dialStatus = Some(r.read_enum(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialResponse { - fn get_size(&self) -> usize { - 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.dialStatus.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - if let Some(ref s) = self.addrIdx { w.write_with_tag(16, |w| w.write_uint32(*s))?; } - if let Some(ref s) = self.dialStatus { w.write_with_tag(24, |w| w.write_enum(*s as i32))?; } - Ok(()) - } -} - -pub mod mod_DialResponse { - - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum ResponseStatus { - E_INTERNAL_ERROR = 0, - E_REQUEST_REJECTED = 100, - E_DIAL_REFUSED = 101, - OK = 200, -} - -impl Default for ResponseStatus { - fn default() -> Self { - ResponseStatus::E_INTERNAL_ERROR - } -} - -impl From for ResponseStatus { - fn from(i: i32) -> Self { - match i { - 0 => ResponseStatus::E_INTERNAL_ERROR, - 100 => ResponseStatus::E_REQUEST_REJECTED, - 101 => ResponseStatus::E_DIAL_REFUSED, - 200 => ResponseStatus::OK, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for ResponseStatus { - fn from(s: &'a str) -> Self { - match s { - "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, - "E_REQUEST_REJECTED" => ResponseStatus::E_REQUEST_REJECTED, - "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, - "OK" => ResponseStatus::OK, - _ => Self::default(), - } - } -} - -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialDataResponse { - pub data: Option>, -} - -impl<'a> MessageRead<'a> for DialDataResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned()), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialDataResponse { - fn get_size(&self) -> usize { - 0 - + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.data { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialBack { - pub nonce: Option, -} - -impl<'a> MessageRead<'a> for DialBack { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(9) => msg.nonce = Some(r.read_fixed64(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialBack { - fn get_size(&self) -> usize { - 0 - + self.nonce.as_ref().map_or(0, |_| 1 + 8) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.nonce { w.write_with_tag(9, |w| w.write_fixed64(*s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialBackResponse { - pub status: Option, -} - -impl<'a> MessageRead<'a> for DialBackResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialBackResponse { - fn get_size(&self) -> usize { - 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - Ok(()) - } -} - -pub mod mod_DialBackResponse { - - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DialBackStatus { - OK = 0, -} - -impl Default for DialBackStatus { - fn default() -> Self { - DialBackStatus::OK - } -} - -impl From for DialBackStatus { - fn from(i: i32) -> Self { - match i { - 0 => DialBackStatus::OK, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for DialBackStatus { - fn from(s: &'a str) -> Self { - match s { - "OK" => DialBackStatus::OK, - _ => Self::default(), - } - } -} - -} - diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs deleted file mode 100644 index d0e2fc30ca6..00000000000 --- a/protocols/autonat/src/v2/protocol.rs +++ /dev/null @@ -1,362 +0,0 @@ -// change to quick-protobuf-codec - -use std::io; -use std::io::ErrorKind; - -use asynchronous_codec::{Framed, FramedRead, FramedWrite}; - -use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt}; -use libp2p_core::Multiaddr; - -use quick_protobuf_codec::Codec; -use rand::Rng; - -use crate::v2::{generated::structs as proto, Nonce}; - -const REQUEST_MAX_SIZE: usize = 4104; -pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; -pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; -pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; - -fn new_io_invalid_data_err(msg: impl Into) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, msg.into()) -} - -macro_rules! ok_or_invalid_data { - ($field:ident) => { - $field.ok_or_else(|| new_io_invalid_data_err(concat!(stringify!($field), " is missing"))) - }; -} - -pub(crate) struct Coder { - inner: Framed>, -} - -impl Coder -where - I: AsyncWrite + AsyncRead + Unpin, -{ - pub(crate) fn new(io: I) -> Self { - Self { - inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), - } - } - pub(crate) async fn close(mut self) -> io::Result<()> { - self.inner.close().await?; - Ok(()) - } -} - -impl Coder -where - I: AsyncRead + Unpin, -{ - pub(crate) async fn next(&mut self) -> io::Result - where - proto::Message: TryInto, - io::Error: From, - { - Ok(self.next_msg().await?.try_into()?) - } - - async fn next_msg(&mut self) -> io::Result { - self.inner - .next() - .await - .ok_or(io::Error::new( - ErrorKind::UnexpectedEof, - "no request to read", - ))? - .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) - } -} - -impl Coder -where - I: AsyncWrite + Unpin, -{ - pub(crate) async fn send(&mut self, msg: M) -> io::Result<()> - where - M: Into, - { - self.inner.send(msg.into()).await?; - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub(crate) enum Request { - Dial(DialRequest), - Data(DialDataResponse), -} - -impl From for proto::Message { - fn from(val: DialRequest) -> Self { - let addrs = val.addrs.iter().map(|e| e.to_vec()).collect(); - let nonce = Some(val.nonce); - - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }), - } - } -} - -impl From for proto::Message { - fn from(val: DialDataResponse) -> Self { - debug_assert!( - val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, - "data_count too large" - ); - proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(vec![0; val.data_count]), // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI - }), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct DialRequest { - pub(crate) addrs: Vec, - pub(crate) nonce: u64, -} - -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct DialDataResponse { - data_count: usize, -} - -impl DialDataResponse { - pub(crate) fn new(data_count: usize) -> Option { - if data_count <= DATA_FIELD_LEN_UPPER_BOUND { - Some(Self { data_count }) - } else { - None - } - } - - pub(crate) fn get_data_count(&self) -> usize { - self.data_count - } -} - -impl TryFrom for Request { - type Error = io::Error; - - fn try_from(msg: proto::Message) -> Result { - match msg.msg { - proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { - let addrs = addrs - .into_iter() - .map(|e| e.to_vec()) - .map(|e| { - Multiaddr::try_from(e).map_err(|err| { - new_io_invalid_data_err(format!("invalid multiaddr: {}", err)) - }) - }) - .collect::, io::Error>>()?; - let nonce = ok_or_invalid_data!(nonce)?; - Ok(Self::Dial(DialRequest { addrs, nonce })) - } - proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { - let data_count = ok_or_invalid_data!(data)?.len(); - Ok(Self::Data(DialDataResponse { data_count })) - } - _ => Err(new_io_invalid_data_err( - "expected dialResponse or dialDataRequest", - )), - } - } -} - -#[derive(Debug, Clone)] -pub(crate) enum Response { - Dial(DialResponse), - Data(DialDataRequest), -} - -#[derive(Debug, Clone)] -pub(crate) struct DialDataRequest { - pub(crate) addr_idx: usize, - pub(crate) num_bytes: usize, -} - -#[derive(Debug, Clone)] -pub(crate) struct DialResponse { - pub(crate) status: proto::mod_DialResponse::ResponseStatus, - pub(crate) addr_idx: usize, - pub(crate) dial_status: proto::DialStatus, -} - -impl TryFrom for Response { - type Error = io::Error; - - fn try_from(msg: proto::Message) -> Result { - match msg.msg { - proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { - status, - addrIdx, - dialStatus, - }) => { - let status = ok_or_invalid_data!(status)?; - let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; - let dial_status = ok_or_invalid_data!(dialStatus)?; - Ok(Response::Dial(DialResponse { - status, - addr_idx, - dial_status, - })) - } - proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx, - numBytes, - }) => { - let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; - let num_bytes = ok_or_invalid_data!(numBytes)? as usize; - Ok(Self::Data(DialDataRequest { - addr_idx, - num_bytes, - })) - } - _ => Err(new_io_invalid_data_err( - "invalid message type, expected dialResponse or dialDataRequest", - )), - } - } -} - -impl From for proto::Message { - fn from(val: Response) -> Self { - match val { - Response::Dial(DialResponse { - status, - addr_idx, - dial_status, - }) => proto::Message { - msg: proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { - status: Some(status), - addrIdx: Some(addr_idx as u32), - dialStatus: Some(dial_status), - }), - }, - Response::Data(DialDataRequest { - addr_idx, - num_bytes, - }) => proto::Message { - msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), - }), - }, - } - } -} - -impl DialDataRequest { - pub(crate) fn from_rng(addr_idx: usize, mut rng: R) -> Self { - let num_bytes = rng.gen_range(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND); - Self { - addr_idx, - num_bytes, - } - } -} - -const DIAL_BACK_MAX_SIZE: usize = 10; - -pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let msg = proto::DialBack { nonce: Some(nonce) }; - let mut framed = FramedWrite::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); - - framed - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - - Ok(()) -} - -pub(crate) async fn recv_dial_back(stream: impl AsyncRead + Unpin) -> io::Result { - let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); - let proto::DialBack { nonce } = framed - .next() - .await - .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - let nonce = ok_or_invalid_data!(nonce)?; - - Ok(nonce) -} - -pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::Result<()> { - let msg = proto::DialBackResponse { - status: Some(proto::mod_DialBackResponse::DialBackStatus::OK), - }; - let mut framed = FramedWrite::new( - stream, - Codec::::new(DIAL_BACK_MAX_SIZE), - ); - framed - .send(msg) - .await - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - framed.close().await?; - - Ok(()) -} - -pub(crate) async fn recv_dial_back_response( - stream: impl AsyncRead + AsyncWrite + Unpin, -) -> io::Result<()> { - let framed = &mut FramedRead::new( - stream, - Codec::::new(DIAL_BACK_MAX_SIZE), - ); - let proto::DialBackResponse { status } = framed - .next() - .await - .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - framed.close().await?; - if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "invalid dial back response", - )) - } -} - -#[cfg(test)] -mod tests { - use crate::v2::generated::structs::{ - mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, - }; - - #[test] - fn message_correct_max_size() { - let message_bytes = quick_protobuf::serialize_into_vec(&Message { - msg: OneOfmsg::dialDataResponse(GenDialDataResponse { - data: Some(vec![0; 4096]), - }), - }) - .unwrap(); - assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); - } - - #[test] - fn dial_back_correct_size() { - let dial_back = super::proto::DialBack { nonce: Some(0) }; - let buf = quick_protobuf::serialize_into_vec(&dial_back).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - - let dial_back_none = super::proto::DialBack { nonce: None }; - let buf = quick_protobuf::serialize_into_vec(&dial_back_none).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - - let dial_back_max_nonce = super::proto::DialBack { - nonce: Some(u64::MAX), - }; - let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - } -} diff --git a/protocols/autonat/src/v2/server.rs b/protocols/autonat/src/v2/server.rs deleted file mode 100644 index 25819307784..00000000000 --- a/protocols/autonat/src/v2/server.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod behaviour; -mod handler; - -pub use behaviour::Behaviour; -pub use behaviour::Event; diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs deleted file mode 100644 index 97446c6263f..00000000000 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::{ - collections::{HashMap, VecDeque}, - io, - task::{Context, Poll}, -}; - -use crate::v2::server::handler::dial_request::DialBackStatus; -use either::Either; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_swarm::dial_opts::PeerCondition; -use libp2p_swarm::{ - dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, - FromSwarm, NetworkBehaviour, ToSwarm, -}; -use rand_core::{OsRng, RngCore}; - -use crate::v2::server::handler::{ - dial_back, - dial_request::{self, DialBackCommand}, - Handler, -}; - -pub struct Behaviour -where - R: Clone + Send + RngCore + 'static, -{ - dialing_dial_back: HashMap, - pending_events: VecDeque< - ToSwarm< - ::ToSwarm, - <::ConnectionHandler as ConnectionHandler>::FromBehaviour, - >, - >, - rng: R, -} - -impl Default for Behaviour { - fn default() -> Self { - Self::new(OsRng) - } -} - -impl Behaviour -where - R: RngCore + Send + Clone + 'static, -{ - pub fn new(rng: R) -> Self { - Self { - dialing_dial_back: HashMap::new(), - pending_events: VecDeque::new(), - rng, - } - } -} - -impl NetworkBehaviour for Behaviour -where - R: RngCore + Send + Clone + 'static, -{ - type ConnectionHandler = Handler; - - type ToSwarm = Event; - - fn handle_established_inbound_connection( - &mut self, - _connection_id: ConnectionId, - peer: PeerId, - _local_addr: &Multiaddr, - remote_addr: &Multiaddr, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(Either::Right(dial_request::Handler::new( - peer, - remote_addr.clone(), - self.rng.clone(), - ))) - } - - fn handle_established_outbound_connection( - &mut self, - connection_id: ConnectionId, - _peer: PeerId, - _addr: &Multiaddr, - _role_override: Endpoint, - _port_use: PortUse, - ) -> Result<::ConnectionHandler, ConnectionDenied> { - Ok(match self.dialing_dial_back.remove(&connection_id) { - Some(cmd) => Either::Left(Either::Left(dial_back::Handler::new(cmd))), - None => Either::Left(Either::Right(dummy::ConnectionHandler)), - }) - } - - fn on_swarm_event(&mut self, event: FromSwarm) { - if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { - if let Some(DialBackCommand { back_channel, .. }) = - self.dialing_dial_back.remove(&connection_id) - { - let _ = back_channel.send(Err(DialBackStatus::DialErr)); - } - } - } - - fn on_connection_handler_event( - &mut self, - peer_id: PeerId, - _connection_id: ConnectionId, - event: as ConnectionHandler>::ToBehaviour, - ) { - match event { - Either::Left(Either::Left(Ok(_))) => {} - Either::Left(Either::Left(Err(e))) => { - tracing::debug!("dial back error: {e:?}"); - } - Either::Left(Either::Right(v)) => void::unreachable(v), - Either::Right(Either::Left(cmd)) => { - let addr = cmd.addr.clone(); - let opts = DialOpts::peer_id(peer_id) - .addresses(Vec::from([addr])) - .condition(PeerCondition::Always) - .allocate_new_port() - .build(); - let conn_id = opts.connection_id(); - self.dialing_dial_back.insert(conn_id, cmd); - self.pending_events.push_back(ToSwarm::Dial { opts }); - } - Either::Right(Either::Right(status_update)) => self - .pending_events - .push_back(ToSwarm::GenerateEvent(status_update)), - } - } - - fn poll( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll as ConnectionHandler>::FromBehaviour>> { - if let Some(event) = self.pending_events.pop_front() { - return Poll::Ready(event); - } - Poll::Pending - } -} - -#[derive(Debug)] -pub struct Event { - /// All address that were submitted for testing. - pub all_addrs: Vec, - /// The address that was eventually tested. - pub tested_addr: Multiaddr, - /// The peer id of the client that submitted addresses for testing. - pub client: PeerId, - /// The amount of data that was requested by the server and was transmitted. - pub data_amount: usize, - /// The result of the test. - pub result: Result<(), io::Error>, -} diff --git a/protocols/autonat/src/v2/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs deleted file mode 100644 index ffdad69c86f..00000000000 --- a/protocols/autonat/src/v2/server/handler.rs +++ /dev/null @@ -1,8 +0,0 @@ -use either::Either; -use libp2p_swarm::dummy; - -pub(crate) mod dial_back; -pub(crate) mod dial_request; - -pub(crate) type Handler = - Either, dial_request::Handler>; diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs deleted file mode 100644 index 3cacd4ff32b..00000000000 --- a/protocols/autonat/src/v2/server/handler/dial_back.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::{ - convert::identity, - io, - task::{Context, Poll}, - time::Duration, -}; - -use futures::{AsyncRead, AsyncWrite}; -use futures_bounded::FuturesSet; -use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; -use libp2p_swarm::{ - handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, - SubstreamProtocol, -}; - -use crate::v2::{ - protocol::{dial_back, recv_dial_back_response}, - DIAL_BACK_PROTOCOL, -}; - -use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; - -pub(crate) type ToBehaviour = io::Result<()>; - -pub struct Handler { - pending_nonce: Option, - requested_substream_nonce: Option, - outbound: FuturesSet, -} - -impl Handler { - pub(crate) fn new(cmd: DialBackCommand) -> Self { - Self { - pending_nonce: Some(cmd), - requested_substream_nonce: None, - outbound: FuturesSet::new(Duration::from_secs(10), 5), - } - } -} - -impl ConnectionHandler for Handler { - type FromBehaviour = (); - type ToBehaviour = ToBehaviour; - type InboundProtocol = DeniedUpgrade; - type OutboundProtocol = ReadyUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(DeniedUpgrade, ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - if let Poll::Ready(result) = self.outbound.poll_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( - result - .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) - .and_then(identity), - )); - } - if let Some(cmd) = self.pending_nonce.take() { - self.requested_substream_nonce = Some(cmd); - return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()), - }); - } - Poll::Pending - } - - fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { - protocol, .. - }) => { - if let Some(cmd) = self.requested_substream_nonce.take() { - if self - .outbound - .try_push(perform_dial_back(protocol, cmd)) - .is_err() - { - tracing::warn!("Dial back dropped, too many requests in flight"); - } - } else { - tracing::warn!("received dial back substream without nonce"); - } - } - ConnectionEvent::DialUpgradeError(DialUpgradeError { - error: StreamUpgradeError::NegotiationFailed | StreamUpgradeError::Timeout, - .. - }) => { - if let Some(cmd) = self.requested_substream_nonce.take() { - let _ = cmd.back_channel.send(Err(DialBackRes::DialBackErr)); - } - } - _ => {} - } - } -} - -async fn perform_dial_back( - mut stream: impl AsyncRead + AsyncWrite + Unpin, - DialBackCommand { - nonce, - back_channel, - .. - }: DialBackCommand, -) -> io::Result<()> { - let res = dial_back(&mut stream, nonce) - .await - .map_err(|_| DialBackRes::DialBackErr) - .map(|_| ()); - - let res = match res { - Ok(()) => recv_dial_back_response(stream) - .await - .map_err(|_| DialBackRes::DialBackErr) - .map(|_| ()), - Err(e) => Err(e), - }; - back_channel - .send(res) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; - Ok(()) -} diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs deleted file mode 100644 index f8c3f6b654d..00000000000 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ /dev/null @@ -1,332 +0,0 @@ -use std::{ - io, - task::{Context, Poll}, - time::Duration, -}; - -use either::Either; -use futures::{ - channel::{mpsc, oneshot}, - AsyncRead, AsyncWrite, SinkExt, StreamExt, -}; -use futures_bounded::FuturesSet; -use libp2p_core::{ - upgrade::{DeniedUpgrade, ReadyUpgrade}, - Multiaddr, -}; -use libp2p_identity::PeerId; -use libp2p_swarm::{ - handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, - ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, -}; -use rand_core::RngCore; - -use crate::v2::{ - generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, - server::behaviour::Event, - Nonce, DIAL_REQUEST_PROTOCOL, -}; - -#[derive(Debug, PartialEq)] -pub(crate) enum DialBackStatus { - /// Failure during dial - DialErr, - /// Failure during dial back - DialBackErr, -} - -#[derive(Debug)] -pub struct DialBackCommand { - pub(crate) addr: Multiaddr, - pub(crate) nonce: Nonce, - pub(crate) back_channel: oneshot::Sender>, -} - -pub struct Handler { - client_id: PeerId, - observed_multiaddr: Multiaddr, - dial_back_cmd_sender: mpsc::Sender, - dial_back_cmd_receiver: mpsc::Receiver, - inbound: FuturesSet, - rng: R, -} - -impl Handler -where - R: RngCore, -{ - pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { - let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); - Self { - client_id, - observed_multiaddr, - dial_back_cmd_sender, - dial_back_cmd_receiver, - inbound: FuturesSet::new(Duration::from_secs(10), 10), - rng, - } - } -} - -impl ConnectionHandler for Handler -where - R: RngCore + Send + Clone + 'static, -{ - type FromBehaviour = void::Void; - type ToBehaviour = Either; - type InboundProtocol = ReadyUpgrade; - type OutboundProtocol = DeniedUpgrade; - type InboundOpenInfo = (); - type OutboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()) - } - - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll< - ConnectionHandlerEvent, - > { - loop { - match self.inbound.poll_unpin(cx) { - Poll::Ready(Ok(event)) => { - if let Err(e) = &event.result { - tracing::warn!("inbound request handle failed: {:?}", e); - } - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( - event, - ))); - } - Poll::Ready(Err(e)) => { - tracing::warn!("inbound request handle timed out {e:?}"); - } - Poll::Pending => break, - } - } - if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { - return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); - } - Poll::Pending - } - - fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} - - fn on_connection_event( - &mut self, - event: ConnectionEvent< - Self::InboundProtocol, - Self::OutboundProtocol, - Self::InboundOpenInfo, - Self::OutboundOpenInfo, - >, - ) { - match event { - ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { - protocol, .. - }) => { - if self - .inbound - .try_push(handle_request( - protocol, - self.observed_multiaddr.clone(), - self.client_id, - self.dial_back_cmd_sender.clone(), - self.rng.clone(), - )) - .is_err() - { - tracing::warn!( - "failed to push inbound request handler, too many requests in flight" - ); - } - } - ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { - tracing::debug!("inbound request failed: {:?}", error); - } - _ => {} - } - } -} - -enum HandleFail { - InternalError(usize), - RequestRejected, - DialRefused, - DialBack { - idx: usize, - result: Result<(), DialBackStatus>, - }, -} - -impl From for DialResponse { - fn from(value: HandleFail) -> Self { - match value { - HandleFail::InternalError(addr_idx) => Self { - status: ResponseStatus::E_INTERNAL_ERROR, - addr_idx, - dial_status: DialStatus::UNUSED, - }, - HandleFail::RequestRejected => Self { - status: ResponseStatus::E_REQUEST_REJECTED, - addr_idx: 0, - dial_status: DialStatus::UNUSED, - }, - HandleFail::DialRefused => Self { - status: ResponseStatus::E_DIAL_REFUSED, - addr_idx: 0, - dial_status: DialStatus::UNUSED, - }, - HandleFail::DialBack { idx, result } => Self { - status: ResponseStatus::OK, - addr_idx: idx, - dial_status: match result { - Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, - Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, - Ok(()) => DialStatus::OK, - }, - }, - } - } -} - -async fn handle_request( - stream: impl AsyncRead + AsyncWrite + Unpin, - observed_multiaddr: Multiaddr, - client: PeerId, - dial_back_cmd_sender: mpsc::Sender, - rng: impl RngCore, -) -> Event { - let mut coder = Coder::new(stream); - let mut all_addrs = Vec::new(); - let mut tested_addr_opt = None; - let mut data_amount = 0; - let response = handle_request_internal( - &mut coder, - observed_multiaddr.clone(), - dial_back_cmd_sender, - rng, - &mut all_addrs, - &mut tested_addr_opt, - &mut data_amount, - ) - .await - .unwrap_or_else(|e| e.into()); - let Some(tested_addr) = tested_addr_opt else { - return Event { - all_addrs, - tested_addr: observed_multiaddr, - client, - data_amount, - result: Err(io::Error::new( - io::ErrorKind::Other, - "client is not conformint to protocol. the tested address is not the observed address", - )), - }; - }; - if let Err(e) = coder.send(Response::Dial(response)).await { - return Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Err(e), - }; - } - if let Err(e) = coder.close().await { - return Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Err(e), - }; - } - Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - } -} - -async fn handle_request_internal( - coder: &mut Coder, - observed_multiaddr: Multiaddr, - dial_back_cmd_sender: mpsc::Sender, - mut rng: impl RngCore, - all_addrs: &mut Vec, - tested_addrs: &mut Option, - data_amount: &mut usize, -) -> Result -where - I: AsyncRead + AsyncWrite + Unpin, -{ - let DialRequest { mut addrs, nonce } = match coder - .next() - .await - .map_err(|_| HandleFail::InternalError(0))? - { - Request::Dial(dial_request) => dial_request, - Request::Data(_) => { - return Err(HandleFail::RequestRejected); - } - }; - *all_addrs = addrs.clone(); - let idx = 0; - let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; - *tested_addrs = Some(addr.clone()); - *data_amount = 0; - if addr != observed_multiaddr { - let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); - let mut rem_data = dial_data_request.num_bytes; - coder - .send(Response::Data(dial_data_request)) - .await - .map_err(|_| HandleFail::InternalError(idx))?; - while rem_data > 0 { - let data_count = match coder - .next() - .await - .map_err(|_e| HandleFail::InternalError(idx))? - { - Request::Dial(_) => { - return Err(HandleFail::RequestRejected); - } - Request::Data(dial_data_response) => dial_data_response.get_data_count(), - }; - rem_data = rem_data.saturating_sub(data_count); - *data_amount += data_count; - } - } - let (back_channel, rx) = oneshot::channel(); - let dial_back_cmd = DialBackCommand { - addr, - nonce, - back_channel, - }; - dial_back_cmd_sender - .clone() - .send(dial_back_cmd) - .await - .map_err(|_| HandleFail::DialBack { - idx, - result: Err(DialBackStatus::DialErr), - })?; - - let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; - if let Err(err) = dial_back { - return Err(HandleFail::DialBack { - idx, - result: Err(err), - }); - } - Ok(DialResponse { - status: ResponseStatus::OK, - addr_idx: idx, - dial_status: DialStatus::OK, - }) -} diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs deleted file mode 100644 index abd0c4bd8eb..00000000000 --- a/protocols/autonat/tests/autonatv2.rs +++ /dev/null @@ -1,568 +0,0 @@ -use libp2p_autonat::v2::client::{self, Config}; -use libp2p_autonat::v2::server; -use libp2p_core::transport::TransportError; -use libp2p_core::Multiaddr; -use libp2p_swarm::{ - DialError, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, Swarm, SwarmEvent, -}; -use libp2p_swarm_test::SwarmExt; -use rand_core::OsRng; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::oneshot; -use tracing_subscriber::EnvFilter; - -#[tokio::test] -async fn confirm_successful() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - let (mut alice, mut bob) = start_and_connect().await; - - let cor_server_peer = *alice.local_peer_id(); - let cor_client_peer = *bob.local_peer_id(); - let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); - let alice_bob_external_addrs = bob_external_addrs.clone(); - - let alice_task = async { - let _ = alice - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), - _ => None, - }) - .await; - - let (dialed_peer_id, dialed_connection_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - .. - } => peer_id.map(|peer_id| (peer_id, connection_id)), - _ => None, - }) - .await; - - assert_eq!(dialed_peer_id, cor_client_peer); - - let _ = alice - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - peer_id, - connection_id, - .. - } if peer_id == dialed_peer_id - && peer_id == cor_client_peer - && connection_id == dialed_connection_id => - { - Some(()) - } - _ => None, - }) - .await; - - let server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result, - } = alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { - Some(status_update) - } - _ => None, - }) - .await; - - assert_eq!(tested_addr, bob_external_addrs.first().cloned().unwrap()); - assert_eq!(data_amount, 0); - assert_eq!(client, cor_client_peer); - assert_eq!(&all_addrs[..], &bob_external_addrs[..]); - assert!(result.is_ok(), "Result: {result:?}"); - }; - - let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; - let incoming_conn_id = bob - .wait(|event| match event { - SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), - _ => None, - }) - .await; - - let _ = bob - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }) - .await; - - let client::Event { - tested_addr, - bytes_sent, - server, - result, - } = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { - Some(status_update) - } - _ => None, - }) - .await; - assert_eq!( - tested_addr, - alice_bob_external_addrs.first().cloned().unwrap() - ); - assert_eq!(bytes_sent, 0); - assert_eq!(server, cor_server_peer); - assert!(result.is_ok(), "Result is {result:?}"); - }; - - tokio::join!(alice_task, bob_task); -} - -#[tokio::test] -async fn dial_back_to_unsupported_protocol() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - let (mut alice, mut bob) = bootstrap().await; - - let alice_peer_id = *alice.local_peer_id(); - - let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); - let bob_test_addr = test_addr.clone(); - bob.behaviour_mut() - .autonat - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &test_addr }, - )); - - let (bob_done_tx, bob_done_rx) = oneshot::channel(); - - let alice_task = async { - let (alice_dialing_peer, alice_conn_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - } => peer_id.map(|e| (e, connection_id)), - _ => None, - }) - .await; - let mut outgoing_conn_error = alice - .wait(|event| match event { - SwarmEvent::OutgoingConnectionError { - connection_id, - peer_id: Some(peer_id), - error: DialError::Transport(transport_errs), - } if connection_id == alice_conn_id && alice_dialing_peer == peer_id => { - Some(transport_errs) - } - _ => None, - }) - .await; - if let Some((multiaddr, TransportError::MultiaddrNotSupported(not_supported_addr))) = - outgoing_conn_error.pop() - { - assert_eq!( - multiaddr, - test_addr.clone().with_p2p(alice_dialing_peer).unwrap() - ); - assert_eq!(not_supported_addr, multiaddr,); - } else { - panic!("Peers are empty"); - } - assert_eq!(outgoing_conn_error.len(), 0); - let data_amount = alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - })) if all_addrs == vec![test_addr.clone()] - && tested_addr == test_addr.clone() - && client == alice_dialing_peer => - { - Some(data_amount) - } - _ => None, - }) - .await; - - let handler = tokio::spawn(async move { - alice.loop_on_next().await; - }); - let _ = bob_done_rx.await; - handler.abort(); - data_amount - }; - - let bob_task = async { - let data_amount = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(bytes_sent), - _ => None, - }) - .await; - bob_done_tx.send(()).unwrap(); - data_amount - }; - let (alice_amount, bob_amount) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_amount, bob_amount); -} - -#[tokio::test] -async fn dial_back_to_non_libp2p() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - let (mut alice, mut bob) = bootstrap().await; - let alice_peer_id = *alice.local_peer_id(); - - for addr_str in ["/ip4/169.150.247.38/tcp/32", "/ip6/::1/tcp/1000"] { - let addr: Multiaddr = addr_str.parse().unwrap(); - let bob_addr = addr.clone(); - bob.behaviour_mut() - .autonat - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, - )); - - let alice_task = async { - let (alice_dialing_peer, alice_conn_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - } => peer_id.map(|p| (p, connection_id)), - _ => None, - }) - .await; - let mut outgoing_conn_error = alice - .wait(|event| match event { - SwarmEvent::OutgoingConnectionError { - connection_id, - peer_id: Some(peer_id), - error: DialError::Transport(peers), - } if connection_id == alice_conn_id && peer_id == alice_dialing_peer => { - Some(peers) - } - _ => None, - }) - .await; - - if let Some((multiaddr, TransportError::Other(o))) = outgoing_conn_error.pop() { - assert_eq!( - multiaddr, - addr.clone().with_p2p(alice_dialing_peer).unwrap() - ); - let error_string = o.to_string(); - assert!( - error_string.contains("Connection refused"), - "Correct error string: {error_string} for {addr_str}" - ); - } else { - panic!("No outgoing connection errors"); - } - - alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - })) if all_addrs == vec![addr.clone()] - && tested_addr == addr - && alice_dialing_peer == client => - { - Some(data_amount) - } - _ => None, - }) - .await - }; - let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), - _ => None, - }) - .await - }; - - let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_bytes_sent, bob_bytes_sent); - bob.behaviour_mut().autonat.validate_addr(&addr); - } -} - -#[tokio::test] -async fn dial_back_to_not_supporting() { - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()) - .try_init(); - - let (mut alice, mut bob) = bootstrap().await; - let alice_peer_id = *alice.local_peer_id(); - - let (bob_done_tx, bob_done_rx) = oneshot::channel(); - - let hannes = new_dummy().await; - let hannes_peer_id = *hannes.local_peer_id(); - let unreachable_address = hannes.external_addresses().next().unwrap().clone(); - let bob_unreachable_address = unreachable_address.clone(); - bob.behaviour_mut() - .autonat - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { - addr: &unreachable_address, - }, - )); - - let handler = tokio::spawn(async { hannes.loop_on_next().await }); - - let alice_task = async { - let (alice_dialing_peer, alice_conn_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - } => peer_id.map(|p| (p, connection_id)), - _ => None, - }) - .await; - alice - .wait(|event| match event { - SwarmEvent::OutgoingConnectionError { - connection_id, - peer_id: Some(peer_id), - error: DialError::WrongPeerId { obtained, .. }, - } if connection_id == alice_conn_id - && peer_id == alice_dialing_peer - && obtained == hannes_peer_id => - { - Some(()) - } - _ => None, - }) - .await; - - let data_amount = alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { - all_addrs, - tested_addr, - client, - data_amount, - result: Ok(()), - })) if all_addrs == vec![unreachable_address.clone()] - && tested_addr == unreachable_address - && alice_dialing_peer == client => - { - Some(data_amount) - } - _ => None, - }) - .await; - tokio::select! { - _ = bob_done_rx => { - data_amount - } - _ = alice.loop_on_next() => { - unreachable!(); - } - } - }; - - let bob_task = async { - let bytes_sent = bob - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { - tested_addr, - bytes_sent, - server, - result: Err(_), - })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { - Some(bytes_sent) - } - _ => None, - }) - .await; - bob_done_tx.send(()).unwrap(); - bytes_sent - }; - - let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); - assert_eq!(alice_bytes_sent, bob_bytes_sent); - handler.abort(); -} - -async fn new_server() -> Swarm { - let mut node = Swarm::new_ephemeral(|identity| CombinedServer { - autonat: libp2p_autonat::v2::server::Behaviour::default(), - identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( - "/libp2p-test/1.0.0".into(), - identity.public().clone(), - )), - }); - node.listen().with_tcp_addr_external().await; - - node -} - -async fn new_client() -> Swarm { - let mut node = Swarm::new_ephemeral(|identity| CombinedClient { - autonat: libp2p_autonat::v2::client::Behaviour::new( - OsRng, - Config::default().with_probe_interval(Duration::from_millis(100)), - ), - identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( - "/libp2p-test/1.0.0".into(), - identity.public().clone(), - )), - }); - node.listen().with_tcp_addr_external().await; - node -} - -#[derive(libp2p_swarm::NetworkBehaviour)] -#[behaviour(prelude = "libp2p_swarm::derive_prelude")] -struct CombinedServer { - autonat: libp2p_autonat::v2::server::Behaviour, - identify: libp2p_identify::Behaviour, -} - -#[derive(libp2p_swarm::NetworkBehaviour)] -#[behaviour(prelude = "libp2p_swarm::derive_prelude")] -struct CombinedClient { - autonat: libp2p_autonat::v2::client::Behaviour, - identify: libp2p_identify::Behaviour, -} - -async fn new_dummy() -> Swarm { - let mut node = Swarm::new_ephemeral(|identity| { - libp2p_identify::Behaviour::new(libp2p_identify::Config::new( - "/libp2p-test/1.0.0".into(), - identity.public().clone(), - )) - }); - node.listen().with_tcp_addr_external().await; - node -} - -async fn start_and_connect() -> (Swarm, Swarm) { - let mut alice = new_server().await; - let mut bob = new_client().await; - - bob.connect(&mut alice).await; - (alice, bob) -} - -async fn bootstrap() -> (Swarm, Swarm) { - let (mut alice, mut bob) = start_and_connect().await; - - let cor_server_peer = *alice.local_peer_id(); - let cor_client_peer = *bob.local_peer_id(); - - let alice_task = async { - let _ = alice - .wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), - _ => None, - }) - .await; - - let (dialed_peer_id, dialed_connection_id) = alice - .wait(|event| match event { - SwarmEvent::Dialing { - peer_id, - connection_id, - .. - } => peer_id.map(|peer_id| (peer_id, connection_id)), - _ => None, - }) - .await; - - let _ = alice - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - peer_id, - connection_id, - .. - } if peer_id == dialed_peer_id - && peer_id == cor_client_peer - && connection_id == dialed_connection_id => - { - Some(()) - } - _ => None, - }) - .await; - - alice - .wait(|event| match event { - SwarmEvent::Behaviour(CombinedServerEvent::Autonat(_)) => Some(()), - _ => None, - }) - .await; - }; - - let bob_task = async { - bob.wait(|event| match event { - SwarmEvent::NewExternalAddrCandidate { address } => Some(address), - _ => None, - }) - .await; - let incoming_conn_id = bob - .wait(|event| match event { - SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), - _ => None, - }) - .await; - - let _ = bob - .wait(|event| match event { - SwarmEvent::ConnectionEstablished { - connection_id, - peer_id, - .. - } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), - _ => None, - }) - .await; - - bob.wait(|event| match event { - SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), - _ => None, - }) - .await; - }; - - tokio::join!(alice_task, bob_task); - (alice, bob) -} diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 2b1460cac85..7509d3ef425 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -17,7 +17,6 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#![allow(deprecated)] use async_std::task::JoinHandle; use libp2p_autonat::{ diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index a01c5901b8e..fd97b1a9132 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -17,7 +17,6 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#![allow(deprecated)] use libp2p_autonat::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, From a1ae4e91b7d5f75418744fbd7117fd3a2f590078 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:16:13 +0200 Subject: [PATCH 126/179] Update protocols/autonat/src/v2/client/handler/dial_request.rs Co-authored-by: stormshield-ebzh <143415961+stormshield-ebzh@users.noreply.github.com> --- .../autonat/src/v2/client/handler/dial_request.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs index 5191382734f..9d2df8ee6b4 100644 --- a/protocols/autonat/src/v2/client/handler/dial_request.rs +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -254,7 +254,18 @@ async fn start_stream_handle( } Response::Dial(dial_response) => (dial_response, 0), }; - coder.close().await?; + match coder.close().await { + Ok(_) => {} + Err(err) => { + if err.kind() == io::ErrorKind::ConnectionReset { + // The AutoNAT server may have already closed the stream (this is normal because the probe is finished), in this case we have this error: + // Err(Custom { kind: ConnectionReset, error: Stopped(0) }) + // so we silently ignore this error + } else { + return Err(err.into()); + } + } + } match res.status { ResponseStatus::E_REQUEST_REJECTED => { From a84c9983b5f79017c1ab28bc2c41d52a9028af6f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:27:42 +0200 Subject: [PATCH 127/179] Format --- swarm/src/lib.rs | 31 +++++++++++++++++++------------ transports/tcp/src/lib.rs | 22 +++++++++++----------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index c20fa79ddcd..3c74e82f28e 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -597,7 +597,9 @@ where } self.behaviour - .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { listener_id })); + .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { + listener_id, + })); Ok(()) } @@ -608,7 +610,9 @@ where /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. pub fn add_external_address(&mut self, a: Multiaddr) { self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &a })); + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &a, + })); self.confirmed_external_addr.insert(a); } @@ -1055,9 +1059,9 @@ where ); let addrs = self.listened_addrs.remove(&listener_id).unwrap_or_default(); for addr in addrs.iter() { - self.behaviour.on_swarm_event( - FromSwarm::ExpiredListenAddr(ExpiredListenAddr { listener_id, addr }) - ); + self.behaviour.on_swarm_event(FromSwarm::ExpiredListenAddr( + ExpiredListenAddr { listener_id, addr }, + )); } self.behaviour .on_swarm_event(FromSwarm::ListenerClosed(ListenerClosed { @@ -2151,9 +2155,9 @@ mod tests { {} match swarm2.poll_next_unpin(cx) { - Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { peer_id, error, .. })) => { - Poll::Ready((peer_id, error)) - } + Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { + peer_id, error, .. + })) => Poll::Ready((peer_id, error)), Poll::Ready(x) => panic!("unexpected {x:?}"), Poll::Pending => Poll::Pending, } @@ -2219,7 +2223,9 @@ mod tests { return Poll::Ready(Ok(())); } } - Poll::Ready(Some(SwarmEvent::IncomingConnectionError { local_addr, .. })) => { + Poll::Ready(Some(SwarmEvent::IncomingConnectionError { + local_addr, .. + })) => { assert!(!got_inc_err); assert_eq!(local_addr, local_address); got_inc_err = true; @@ -2340,9 +2346,10 @@ mod tests { // This constitutes a fairly typical error for chained transports. let error = DialError::Transport(vec![( "/ip4/127.0.0.1/tcp/80".parse().unwrap(), - TransportError::Other( - io::Error::new(io::ErrorKind::Other, MemoryTransportError::Unreachable) - ), + TransportError::Other(io::Error::new( + io::ErrorKind::Other, + MemoryTransportError::Unreachable, + )), )]); let string = format!("{error}"); diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index cbe56e6904d..352a5b95db3 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -802,7 +802,9 @@ mod tests { .unwrap() ), Ok(SocketAddr::new( - IpAddr::V6(Ipv6Addr::new(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,)), + IpAddr::V6(Ipv6Addr::new( + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + )), 8080, )) ); @@ -1275,11 +1277,10 @@ mod tests { #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); assert!(rt.block_on(cycle_listeners::())); } } @@ -1312,11 +1313,10 @@ mod tests { } #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); rt.block_on(async { test::(); }); From 0dc500344f4d5d4dec8d6cf69bd66de0f26da919 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 May 2024 13:14:13 +0200 Subject: [PATCH 128/179] Resolve concern --- transports/tcp/src/lib.rs | 55 ++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 89ea859ce73..3e8db26af78 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -47,11 +47,11 @@ use libp2p_core::{ use provider::{Incoming, Provider}; use socket2::{Domain, Socket, Type}; use std::{ - collections::{HashSet, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, io, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpListener}, pin::Pin, - sync::{Arc, RwLock}, + sync::{Arc, Mutex, RwLock}, task::{Context, Poll, Waker}, time::Duration, }; @@ -76,7 +76,7 @@ struct PortReuse { /// registered as eligible for port reuse when dialing listen_addrs: Arc>>, /// Contains a hashset of all multiaddr that where dialed from a reused port. - addr_dialed_from_reuse_port: Arc>>, + addr_dialed_from_reuse_poralways_reused_port: Arc>>, } impl PortReuse { @@ -130,18 +130,25 @@ impl PortReuse { None } - fn dialed_from_reuse_port(&self, addr: Multiaddr) { - self.addr_dialed_from_reuse_port - .write() + fn dialed_from_reuse_port(&self, addr: SocketAddr) { + self.addr_dialed_from_reuse_poralways_reused_port + .lock() .expect("`dialed_as_listener` never panic while holding the lock") - .insert(addr); + .entry(addr) + .or_insert(true); } fn was_dialed_from_reuse_port(&self, addr: &Multiaddr) -> bool { - self.addr_dialed_from_reuse_port - .read() - .expect("`already_dialed_as_listener` never panic while holding the lock") - .contains(addr) + if let Ok(socket_addr) = multiaddr_to_socketaddr(addr.clone()) { + *self + .addr_dialed_from_reuse_poralways_reused_port + .lock() + .expect("`already_dialed_as_listener` never panic while holding the lock") + .entry(socket_addr) + .or_insert(false) + } else { + false + } } } @@ -387,7 +394,7 @@ where socket .bind(&socket_addr.into()) .map_err(TransportError::Other)?; - self.port_reuse.dialed_from_reuse_port(addr); + self.port_reuse.dialed_from_reuse_port(socket_addr); } _ => {} } @@ -433,7 +440,7 @@ where if !is_tcp_addr(listen) || !is_tcp_addr(observed) { return None; } - if self.port_reuse.was_dialed_from_reuse_port(observed) { + if self.port_reuse.was_dialed_from_reuse_port(listen) { return Some(observed.clone()); } @@ -798,7 +805,9 @@ mod tests { .unwrap() ), Ok(SocketAddr::new( - IpAddr::V6(Ipv6Addr::new(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,)), + IpAddr::V6(Ipv6Addr::new( + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + )), 8080, )) ); @@ -1271,11 +1280,10 @@ mod tests { #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); assert!(rt.block_on(cycle_listeners::())); } } @@ -1308,11 +1316,10 @@ mod tests { } #[cfg(feature = "tokio")] { - let rt = - ::tokio::runtime::Builder::new_current_thread() - .enable_io() - .build() - .unwrap(); + let rt = ::tokio::runtime::Builder::new_current_thread() + .enable_io() + .build() + .unwrap(); rt.block_on(async { test::(); }); From 0918ea84f48b462f041fc76b2bdf68232e30a7ad Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 May 2024 13:17:39 +0200 Subject: [PATCH 129/179] Remove deprecated --- misc/server/src/behaviour.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index c5bcb7e9d9a..36b18c9798d 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -1,5 +1,3 @@ -#![allow(deprecated)] - use libp2p::autonat; use libp2p::identify; use libp2p::kad; From 45863c3ab503c716ea03bc5eb563ce923b29fdec Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 8 May 2024 13:23:20 +0200 Subject: [PATCH 130/179] Formatting --- protocols/autonat/src/behaviour.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index bf36080a721..a1c0fe968f0 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -517,8 +517,13 @@ impl NetworkBehaviour for Behaviour { role_override: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { - self.inner - .handle_established_outbound_connection(connection_id, peer, addr, role_override, port_use) + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) } fn on_swarm_event(&mut self, event: FromSwarm) { From 86650bd7e48d87c71fd111cc614399431c2cf21a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 14 May 2024 21:38:08 +0200 Subject: [PATCH 131/179] Don't close stream --- protocols/autonat/src/v2/protocol.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index d0e2fc30ca6..47d38ed541b 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -299,7 +299,6 @@ pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::R .send(msg) .await .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - framed.close().await?; Ok(()) } @@ -315,7 +314,7 @@ pub(crate) async fn recv_dial_back_response( .next() .await .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - framed.close().await?; + if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { Ok(()) } else { From e41f667bfdf1f2508889c3ec26408a2774a3a7e1 Mon Sep 17 00:00:00 2001 From: Eric Bourneuf Date: Thu, 16 May 2024 16:10:17 +0200 Subject: [PATCH 132/179] identify detects if address translation is required --- protocols/identify/src/behaviour.rs | 41 +++++++++++--- swarm/src/behaviour.rs | 29 +++++++++- swarm/src/lib.rs | 86 ++++++++++++++++++++--------- transports/tcp/src/lib.rs | 31 +---------- 4 files changed, 120 insertions(+), 67 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index ac58ec311fc..653f98603e3 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -24,7 +24,10 @@ use libp2p_core::transport::PortUse; use libp2p_core::{multiaddr, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; -use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; +use libp2p_swarm::behaviour::{ + ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm, + NewExternalAddrCandidateEndpoint, +}; use libp2p_swarm::{ ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, @@ -53,6 +56,9 @@ pub struct Behaviour { /// The address a remote observed for us. our_observed_addresses: HashMap, + /// Established connections information (Listener / Dialer / port_reuse or not) + connections_endpoints: HashMap, + /// Pending events to be emitted when polled. events: VecDeque>, /// The addresses of all peers that we have discovered. @@ -154,6 +160,7 @@ impl Behaviour { config, connected: HashMap::new(), our_observed_addresses: Default::default(), + connections_endpoints: Default::default(), events: VecDeque::new(), discovered_peers, listen_addresses: Default::default(), @@ -222,11 +229,14 @@ impl NetworkBehaviour for Behaviour { fn handle_established_inbound_connection( &mut self, - _: ConnectionId, + connection_id: ConnectionId, peer: PeerId, _: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { + self.connections_endpoints + .insert(connection_id, NewExternalAddrCandidateEndpoint::Listener); + Ok(Handler::new( self.config.interval, peer, @@ -240,12 +250,17 @@ impl NetworkBehaviour for Behaviour { fn handle_established_outbound_connection( &mut self, - _: ConnectionId, + connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, _: Endpoint, - _: PortUse, + port_use: PortUse, ) -> Result, ConnectionDenied> { + self.connections_endpoints.insert( + connection_id, + NewExternalAddrCandidateEndpoint::Dialer { port_use }, + ); + Ok(Handler::new( self.config.interval, peer, @@ -287,11 +302,18 @@ impl NetworkBehaviour for Behaviour { } } + let endpoint = self + .connections_endpoints + .get(&id) + .expect("we are connected"); + match self.our_observed_addresses.entry(id) { Entry::Vacant(not_yet_observed) => { not_yet_observed.insert(observed.clone()); - self.events - .push_back(ToSwarm::NewExternalAddrCandidate(observed)); + self.events.push_back(ToSwarm::NewExternalAddrCandidate { + endpoint: *endpoint, + observed_addr: observed, + }); } Entry::Occupied(already_observed) if already_observed.get() == &observed => { // No-op, we already observed this address. @@ -304,8 +326,10 @@ impl NetworkBehaviour for Behaviour { ); *already_observed.get_mut() = observed.clone(); - self.events - .push_back(ToSwarm::NewExternalAddrCandidate(observed)); + self.events.push_back(ToSwarm::NewExternalAddrCandidate { + endpoint: *endpoint, + observed_addr: observed, + }); } } } @@ -396,6 +420,7 @@ impl NetworkBehaviour for Behaviour { } self.our_observed_addresses.remove(&connection_id); + self.connections_endpoints.remove(&connection_id); } FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => { if let (Some(peer_id), Some(cache), DialError::Transport(errors)) = diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index b7045c0ec3a..e1c135e3525 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -219,6 +219,14 @@ pub trait NetworkBehaviour: 'static { -> Poll>>; } +#[derive(Debug, Copy, Clone)] +pub enum NewExternalAddrCandidateEndpoint { + /// no translation is required + Listener, + /// translation is required when dialed with PortUse::New + Dialer { port_use: PortUse }, +} + /// A command issued from a [`NetworkBehaviour`] for the [`Swarm`]. /// /// [`Swarm`]: super::Swarm @@ -282,7 +290,10 @@ pub enum ToSwarm { /// - A protocol such as identify obtained it from a remote. /// - The user provided it based on configuration. /// - We made an educated guess based on one of our listen addresses. - NewExternalAddrCandidate(Multiaddr), + NewExternalAddrCandidate { + endpoint: NewExternalAddrCandidateEndpoint, + observed_addr: Multiaddr, + }, /// Indicates to the [`Swarm`](crate::Swarm) that the provided address is confirmed to be externally reachable. /// @@ -341,7 +352,13 @@ impl ToSwarm { peer_id, connection, }, - ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), + ToSwarm::NewExternalAddrCandidate { + endpoint, + observed_addr: addr, + } => ToSwarm::NewExternalAddrCandidate { + endpoint, + observed_addr: addr, + }, ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), ToSwarm::NewExternalAddrOfPeer { @@ -372,7 +389,13 @@ impl ToSwarm { handler, event, }, - ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), + ToSwarm::NewExternalAddrCandidate { + endpoint, + observed_addr: addr, + } => ToSwarm::NewExternalAddrCandidate { + endpoint, + observed_addr: addr, + }, ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), ToSwarm::CloseConnection { diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 3c74e82f28e..2b87371b2d2 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -106,6 +106,7 @@ pub mod derive_prelude { pub use libp2p_identity::PeerId; } +use behaviour::NewExternalAddrCandidateEndpoint; pub use behaviour::{ AddressChange, CloseConnection, ConnectionClosed, DialFailure, ExpiredListenAddr, ExternalAddrExpired, ExternalAddresses, FromSwarm, ListenAddresses, ListenFailure, @@ -119,6 +120,7 @@ pub use handler::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerSelect, OneShotHandler, OneShotHandlerConfig, StreamUpgradeError, SubstreamProtocol, }; +use libp2p_core::transport::PortUse; #[cfg(feature = "macros")] pub use libp2p_swarm_derive::NetworkBehaviour; pub use listen_opts::ListenOpts; @@ -1132,39 +1134,69 @@ where self.pending_handler_event = Some((peer_id, handler, event)); } - ToSwarm::NewExternalAddrCandidate(addr) => { + ToSwarm::NewExternalAddrCandidate { + endpoint, + observed_addr, + } => { // Apply address translation to the candidate address. // For TCP without port-reuse, the observed address contains an ephemeral port which needs to be replaced by the port of a listen address. - let translated_addresses = { - let mut addrs: Vec<_> = self - .listened_addrs - .values() - .flatten() - .filter_map(|server| self.transport.address_translation(server, &addr)) - .collect(); - - // remove duplicates - addrs.sort_unstable(); - addrs.dedup(); - addrs - }; - - // If address translation yielded nothing, broadcast the original candidate address. - if translated_addresses.is_empty() { - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, - )); - self.pending_swarm_events - .push_back(SwarmEvent::NewExternalAddrCandidate { address: addr }); - } else { - for addr in translated_addresses { + match endpoint { + NewExternalAddrCandidateEndpoint::Listener + | NewExternalAddrCandidateEndpoint::Dialer { + port_use: PortUse::Reuse, + } => { + // no translation is required : use the observed address self.behaviour .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, + NewExternalAddrCandidate { + addr: &observed_addr, + }, )); self.pending_swarm_events - .push_back(SwarmEvent::NewExternalAddrCandidate { address: addr }); + .push_back(SwarmEvent::NewExternalAddrCandidate { + address: observed_addr, + }); + } + NewExternalAddrCandidateEndpoint::Dialer { + port_use: PortUse::New, + } => { + let mut translated_addresses: Vec<_> = self + .listened_addrs + .values() + .flatten() + .filter_map(|server| { + self.transport.address_translation(server, &observed_addr) + }) + .collect(); + + // remove duplicates + translated_addresses.sort_unstable(); + translated_addresses.dedup(); + + // If address translation yielded nothing, broadcast the original candidate address. + if translated_addresses.is_empty() { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { + addr: &observed_addr, + }, + )); + self.pending_swarm_events.push_back( + SwarmEvent::NewExternalAddrCandidate { + address: observed_addr, + }, + ); + } else { + for addr in translated_addresses { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + self.pending_swarm_events.push_back( + SwarmEvent::NewExternalAddrCandidate { address: addr }, + ); + } + } } } } diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index d01d83157aa..088de025bda 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -47,11 +47,11 @@ use libp2p_core::{ use provider::{Incoming, Provider}; use socket2::{Domain, Socket, Type}; use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashSet, VecDeque}, io, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpListener}, pin::Pin, - sync::{Arc, Mutex, RwLock}, + sync::{Arc, RwLock}, task::{Context, Poll, Waker}, time::Duration, }; @@ -75,8 +75,6 @@ struct PortReuse { /// The addresses and ports of the listening sockets /// registered as eligible for port reuse when dialing listen_addrs: Arc>>, - /// Contains a hashset of all multiaddr that where dialed from a reused port. - addr_dialed_from_reuse_poralways_reused_port: Arc>>, } impl PortReuse { @@ -129,27 +127,6 @@ impl PortReuse { None } - - fn dialed_from_reuse_port(&self, addr: SocketAddr) { - self.addr_dialed_from_reuse_poralways_reused_port - .lock() - .expect("`dialed_as_listener` never panic while holding the lock") - .entry(addr) - .or_insert(true); - } - - fn was_dialed_from_reuse_port(&self, addr: &Multiaddr) -> bool { - if let Ok(socket_addr) = multiaddr_to_socketaddr(addr.clone()) { - *self - .addr_dialed_from_reuse_poralways_reused_port - .lock() - .expect("`already_dialed_as_listener` never panic while holding the lock") - .entry(socket_addr) - .or_insert(false) - } else { - false - } - } } impl Config { @@ -398,7 +375,6 @@ where socket .bind(&socket_addr.into()) .map_err(TransportError::Other)?; - self.port_reuse.dialed_from_reuse_port(socket_addr); } _ => {} } @@ -444,9 +420,6 @@ where if !is_tcp_addr(listen) || !is_tcp_addr(observed) { return None; } - if self.port_reuse.was_dialed_from_reuse_port(listen) { - return Some(observed.clone()); - } address_translation(listen, observed) } From 235447bbc2e6aaf5b16b030909745f236ee2f1b3 Mon Sep 17 00:00:00 2001 From: Eric Bourneuf Date: Thu, 16 May 2024 19:04:22 +0200 Subject: [PATCH 133/179] homogeneize fully-p2p-qualified addresses to avoid double probe --- protocols/identify/src/behaviour.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 653f98603e3..7c8e0269461 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -256,6 +256,14 @@ impl NetworkBehaviour for Behaviour { _: Endpoint, port_use: PortUse, ) -> Result, ConnectionDenied> { + // Contrary to inbound events, outbound events are full-p2p qualified + // so we remove /p2p/ in order to be homogeneous + // this will avoid Autonatv2 to probe twice the same address (fully-p2p-qualified + not fully-p2p-qualified) + let mut addr = addr.clone(); + if matches!(addr.iter().last(), Some(multiaddr::Protocol::P2p(_))) { + addr.pop(); + } + self.connections_endpoints.insert( connection_id, NewExternalAddrCandidateEndpoint::Dialer { port_use }, From 6103cd8a263e6844609d7e8091bfe908933e94e1 Mon Sep 17 00:00:00 2001 From: Eric Bourneuf Date: Wed, 22 May 2024 10:44:15 +0200 Subject: [PATCH 134/179] feat(nat): get rid of Transport::address_translation --- protocols/identify/src/behaviour.rs | 140 ++++++++++++++++++++++------ swarm/src/behaviour.rs | 29 +----- swarm/src/lib.rs | 74 ++------------- 3 files changed, 120 insertions(+), 123 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 7c8e0269461..e3a0c677f48 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -20,14 +20,12 @@ use crate::handler::{self, Handler, InEvent}; use crate::protocol::{Info, UpgradeError}; +use libp2p_core::multiaddr::Protocol; use libp2p_core::transport::PortUse; -use libp2p_core::{multiaddr, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{address_translation, multiaddr, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; -use libp2p_swarm::behaviour::{ - ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm, - NewExternalAddrCandidateEndpoint, -}; +use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p_swarm::{ ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, @@ -43,6 +41,50 @@ use std::{ time::Duration, }; +/// Whether an [`Multiaddr`] is a valid for the QUIC transport. +fn is_quic_addr(addr: &Multiaddr, v1: bool) -> bool { + use Protocol::*; + let mut iter = addr.iter(); + let Some(first) = iter.next() else { + return false; + }; + let Some(second) = iter.next() else { + return false; + }; + let Some(third) = iter.next() else { + return false; + }; + let fourth = iter.next(); + let fifth = iter.next(); + + matches!(first, Ip4(_) | Ip6(_) | Dns(_) | Dns4(_) | Dns6(_)) + && matches!(second, Udp(_)) + && if v1 { + matches!(third, QuicV1) + } else { + matches!(third, Quic) + } + && matches!(fourth, Some(P2p(_)) | None) + && fifth.is_none() +} + +fn is_tcp_addr(addr: &Multiaddr) -> bool { + use Protocol::*; + + let mut iter = addr.iter(); + + let first = match iter.next() { + None => return false, + Some(p) => p, + }; + let second = match iter.next() { + None => return false, + Some(p) => p, + }; + + matches!(first, Ip4(_) | Ip6(_) | Dns(_) | Dns4(_) | Dns6(_)) && matches!(second, Tcp(_)) +} + /// Network behaviour that automatically identifies nodes periodically, returns information /// about them, and answers identify queries from other nodes. /// @@ -56,8 +98,8 @@ pub struct Behaviour { /// The address a remote observed for us. our_observed_addresses: HashMap, - /// Established connections information (Listener / Dialer / port_reuse or not) - connections_endpoints: HashMap, + /// The outbound connections established without port reuse (require translation) + outbound_connections_without_port_reuse: HashSet, /// Pending events to be emitted when polled. events: VecDeque>, @@ -160,7 +202,7 @@ impl Behaviour { config, connected: HashMap::new(), our_observed_addresses: Default::default(), - connections_endpoints: Default::default(), + outbound_connections_without_port_reuse: Default::default(), events: VecDeque::new(), discovered_peers, listen_addresses: Default::default(), @@ -221,6 +263,57 @@ impl Behaviour { .cloned() .collect() } + + fn emit_new_external_addr_candidate_event( + &mut self, + connection_id: ConnectionId, + observed: &Multiaddr, + ) { + if self + .outbound_connections_without_port_reuse + .contains(&connection_id) + { + // Apply address translation to the candidate address. + // For TCP without port-reuse, the observed address contains an ephemeral port which needs to be replaced by the port of a listen address. + let translated_addresses = { + let mut addrs: Vec<_> = self + .listen_addresses + .iter() + .filter_map(|server| { + if (is_tcp_addr(server) && is_tcp_addr(&observed)) + || (is_quic_addr(server, true) && is_quic_addr(&observed, true)) + || (is_quic_addr(server, false) && is_quic_addr(&observed, false)) + { + address_translation(server, &observed) + } else { + None + } + }) + .collect(); + + // remove duplicates + addrs.sort_unstable(); + addrs.dedup(); + addrs + }; + + // If address translation yielded nothing, broadcast the original candidate address. + if translated_addresses.is_empty() { + self.events + .push_back(ToSwarm::NewExternalAddrCandidate(observed.clone())); + } else { + for addr in translated_addresses { + self.events + .push_back(ToSwarm::NewExternalAddrCandidate(addr)); + } + } + } else { + // outgoing connection dialed with port reuse + // incomming connection + self.events + .push_back(ToSwarm::NewExternalAddrCandidate(observed.clone())); + } + } } impl NetworkBehaviour for Behaviour { @@ -229,14 +322,11 @@ impl NetworkBehaviour for Behaviour { fn handle_established_inbound_connection( &mut self, - connection_id: ConnectionId, + _: ConnectionId, peer: PeerId, _: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { - self.connections_endpoints - .insert(connection_id, NewExternalAddrCandidateEndpoint::Listener); - Ok(Handler::new( self.config.interval, peer, @@ -264,10 +354,10 @@ impl NetworkBehaviour for Behaviour { addr.pop(); } - self.connections_endpoints.insert( - connection_id, - NewExternalAddrCandidateEndpoint::Dialer { port_use }, - ); + if port_use == PortUse::New { + self.outbound_connections_without_port_reuse + .insert(connection_id); + } Ok(Handler::new( self.config.interval, @@ -310,18 +400,10 @@ impl NetworkBehaviour for Behaviour { } } - let endpoint = self - .connections_endpoints - .get(&id) - .expect("we are connected"); - match self.our_observed_addresses.entry(id) { Entry::Vacant(not_yet_observed) => { not_yet_observed.insert(observed.clone()); - self.events.push_back(ToSwarm::NewExternalAddrCandidate { - endpoint: *endpoint, - observed_addr: observed, - }); + self.emit_new_external_addr_candidate_event(id, &observed); } Entry::Occupied(already_observed) if already_observed.get() == &observed => { // No-op, we already observed this address. @@ -334,10 +416,7 @@ impl NetworkBehaviour for Behaviour { ); *already_observed.get_mut() = observed.clone(); - self.events.push_back(ToSwarm::NewExternalAddrCandidate { - endpoint: *endpoint, - observed_addr: observed, - }); + self.emit_new_external_addr_candidate_event(id, &observed); } } } @@ -428,7 +507,8 @@ impl NetworkBehaviour for Behaviour { } self.our_observed_addresses.remove(&connection_id); - self.connections_endpoints.remove(&connection_id); + self.outbound_connections_without_port_reuse + .remove(&connection_id); } FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => { if let (Some(peer_id), Some(cache), DialError::Transport(errors)) = diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index e1c135e3525..b7045c0ec3a 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -219,14 +219,6 @@ pub trait NetworkBehaviour: 'static { -> Poll>>; } -#[derive(Debug, Copy, Clone)] -pub enum NewExternalAddrCandidateEndpoint { - /// no translation is required - Listener, - /// translation is required when dialed with PortUse::New - Dialer { port_use: PortUse }, -} - /// A command issued from a [`NetworkBehaviour`] for the [`Swarm`]. /// /// [`Swarm`]: super::Swarm @@ -290,10 +282,7 @@ pub enum ToSwarm { /// - A protocol such as identify obtained it from a remote. /// - The user provided it based on configuration. /// - We made an educated guess based on one of our listen addresses. - NewExternalAddrCandidate { - endpoint: NewExternalAddrCandidateEndpoint, - observed_addr: Multiaddr, - }, + NewExternalAddrCandidate(Multiaddr), /// Indicates to the [`Swarm`](crate::Swarm) that the provided address is confirmed to be externally reachable. /// @@ -352,13 +341,7 @@ impl ToSwarm { peer_id, connection, }, - ToSwarm::NewExternalAddrCandidate { - endpoint, - observed_addr: addr, - } => ToSwarm::NewExternalAddrCandidate { - endpoint, - observed_addr: addr, - }, + ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), ToSwarm::NewExternalAddrOfPeer { @@ -389,13 +372,7 @@ impl ToSwarm { handler, event, }, - ToSwarm::NewExternalAddrCandidate { - endpoint, - observed_addr: addr, - } => ToSwarm::NewExternalAddrCandidate { - endpoint, - observed_addr: addr, - }, + ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), ToSwarm::CloseConnection { diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 2b87371b2d2..c528e8d586f 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -106,7 +106,6 @@ pub mod derive_prelude { pub use libp2p_identity::PeerId; } -use behaviour::NewExternalAddrCandidateEndpoint; pub use behaviour::{ AddressChange, CloseConnection, ConnectionClosed, DialFailure, ExpiredListenAddr, ExternalAddrExpired, ExternalAddresses, FromSwarm, ListenAddresses, ListenFailure, @@ -120,7 +119,6 @@ pub use handler::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerSelect, OneShotHandler, OneShotHandlerConfig, StreamUpgradeError, SubstreamProtocol, }; -use libp2p_core::transport::PortUse; #[cfg(feature = "macros")] pub use libp2p_swarm_derive::NetworkBehaviour; pub use listen_opts::ListenOpts; @@ -1134,71 +1132,13 @@ where self.pending_handler_event = Some((peer_id, handler, event)); } - ToSwarm::NewExternalAddrCandidate { - endpoint, - observed_addr, - } => { - // Apply address translation to the candidate address. - // For TCP without port-reuse, the observed address contains an ephemeral port which needs to be replaced by the port of a listen address. - match endpoint { - NewExternalAddrCandidateEndpoint::Listener - | NewExternalAddrCandidateEndpoint::Dialer { - port_use: PortUse::Reuse, - } => { - // no translation is required : use the observed address - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { - addr: &observed_addr, - }, - )); - self.pending_swarm_events - .push_back(SwarmEvent::NewExternalAddrCandidate { - address: observed_addr, - }); - } - NewExternalAddrCandidateEndpoint::Dialer { - port_use: PortUse::New, - } => { - let mut translated_addresses: Vec<_> = self - .listened_addrs - .values() - .flatten() - .filter_map(|server| { - self.transport.address_translation(server, &observed_addr) - }) - .collect(); - - // remove duplicates - translated_addresses.sort_unstable(); - translated_addresses.dedup(); - - // If address translation yielded nothing, broadcast the original candidate address. - if translated_addresses.is_empty() { - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { - addr: &observed_addr, - }, - )); - self.pending_swarm_events.push_back( - SwarmEvent::NewExternalAddrCandidate { - address: observed_addr, - }, - ); - } else { - for addr in translated_addresses { - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, - )); - self.pending_swarm_events.push_back( - SwarmEvent::NewExternalAddrCandidate { address: addr }, - ); - } - } - } - } + ToSwarm::NewExternalAddrCandidate(addr) => { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + self.pending_swarm_events + .push_back(SwarmEvent::NewExternalAddrCandidate { address: addr }); } ToSwarm::ExternalAddrConfirmed(addr) => { self.add_external_address(addr.clone()); From 4b16e4291255fc1601588252ba65b7edc59f78d9 Mon Sep 17 00:00:00 2001 From: Eric Bourneuf Date: Thu, 23 May 2024 18:00:47 +0200 Subject: [PATCH 135/179] Thomas remarks --- protocols/identify/src/behaviour.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index e3a0c677f48..d42032badf4 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -99,7 +99,7 @@ pub struct Behaviour { our_observed_addresses: HashMap, /// The outbound connections established without port reuse (require translation) - outbound_connections_without_port_reuse: HashSet, + outbound_connections_with_ephemeral_port: HashSet, /// Pending events to be emitted when polled. events: VecDeque>, @@ -202,7 +202,7 @@ impl Behaviour { config, connected: HashMap::new(), our_observed_addresses: Default::default(), - outbound_connections_without_port_reuse: Default::default(), + outbound_connections_with_ephemeral_port: Default::default(), events: VecDeque::new(), discovered_peers, listen_addresses: Default::default(), @@ -270,7 +270,7 @@ impl Behaviour { observed: &Multiaddr, ) { if self - .outbound_connections_without_port_reuse + .outbound_connections_with_ephemeral_port .contains(&connection_id) { // Apply address translation to the candidate address. @@ -307,12 +307,13 @@ impl Behaviour { .push_back(ToSwarm::NewExternalAddrCandidate(addr)); } } - } else { - // outgoing connection dialed with port reuse - // incomming connection - self.events - .push_back(ToSwarm::NewExternalAddrCandidate(observed.clone())); + return; } + + // outgoing connection dialed with port reuse + // incomming connection + self.events + .push_back(ToSwarm::NewExternalAddrCandidate(observed.clone())); } } @@ -355,7 +356,7 @@ impl NetworkBehaviour for Behaviour { } if port_use == PortUse::New { - self.outbound_connections_without_port_reuse + self.outbound_connections_with_ephemeral_port .insert(connection_id); } @@ -507,7 +508,7 @@ impl NetworkBehaviour for Behaviour { } self.our_observed_addresses.remove(&connection_id); - self.outbound_connections_without_port_reuse + self.outbound_connections_with_ephemeral_port .remove(&connection_id); } FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => { From 0f538f316e06acf289b4b95ae3a5c512bc62b944 Mon Sep 17 00:00:00 2001 From: Eric Bourneuf Date: Thu, 23 May 2024 18:27:46 +0200 Subject: [PATCH 136/179] removed Transport::address_translation and moved generic address_translation to libp2p_swarm --- core/src/either.rs | 7 -- core/src/lib.rs | 3 - core/src/transport.rs | 18 ----- core/src/transport/and_then.rs | 4 - core/src/transport/boxed.rs | 9 --- core/src/transport/choice.rs | 8 -- core/src/transport/dummy.rs | 4 - core/src/transport/global_only.rs | 4 - core/src/transport/map.rs | 4 - core/src/transport/map_err.rs | 4 - core/src/transport/memory.rs | 4 - core/src/transport/optional.rs | 8 -- core/src/transport/timeout.rs | 4 - core/src/transport/upgrade.rs | 8 -- misc/metrics/src/bandwidth.rs | 4 - protocols/identify/src/behaviour.rs | 6 +- protocols/mdns/src/behaviour/iface/query.rs | 7 +- protocols/relay/src/priv_client/transport.rs | 4 - swarm/src/lib.rs | 2 + {core => swarm}/src/translation.rs | 2 +- transports/dns/src/lib.rs | 8 -- transports/quic/src/transport.rs | 36 --------- transports/quic/tests/smoke.rs | 8 -- transports/tcp/src/lib.rs | 80 ------------------- transports/uds/src/lib.rs | 8 -- transports/webrtc-websys/src/transport.rs | 4 - transports/webrtc/src/tokio/transport.rs | 4 - transports/websocket-websys/src/lib.rs | 4 - transports/websocket/src/framed.rs | 4 - transports/websocket/src/lib.rs | 4 - .../webtransport-websys/src/transport.rs | 4 - 31 files changed, 9 insertions(+), 269 deletions(-) rename {core => swarm}/src/translation.rs (98%) diff --git a/core/src/either.rs b/core/src/either.rs index 590dcd66595..2593174290c 100644 --- a/core/src/either.rs +++ b/core/src/either.rs @@ -192,11 +192,4 @@ where }, } } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - match self { - Either::Left(a) => a.address_translation(server, observed), - Either::Right(b) => b.address_translation(server, observed), - } - } } diff --git a/core/src/lib.rs b/core/src/lib.rs index abb83481d6c..cf48ee25329 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -45,8 +45,6 @@ mod proto { pub use multiaddr; pub type Negotiated = multistream_select::Negotiated; -mod translation; - pub mod connection; pub mod either; pub mod muxing; @@ -61,7 +59,6 @@ pub use multihash; pub use muxing::StreamMuxer; pub use peer_record::PeerRecord; pub use signed_envelope::SignedEnvelope; -pub use translation::address_translation; pub use transport::Transport; pub use upgrade::{InboundUpgrade, OutboundUpgrade, UpgradeInfo}; diff --git a/core/src/transport.rs b/core/src/transport.rs index 82aab3fda58..5202c3e0510 100644 --- a/core/src/transport.rs +++ b/core/src/transport.rs @@ -175,24 +175,6 @@ pub trait Transport { cx: &mut Context<'_>, ) -> Poll>; - /// Performs a transport-specific mapping of an address `observed` by a remote onto a - /// local `listen` address to yield an address for the local node that may be reachable - /// for other peers. - /// - /// This is relevant for transports where Network Address Translation (NAT) can occur - /// so that e.g. the peer is observed at a different IP than the IP of the local - /// listening address. See also [`address_translation`][crate::address_translation]. - /// - /// Within [`libp2p::Swarm`]() this is - /// used when extending the listening addresses of the local peer with external addresses - /// observed by remote peers. - /// On transports where this is not relevant (i.e. no NATs are present) `None` should be - /// returned for the sake of de-duplication. - /// - /// Note: if the listen or observed address is not a valid address of this transport, - /// `None` should be returned as well. - fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option; - /// Boxes the transport, including custom transport errors. fn boxed(self) -> boxed::Boxed where diff --git a/core/src/transport/and_then.rs b/core/src/transport/and_then.rs index fb6ed2949a4..e85703f77fb 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -92,10 +92,6 @@ where Ok(future) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index 7b7262c977d..596ab262221 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -63,7 +63,6 @@ trait Abstract { addr: Multiaddr, opts: DialOpts, ) -> Result, TransportError>; - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option; fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -100,10 +99,6 @@ where Ok(Box::pin(fut) as Dial<_>) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - Transport::address_translation(self, server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -151,10 +146,6 @@ impl Transport for Boxed { self.inner.dial(addr, opts) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.address_translation(server, observed) - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index 097dcd7de2f..4339f6bba71 100644 --- a/core/src/transport/choice.rs +++ b/core/src/transport/choice.rs @@ -140,14 +140,6 @@ where Err(TransportError::MultiaddrNotSupported(addr)) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - if let Some(addr) = self.0.address_translation(server, observed) { - Some(addr) - } else { - self.1.address_translation(server, observed) - } - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/dummy.rs b/core/src/transport/dummy.rs index a8b1e7e1a90..72558d34a79 100644 --- a/core/src/transport/dummy.rs +++ b/core/src/transport/dummy.rs @@ -79,10 +79,6 @@ impl Transport for DummyTransport { Err(TransportError::MultiaddrNotSupported(addr)) } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } - fn poll( self: Pin<&mut Self>, _: &mut Context<'_>, diff --git a/core/src/transport/global_only.rs b/core/src/transport/global_only.rs index d975070ea0f..83774f37004 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -314,10 +314,6 @@ impl crate::Transport for Transport { } } - fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.address_translation(listen, observed) - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/map.rs b/core/src/transport/map.rs index a7505d36e6c..9aab84ba8b1 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -91,10 +91,6 @@ where }) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/map_err.rs b/core/src/transport/map_err.rs index 20580c23537..5d44af9af2e 100644 --- a/core/src/transport/map_err.rs +++ b/core/src/transport/map_err.rs @@ -80,10 +80,6 @@ where } } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/memory.rs b/core/src/transport/memory.rs index 72511243e58..85680265e8b 100644 --- a/core/src/transport/memory.rs +++ b/core/src/transport/memory.rs @@ -226,10 +226,6 @@ impl Transport for MemoryTransport { DialFuture::new(port).ok_or(TransportError::Other(MemoryTransportError::Unreachable)) } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/optional.rs b/core/src/transport/optional.rs index f381d98f163..f18bfa441b0 100644 --- a/core/src/transport/optional.rs +++ b/core/src/transport/optional.rs @@ -92,14 +92,6 @@ where } } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - if let Some(inner) = &self.0 { - inner.address_translation(server, observed) - } else { - None - } - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/timeout.rs b/core/src/transport/timeout.rs index 1ed4f8a5796..830ed099629 100644 --- a/core/src/transport/timeout.rs +++ b/core/src/transport/timeout.rs @@ -115,10 +115,6 @@ where }) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/core/src/transport/upgrade.rs b/core/src/transport/upgrade.rs index 2a90172e710..66b9e7509af 100644 --- a/core/src/transport/upgrade.rs +++ b/core/src/transport/upgrade.rs @@ -356,10 +356,6 @@ where self.0.listen_on(id, addr) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.0.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -431,10 +427,6 @@ where .map_err(|err| err.map(TransportUpgradeError::Transport)) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/misc/metrics/src/bandwidth.rs b/misc/metrics/src/bandwidth.rs index 366283130d5..8a0f54e5b65 100644 --- a/misc/metrics/src/bandwidth.rs +++ b/misc/metrics/src/bandwidth.rs @@ -98,10 +98,6 @@ where }))) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index d42032badf4..5af8ce23ab7 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -22,13 +22,13 @@ use crate::handler::{self, Handler, InEvent}; use crate::protocol::{Info, UpgradeError}; use libp2p_core::multiaddr::Protocol; use libp2p_core::transport::PortUse; -use libp2p_core::{address_translation, multiaddr, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{multiaddr, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p_swarm::{ - ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, - NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, + address_translation, ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, + NetworkBehaviour, NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, }; use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent}; diff --git a/protocols/mdns/src/behaviour/iface/query.rs b/protocols/mdns/src/behaviour/iface/query.rs index eeb699fca6b..f28c39fb245 100644 --- a/protocols/mdns/src/behaviour/iface/query.rs +++ b/protocols/mdns/src/behaviour/iface/query.rs @@ -24,10 +24,9 @@ use hickory_proto::{ op::Message, rr::{Name, RData}, }; -use libp2p_core::{ - address_translation, - multiaddr::{Multiaddr, Protocol}, -}; +use libp2p_core::multiaddr::{Multiaddr, Protocol}; +use libp2p_swarm::address_translation; + use libp2p_identity::PeerId; use std::time::Instant; use std::{fmt, net::SocketAddr, str, time::Duration}; diff --git a/protocols/relay/src/priv_client/transport.rs b/protocols/relay/src/priv_client/transport.rs index c4cc4b6a6d0..54587719793 100644 --- a/protocols/relay/src/priv_client/transport.rs +++ b/protocols/relay/src/priv_client/transport.rs @@ -210,10 +210,6 @@ impl libp2p_core::Transport for Transport { .boxed()) } - fn address_translation(&self, _server: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index c528e8d586f..b3effa4fef5 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -68,6 +68,7 @@ pub mod dial_opts; pub mod dummy; pub mod handler; mod listen_opts; +pub mod translation; /// Bundles all symbols required for the [`libp2p_swarm_derive::NetworkBehaviour`] macro. #[doc(hidden)] @@ -153,6 +154,7 @@ use std::{ task::{Context, Poll}, }; use tracing::Instrument; +pub use translation::address_translation; /// Event generated by the [`NetworkBehaviour`] that the swarm will report back. type TBehaviourOutEvent = ::ToSwarm; diff --git a/core/src/translation.rs b/swarm/src/translation.rs similarity index 98% rename from core/src/translation.rs rename to swarm/src/translation.rs index efddae31052..fd448bfaf23 100644 --- a/core/src/translation.rs +++ b/swarm/src/translation.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use multiaddr::{Multiaddr, Protocol}; +use libp2p_core::{multiaddr::Protocol, Multiaddr}; /// Perform IP address translation. /// diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index 7df74b6c91d..2f6ccb27150 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -237,10 +237,6 @@ where self.do_dial(addr, dial_opts) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.lock().address_translation(server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -668,10 +664,6 @@ mod tests { Ok(Box::pin(future::ready(Ok(())))) } - fn address_translation(&self, _: &Multiaddr, _: &Multiaddr) -> Option { - None - } - fn poll( self: Pin<&mut Self>, _: &mut Context<'_>, diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index d9ca9740a90..ff0a4a20dc4 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -249,15 +249,6 @@ impl Transport for GenTransport

{ } } - fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { - if !is_quic_addr(listen, self.support_draft_29) - || !is_quic_addr(observed, self.support_draft_29) - { - return None; - } - Some(observed.clone()) - } - fn dial( &mut self, addr: Multiaddr, @@ -723,33 +714,6 @@ fn multiaddr_to_socketaddr( } } -/// Whether an [`Multiaddr`] is a valid for the QUIC transport. -fn is_quic_addr(addr: &Multiaddr, support_draft_29: bool) -> bool { - use Protocol::*; - let mut iter = addr.iter(); - let Some(first) = iter.next() else { - return false; - }; - let Some(second) = iter.next() else { - return false; - }; - let Some(third) = iter.next() else { - return false; - }; - let fourth = iter.next(); - let fifth = iter.next(); - - matches!(first, Ip4(_) | Ip6(_) | Dns(_) | Dns4(_) | Dns6(_)) - && matches!(second, Udp(_)) - && if support_draft_29 { - matches!(third, QuicV1 | Quic) - } else { - matches!(third, QuicV1) - } - && matches!(fourth, Some(P2p(_)) | None) - && fifth.is_none() -} - /// Turns an IP address and port into the corresponding QUIC multiaddr. fn socketaddr_to_multiaddr(socket_addr: &SocketAddr, version: ProtocolVersion) -> Multiaddr { let quic_proto = match version { diff --git a/transports/quic/tests/smoke.rs b/transports/quic/tests/smoke.rs index a95f23748b1..6a1e0e0df9e 100644 --- a/transports/quic/tests/smoke.rs +++ b/transports/quic/tests/smoke.rs @@ -117,14 +117,6 @@ async fn wrapped_with_delay() { self.0.lock().unwrap().remove_listener(id) } - fn address_translation( - &self, - listen: &Multiaddr, - observed: &Multiaddr, - ) -> Option { - self.0.lock().unwrap().address_translation(listen, observed) - } - /// Delayed dial, i.e. calling [`Transport::dial`] on the inner [`Transport`] not within the /// synchronous [`Transport::dial`] method, but within the [`Future`] returned by the outer /// [`Transport::dial`]. diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 088de025bda..183d90b77a3 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -40,7 +40,6 @@ use futures::{future::Ready, prelude::*, stream::SelectAll}; use futures_timer::Delay; use if_watch::IfEvent; use libp2p_core::{ - address_translation, multiaddr::{Multiaddr, Protocol}, transport::{DialOpts, ListenerId, PortUse, TransportError, TransportEvent}, }; @@ -399,31 +398,6 @@ where .boxed()) } - /// When port reuse is disabled and hence ephemeral local ports are - /// used for outgoing connections, the returned address is the - /// `observed` address with the port replaced by the port of the - /// `listen` address. - /// - /// If port reuse is enabled, `Some(observed)` is returned, as there - /// is a chance that the `observed` address _and_ port are reachable - /// for other peers if there is a NAT in the way that does endpoint- - /// independent filtering. Furthermore, even if that is not the case - /// and TCP hole punching techniques must be used for NAT traversal, - /// the `observed` address is still the one that a remote should connect - /// to for the purpose of the hole punching procedure, as it represents - /// the mapped IP and port of the NAT device in front of the local - /// node. - /// - /// `None` is returned if one of the given addresses is not a TCP/IP - /// address. - fn address_translation(&self, listen: &Multiaddr, observed: &Multiaddr) -> Option { - if !is_tcp_addr(listen) || !is_tcp_addr(observed) { - return None; - } - - address_translation(listen, observed) - } - /// Poll all listeners. #[tracing::instrument(level = "trace", name = "Transport::poll", skip(self, cx))] fn poll( @@ -713,23 +687,6 @@ fn ip_to_multiaddr(ip: IpAddr, port: u16) -> Multiaddr { Multiaddr::empty().with(ip.into()).with(Protocol::Tcp(port)) } -fn is_tcp_addr(addr: &Multiaddr) -> bool { - use Protocol::*; - - let mut iter = addr.iter(); - - let first = match iter.next() { - None => return false, - Some(p) => p, - }; - let second = match iter.next() { - None => return false, - Some(p) => p, - }; - - matches!(first, Ip4(_) | Ip6(_) | Dns(_) | Dns4(_) | Dns6(_)) && matches!(second, Tcp(_)) -} - #[cfg(test)] mod tests { use super::*; @@ -1198,43 +1155,6 @@ mod tests { test_address_translation::() } - fn test_address_translation() - where - T: Default + libp2p_core::Transport, - { - let transport = T::default(); - - let port = 42; - let tcp_listen_addr = Multiaddr::empty() - .with(Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1))) - .with(Protocol::Tcp(port)); - let observed_ip = Ipv4Addr::new(123, 45, 67, 8); - let tcp_observed_addr = Multiaddr::empty() - .with(Protocol::Ip4(observed_ip)) - .with(Protocol::Tcp(1)) - .with(Protocol::P2p(PeerId::random())); - - let translated = transport - .address_translation(&tcp_listen_addr, &tcp_observed_addr) - .unwrap(); - let mut iter = translated.iter(); - assert_eq!(iter.next(), Some(Protocol::Ip4(observed_ip))); - assert_eq!(iter.next(), Some(Protocol::Tcp(port))); - assert_eq!(iter.next(), None); - - let quic_addr = Multiaddr::empty() - .with(Protocol::Ip4(Ipv4Addr::new(87, 65, 43, 21))) - .with(Protocol::Udp(1)) - .with(Protocol::QuicV1); - - assert!(transport - .address_translation(&tcp_listen_addr, &quic_addr) - .is_none()); - assert!(transport - .address_translation(&quic_addr, &tcp_observed_addr) - .is_none()); - } - #[test] fn test_remove_listener() { let _ = tracing_subscriber::fmt() diff --git a/transports/uds/src/lib.rs b/transports/uds/src/lib.rs index 4ef4c20552e..5c57e255b4d 100644 --- a/transports/uds/src/lib.rs +++ b/transports/uds/src/lib.rs @@ -169,14 +169,6 @@ macro_rules! codegen { } } - fn address_translation( - &self, - _server: &Multiaddr, - _observed: &Multiaddr, - ) -> Option { - None - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/transports/webrtc-websys/src/transport.rs b/transports/webrtc-websys/src/transport.rs index 16faa95fb14..836acb0b9f6 100644 --- a/transports/webrtc-websys/src/transport.rs +++ b/transports/webrtc-websys/src/transport.rs @@ -104,10 +104,6 @@ impl libp2p_core::Transport for Transport { ) -> Poll> { Poll::Pending } - - fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } } /// Checks if local Firefox. diff --git a/transports/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index 0cf18bab59c..62049c8f59b 100644 --- a/transports/webrtc/src/tokio/transport.rs +++ b/transports/webrtc/src/tokio/transport.rs @@ -162,10 +162,6 @@ impl libp2p_core::Transport for Transport { } .boxed()) } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - libp2p_core::address_translation(server, observed) - } } /// A stream of incoming connections on one or more interfaces. diff --git a/transports/websocket-websys/src/lib.rs b/transports/websocket-websys/src/lib.rs index 87fa3cf6f5e..5f57b68650b 100644 --- a/transports/websocket-websys/src/lib.rs +++ b/transports/websocket-websys/src/lib.rs @@ -116,10 +116,6 @@ impl libp2p_core::Transport for Transport { ) -> std::task::Poll> { Poll::Pending } - - fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } } // Try to convert Multiaddr to a Websocket url. diff --git a/transports/websocket/src/framed.rs b/transports/websocket/src/framed.rs index d4d83c26584..2085cec9af8 100644 --- a/transports/websocket/src/framed.rs +++ b/transports/websocket/src/framed.rs @@ -156,10 +156,6 @@ where self.do_dial(addr, dial_opts) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.lock().address_translation(server, observed) - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/transports/websocket/src/lib.rs b/transports/websocket/src/lib.rs index 639cab71d31..40d6db44471 100644 --- a/transports/websocket/src/lib.rs +++ b/transports/websocket/src/lib.rs @@ -210,10 +210,6 @@ where self.transport.dial(addr, opts) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.address_translation(server, observed) - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/transports/webtransport-websys/src/transport.rs b/transports/webtransport-websys/src/transport.rs index 9f828922182..1b470fc40b6 100644 --- a/transports/webtransport-websys/src/transport.rs +++ b/transports/webtransport-websys/src/transport.rs @@ -99,8 +99,4 @@ impl libp2p_core::Transport for Transport { ) -> Poll> { Poll::Pending } - - fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } } From efd5882153275c2ef76063fb464ebdee8573a310 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:11:00 +0200 Subject: [PATCH 137/179] Update lock file --- Cargo.lock | 340 +++++++++++++++++++++++++---------------------------- 1 file changed, 158 insertions(+), 182 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ce7c2c246c..d4220d6fc4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -216,7 +216,7 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", "synstructure 0.13.1", ] @@ -239,7 +239,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -271,22 +271,21 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2776ead772134d55b62dd45e59a79e21612d85d0af729b8b7d3967d601a62a" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.2", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ "async-task", "concurrent-queue", @@ -313,10 +312,10 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.0", + "async-channel 2.3.1", "async-executor", - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io 2.3.3", + "async-lock 3.4.0", "blocking", "futures-lite 2.3.0", "once_cell", @@ -344,17 +343,17 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.7.0", + "polling 3.7.1", "rustix 0.38.34", "slab", "tracing", @@ -372,12 +371,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener 5.3.1", + "event-listener-strategy", "pin-project-lite", ] @@ -411,12 +410,12 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" +checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" dependencies = [ - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io 2.3.3", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", @@ -489,7 +488,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -506,7 +505,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -570,7 +569,7 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", "itoa", "matchit", "memchr", @@ -659,9 +658,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -773,12 +772,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel 2.3.0", - "async-lock 3.3.0", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite 2.3.0", @@ -877,9 +875,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "ccm" @@ -1004,7 +1002,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1145,9 +1143,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -1173,9 +1171,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1261,7 +1259,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1402,7 +1400,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1464,9 +1462,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elliptic-curve" @@ -1507,7 +1505,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1568,43 +1566,22 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.0", + "event-listener 5.3.1", "pin-project-lite", ] @@ -1700,9 +1677,9 @@ dependencies = [ [[package]] name = "futures-bounded" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e2774cc104e198ef3d3e1ff4ab40f86fa3245d6cb6a3a46174f21463cee173" +checksum = "91f328e7fb845fc832912fb6a34f40cf6d1888c92f974d1893a54e97b5ff542e" dependencies = [ "futures-timer", "futures-util", @@ -1778,7 +1755,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1798,7 +1775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.5", + "rustls 0.23.9", "rustls-pki-types", ] @@ -1912,9 +1889,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -1979,15 +1956,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http 1.1.0", "indexmap 2.2.6", "slab", @@ -2252,9 +2229,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -2283,7 +2260,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.4", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2318,7 +2295,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.28", + "hyper 0.14.29", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -2342,9 +2319,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -2408,7 +2385,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" dependencies = [ - "async-io 2.3.2", + "async-io 2.3.3", "core-foundation", "fnv", "futures", @@ -2433,7 +2410,7 @@ dependencies = [ "bytes", "futures", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.29", "log", "rand 0.8.5", "tokio", @@ -2473,9 +2450,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -2682,9 +2659,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libp2p" @@ -3023,7 +3000,7 @@ dependencies = [ name = "libp2p-mdns" version = "0.45.1" dependencies = [ - "async-io 2.3.2", + "async-io 2.3.3", "async-std", "data-encoding", "futures", @@ -3427,7 +3404,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -3451,7 +3428,7 @@ dependencies = [ name = "libp2p-tcp" version = "0.41.0" dependencies = [ - "async-io 2.3.2", + "async-io 2.3.3", "async-std", "futures", "futures-timer", @@ -3732,9 +3709,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -3870,9 +3847,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -3952,11 +3929,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -4140,9 +4116,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] @@ -4206,7 +4182,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -4354,9 +4330,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -4423,7 +4399,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -4451,9 +4427,9 @@ dependencies = [ [[package]] name = "piper" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", "fastrand 2.1.0", @@ -4484,9 +4460,9 @@ checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -4497,15 +4473,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] @@ -4528,9 +4504,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" dependencies = [ "cfg-if", "concurrent-queue", @@ -4611,9 +4587,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -4638,14 +4614,14 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", "prost-derive", @@ -4653,15 +4629,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -4995,7 +4971,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.4", + "h2 0.4.5", "http 1.1.0", "http-body 1.0.0", "http-body-util", @@ -5186,7 +5162,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.63", + "syn 2.0.66", "walkdir", ] @@ -5254,7 +5230,7 @@ dependencies = [ "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -5279,21 +5255,21 @@ dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.5" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcee0551bd1aa3e18e5adbf2c0544722014b899adb31bd186ec638d3da97e" +checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" dependencies = [ "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -5326,9 +5302,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -5337,9 +5313,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rw-stream-sink" @@ -5478,22 +5454,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -5526,14 +5502,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -5793,15 +5769,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -5851,9 +5827,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -5892,7 +5868,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -5992,22 +5968,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -6078,9 +6054,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -6107,13 +6083,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -6164,9 +6140,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -6176,18 +6152,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap 2.2.6", "serde", @@ -6210,7 +6186,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", + "hyper 0.14.29", "hyper-timeout", "percent-encoding", "pin-project", @@ -6300,7 +6276,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -6379,9 +6355,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddb747392ea12569d501a5bbca08852e4c8cd88b92566074b2243b8846f09e6" +checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ "glob", "serde", @@ -6642,7 +6618,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -6676,7 +6652,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6709,7 +6685,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -7178,9 +7154,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" dependencies = [ "memchr", ] @@ -7324,14 +7300,14 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -7344,5 +7320,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] From 0c750efde44bb481fee891cb55c8f7c6b7ce5c72 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:32:50 +0200 Subject: [PATCH 138/179] Update transport to always use a new endpoint when dialing without reuse --- transports/quic/src/transport.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index ff0a4a20dc4..c4454b45249 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -263,9 +263,9 @@ impl Transport for GenTransport

{ None => { // No listener. Get or create an explicit dialer. let socket_family = socket_addr.ip().into(); - let dialer = match self.dialer.entry(socket_family) { - Entry::Occupied(occupied) => occupied.get().clone(), - Entry::Vacant(vacant) => { + let dialer = match (dial_opts.port_use, self.dialer.entry(socket_family)) { + (PortUse::Reuse, Entry::Occupied(occupied)) => occupied.get().clone(), + (_, entry) => { if let Some(waker) = self.waker.take() { waker.wake(); } @@ -282,7 +282,11 @@ impl Transport for GenTransport

{ let endpoint_config = self.quinn_config.endpoint_config.clone(); let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; - vacant.insert(endpoint.clone()); + if dial_opts.port_use == PortUse::Reuse { + if let Entry::Vacant(vacant) = entry { + vacant.insert(endpoint.clone()); + } + } endpoint } }; From aa7af5b5d99001325a84a810b4286289e07a30fc Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:44:31 +0200 Subject: [PATCH 139/179] No reference in observed --- protocols/identify/src/behaviour.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 5af8ce23ab7..10cd338e543 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -280,11 +280,11 @@ impl Behaviour { .listen_addresses .iter() .filter_map(|server| { - if (is_tcp_addr(server) && is_tcp_addr(&observed)) - || (is_quic_addr(server, true) && is_quic_addr(&observed, true)) - || (is_quic_addr(server, false) && is_quic_addr(&observed, false)) + if (is_tcp_addr(server) && is_tcp_addr(observed)) + || (is_quic_addr(server, true) && is_quic_addr(observed, true)) + || (is_quic_addr(server, false) && is_quic_addr(observed, false)) { - address_translation(server, &observed) + address_translation(server, observed) } else { None } From 51b1333f5cf2d26336d653aa3a0e2e9d68219e69 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:48:34 +0200 Subject: [PATCH 140/179] Remove now defunct test --- transports/tcp/src/lib.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 183d90b77a3..e2a1aac13d2 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -696,7 +696,6 @@ mod tests { }; use libp2p_core::Endpoint; use libp2p_core::Transport as _; - use libp2p_identity::PeerId; #[test] fn multiaddr_to_tcp_conversion() { @@ -1143,18 +1142,6 @@ mod tests { test("/ip4/127.0.0.1/tcp/12345/tcp/12345".parse().unwrap()); } - #[cfg(feature = "async-io")] - #[test] - fn test_address_translation_async_io() { - test_address_translation::() - } - - #[cfg(feature = "tokio")] - #[test] - fn test_address_translation_tokio() { - test_address_translation::() - } - #[test] fn test_remove_listener() { let _ = tracing_subscriber::fmt() From 4c3382fb9d5113a13d241b35f62618df4be9421e Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:06:05 +0200 Subject: [PATCH 141/179] Add changelog to core --- core/CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 73f377a8aa5..adf13fac6cf 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,3 +1,29 @@ +## 0.42.0 + +- Refactor `Transport` to allow the behaviour to tell the implementor of `Transport` to reuse an +existing port or a new one. + See [PR 4568](https://https://github.com/libp2p/rust-libp2p/pull/4568) + - Add new enum `PortUse`, defaulting on `PortUse::Reuse`. + - Add new struct `DialOpts`, containing `PortUse` and `Endpoint`. + - Add field `port_use` to `ConnectedPoint`. + - Add new parameter to `Transport::dial`: `DialOpts`. + - Remove `Transport::dial_as_listener` and move into `Transport::dial`. To dial as listener, one + has to set the `endpoint` field in `DialOpts` to `Endpoint::Listener`. + - Adjust utility transports: + - `AndThen` + - `Boxed` + - `Choice` + - `Dummy` + - `GlobalOnly` + - `Map` + - `MapErr` + - `Memory` + - `Optional` + - `Timeout` + - `Upgrade` +- Remove `Transport::address_translation` and move this functionality into `libp2p_swarm`. + See [umgefahren/libp2p PR 4](https://github.com/umgefahren/rust-libp2p/pull/4) + ## 0.41.2 - Implement `std::fmt::Display` on `ListenerId`. From 3764d10f10c1139e2a97fb541a72ef6547f5b4bf Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:12:51 +0200 Subject: [PATCH 142/179] Remove expected output /p2p/ --- protocols/identify/tests/smoke.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/protocols/identify/tests/smoke.rs b/protocols/identify/tests/smoke.rs index dd92d10979a..6e1e884e709 100644 --- a/protocols/identify/tests/smoke.rs +++ b/protocols/identify/tests/smoke.rs @@ -59,12 +59,7 @@ async fn periodic_identify() { assert_eq!(s1_info.protocol_version, "c"); assert_eq!(s1_info.agent_version, "d"); assert!(!s1_info.protocols.is_empty()); - assert_eq!( - s1_info.observed_addr, - swarm1_memory_listen - .clone() - .with(Protocol::P2p(swarm1_peer_id)) - ); + assert_eq!(s1_info.observed_addr, swarm1_memory_listen); assert!(s1_info.listen_addrs.contains(&swarm2_tcp_listen_addr)); assert!(s1_info.listen_addrs.contains(&swarm2_memory_listen)); From f506edde2627ae4828865337366b532a280c3c2f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:14:11 +0200 Subject: [PATCH 143/179] Port reuse test --- transports/quic/src/transport.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 3b3f8f65e4a..5887102e6e2 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -904,7 +904,7 @@ mod tests { "/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap(), DialOpts { role: Endpoint::Dialer, - port_use: PortUse::New, + port_use: PortUse::Reuse, }, ) .unwrap(); From 945874939042dfec245be0c26183a7512e91b565 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:20:47 +0200 Subject: [PATCH 144/179] I don't need Protocol anymore --- protocols/identify/tests/smoke.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/identify/tests/smoke.rs b/protocols/identify/tests/smoke.rs index 6e1e884e709..49ae9f0726f 100644 --- a/protocols/identify/tests/smoke.rs +++ b/protocols/identify/tests/smoke.rs @@ -1,5 +1,4 @@ use futures::StreamExt; -use libp2p_core::multiaddr::Protocol; use libp2p_identify as identify; use libp2p_swarm::{Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt; From 20319cfda97707dc7cb8cdbbfddef53c3bea9a81 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:32:43 +0200 Subject: [PATCH 145/179] Restore doc test --- protocols/relay/src/priv_client/transport.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/protocols/relay/src/priv_client/transport.rs b/protocols/relay/src/priv_client/transport.rs index 54587719793..f43c265607f 100644 --- a/protocols/relay/src/priv_client/transport.rs +++ b/protocols/relay/src/priv_client/transport.rs @@ -50,7 +50,7 @@ use thiserror::Error; /// 1. Establish relayed connections by dialing `/p2p-circuit` addresses. /// /// ``` -/// # use libp2p_core::{Multiaddr, multiaddr::{Protocol}, Transport}; +/// # use libp2p_core::{Multiaddr, multiaddr::{Protocol}, Transport, transport::{DialOpts, PortUse}, connection::Endpoint}; /// # use libp2p_core::transport::memory::MemoryTransport; /// # use libp2p_core::transport::choice::OrTransport; /// # use libp2p_relay as relay; @@ -67,7 +67,10 @@ use thiserror::Error; /// .with(Protocol::P2p(relay_id.into())) // Relay peer id. /// .with(Protocol::P2pCircuit) // Signal to connect via relay and not directly. /// .with(Protocol::P2p(destination_id.into())); // Destination peer id. -/// transport.dial(dst_addr_via_relay).unwrap(); +/// transport.dial(dst_addr_via_relay, DialOpts { +/// port_use: PortUse::Reuse, +/// role: Endpoint::Dialer, +/// }).unwrap(); /// ``` /// /// 3. Listen for incoming relayed connections via specific relay. From 8b136cf285c09e1f4aaae4cee218064ce6172c52 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:23:35 +0200 Subject: [PATCH 146/179] Add CHANGELOG entries --- Cargo.toml | 4 ++-- core/CHANGELOG.md | 2 +- libp2p/CHANGELOG.md | 3 +++ protocols/autonat/CHANGELOG.md | 5 +++++ protocols/autonat/Cargo.toml | 2 +- protocols/dcutr/CHANGELOG.md | 5 +++++ protocols/dcutr/Cargo.toml | 2 +- protocols/floodsub/CHANGELOG.md | 5 +++++ protocols/floodsub/Cargo.toml | 2 +- protocols/gossipsub/CHANGELOG.md | 7 ++++++- protocols/gossipsub/Cargo.toml | 2 +- protocols/identify/CHANGELOG.md | 5 +++++ protocols/identify/Cargo.toml | 2 +- protocols/kad/CHANGELOG.md | 2 ++ protocols/mdns/CHANGELOG.md | 5 +++++ protocols/mdns/Cargo.toml | 2 +- protocols/perf/CHANGELOG.md | 5 +++++ protocols/perf/Cargo.toml | 2 +- protocols/ping/CHANGELOG.md | 5 ++++- protocols/ping/Cargo.toml | 2 +- protocols/relay/CHANGELOG.md | 5 +++++ protocols/relay/Cargo.toml | 2 +- protocols/rendezvous/CHANGELOG.md | 5 +++++ protocols/rendezvous/Cargo.toml | 2 +- protocols/request-response/CHANGELOG.md | 5 +++++ protocols/request-response/Cargo.toml | 2 +- protocols/stream/CHANGELOG.md | 5 +++++ protocols/stream/Cargo.toml | 2 +- protocols/upnp/CHANGELOG.md | 5 +++++ protocols/upnp/Cargo.toml | 2 +- swarm/CHANGELOG.md | 9 +++++++++ swarm/Cargo.toml | 2 +- transports/dns/CHANGELOG.md | 5 +++++ transports/dns/Cargo.toml | 2 +- transports/quic/CHANGELOG.md | 5 +++++ transports/quic/Cargo.toml | 2 +- transports/tcp/CHANGELOG.md | 5 +++++ transports/tcp/Cargo.toml | 2 +- transports/uds/CHANGELOG.md | 5 +++++ transports/uds/Cargo.toml | 2 +- transports/webrtc-websys/CHANGELOG.md | 5 +++++ transports/webrtc-websys/Cargo.toml | 2 +- transports/webrtc/CHANGELOG.md | 5 +++++ transports/webrtc/Cargo.toml | 2 +- transports/websocket-websys/CHANGELOG.md | 5 +++++ transports/websocket-websys/Cargo.toml | 2 +- transports/websocket/CHANGELOG.md | 5 +++++ transports/websocket/Cargo.toml | 2 +- transports/webtransport-websys/CHANGELOG.md | 5 +++++ transports/webtransport-websys/Cargo.toml | 2 +- 50 files changed, 150 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b037244a60c..bcf884c32e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ libp2p-perf = { version = "0.3.0", path = "protocols/perf" } libp2p-ping = { version = "0.44.1", path = "protocols/ping" } libp2p-plaintext = { version = "0.41.0", path = "transports/plaintext" } libp2p-pnet = { version = "0.24.0", path = "transports/pnet" } -libp2p-quic = { version = "0.10.3", path = "transports/quic" } +libp2p-quic = { version = "0.11.0", path = "transports/quic" } libp2p-relay = { version = "0.17.2", path = "protocols/relay" } libp2p-rendezvous = { version = "0.14.0", path = "protocols/rendezvous" } libp2p-request-response = { version = "0.26.3", path = "protocols/request-response" } @@ -104,7 +104,7 @@ libp2p-stream = { version = "0.1.0-alpha.1", path = "protocols/stream" } libp2p-swarm = { version = "0.44.2", path = "swarm" } libp2p-swarm-derive = { version = "=0.34.2", path = "swarm-derive" } # `libp2p-swarm-derive` may not be compatible with different `libp2p-swarm` non-breaking releases. E.g. `libp2p-swarm` might introduce a new enum variant `FromSwarm` (which is `#[non-exhaustive]`) in a non-breaking release. Older versions of `libp2p-swarm-derive` would not forward this enum variant within the `NetworkBehaviour` hierarchy. Thus the version pinning is required. libp2p-swarm-test = { version = "0.3.0", path = "swarm-test" } -libp2p-tcp = { version = "0.41.1", path = "transports/tcp" } +libp2p-tcp = { version = "0.42.0", path = "transports/tcp" } libp2p-tls = { version = "0.4.0", path = "transports/tls" } libp2p-uds = { version = "0.40.0", path = "transports/uds" } libp2p-upnp = { version = "0.2.2", path = "protocols/upnp" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index adf13fac6cf..505ebd5d919 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.42.0 +## 0.42.0 -- unreleased - Refactor `Transport` to allow the behaviour to tell the implementor of `Transport` to reuse an existing port or a new one. diff --git a/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md index 7acd4ab602d..a41586c6814 100644 --- a/libp2p/CHANGELOG.md +++ b/libp2p/CHANGELOG.md @@ -6,6 +6,9 @@ - Raise MSRV to 1.73. See [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.53.2 - Allow `SwarmBuilder::with_bandwidth_metrics` after `SwarmBuilder::with_websocket`. diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index 1259dd01fd4..b75a76d4db8 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.12.0 - Remove `Clone`, `PartialEq` and `Eq` implementations on `Event` and its sub-structs. diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index ba7a21b4da7..2fd5d22f0cb 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" authors = ["David Craven ", "Elena Frank "] -version = "0.12.0" +version = "0.13.0" license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] diff --git a/protocols/dcutr/CHANGELOG.md b/protocols/dcutr/CHANGELOG.md index d3857373658..fcdd4d60bd2 100644 --- a/protocols/dcutr/CHANGELOG.md +++ b/protocols/dcutr/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.11.0 - Add `ConnectionId` to `Event::DirectConnectionUpgradeSucceeded` and `Event::DirectConnectionUpgradeFailed`. diff --git a/protocols/dcutr/Cargo.toml b/protocols/dcutr/Cargo.toml index 819c7a6e56b..3f9cb695165 100644 --- a/protocols/dcutr/Cargo.toml +++ b/protocols/dcutr/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-dcutr" edition = "2021" rust-version = { workspace = true } description = "Direct connection upgrade through relay" -version = "0.11.0" +version = "0.12.0" authors = ["Max Inden "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/floodsub/CHANGELOG.md b/protocols/floodsub/CHANGELOG.md index 8e3cb70ddf1..e741d5aef35 100644 --- a/protocols/floodsub/CHANGELOG.md +++ b/protocols/floodsub/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.45.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.44.0 - Change publish to require `data: impl Into` to internally avoid any costly cloning / allocation. diff --git a/protocols/floodsub/Cargo.toml b/protocols/floodsub/Cargo.toml index 8bcbc4a3557..9f0557c6d01 100644 --- a/protocols/floodsub/Cargo.toml +++ b/protocols/floodsub/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-floodsub" edition = "2021" rust-version = { workspace = true } description = "Floodsub protocol for libp2p" -version = "0.44.0" +version = "0.45.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/gossipsub/CHANGELOG.md b/protocols/gossipsub/CHANGELOG.md index 5ff4cfa27d6..e1059b13bf3 100644 --- a/protocols/gossipsub/CHANGELOG.md +++ b/protocols/gossipsub/CHANGELOG.md @@ -1,7 +1,12 @@ +## 0.47.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.46.1 - Deprecate `Rpc` in preparation for removing it from the public API because it is an internal type. - See [PR 4833](https://github.com/libp2p/rust-libp2p/pull/4833). + See [PR 4833](https://github.com/libp2p/rust-libp2p/pull/4833). ## 0.46.0 diff --git a/protocols/gossipsub/Cargo.toml b/protocols/gossipsub/Cargo.toml index aa11a8a8309..f808e47fc35 100644 --- a/protocols/gossipsub/Cargo.toml +++ b/protocols/gossipsub/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-gossipsub" edition = "2021" rust-version = { workspace = true } description = "Gossipsub protocol for libp2p" -version = "0.46.1" +version = "0.47.0" authors = ["Age Manning "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/identify/CHANGELOG.md b/protocols/identify/CHANGELOG.md index 83984448d07..b9448f71b4e 100644 --- a/protocols/identify/CHANGELOG.md +++ b/protocols/identify/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.45.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.44.2 - Emit `ToSwarm::NewExternalAddrOfPeer` for all external addresses of remote peers. diff --git a/protocols/identify/Cargo.toml b/protocols/identify/Cargo.toml index e75a2c4c4bc..dcb2d77a917 100644 --- a/protocols/identify/Cargo.toml +++ b/protocols/identify/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-identify" edition = "2021" rust-version = { workspace = true } description = "Nodes identifcation protocol for libp2p" -version = "0.44.2" +version = "0.45.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index bd0c9857cb5..20301689db9 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -17,6 +17,8 @@ See [PR 5148](https://github.com/libp2p/rust-libp2p/pull/5148). - Derive `Copy` for `kbucket::key::Key`. See [PR 5317](https://github.com/libp2p/rust-libp2p/pull/5317). +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) ## 0.45.3 diff --git a/protocols/mdns/CHANGELOG.md b/protocols/mdns/CHANGELOG.md index cfd02232b07..44db763adf6 100644 --- a/protocols/mdns/CHANGELOG.md +++ b/protocols/mdns/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.46.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.45.1 - Ensure `Multiaddr` handled and returned by `Behaviour` are `/p2p` terminated. diff --git a/protocols/mdns/Cargo.toml b/protocols/mdns/Cargo.toml index 0d58c9467c4..1b98ecc9cda 100644 --- a/protocols/mdns/Cargo.toml +++ b/protocols/mdns/Cargo.toml @@ -2,7 +2,7 @@ name = "libp2p-mdns" edition = "2021" rust-version = { workspace = true } -version = "0.45.1" +version = "0.46.0" description = "Implementation of the libp2p mDNS discovery method" authors = ["Parity Technologies "] license = "MIT" diff --git a/protocols/perf/CHANGELOG.md b/protocols/perf/CHANGELOG.md index 4e448d7f44a..22066844e34 100644 --- a/protocols/perf/CHANGELOG.md +++ b/protocols/perf/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.3.0 - Continuously measure on single connection (iperf-style). diff --git a/protocols/perf/Cargo.toml b/protocols/perf/Cargo.toml index 6ed6baa0714..6133c40ad22 100644 --- a/protocols/perf/Cargo.toml +++ b/protocols/perf/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-perf" edition = "2021" rust-version = { workspace = true } description = "libp2p perf protocol implementation" -version = "0.3.0" +version = "0.4.0" authors = ["Max Inden "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/ping/CHANGELOG.md b/protocols/ping/CHANGELOG.md index 17338a4ba9a..b1609d2a70d 100644 --- a/protocols/ping/CHANGELOG.md +++ b/protocols/ping/CHANGELOG.md @@ -1,10 +1,13 @@ -## 0.44.1 - unreleased +## 0.45.0 - unreleased - Impose `Sync` on `ping::Failure::Other`. `ping::Event` can now be shared between threads. See [PR 5250] +- Implement refactored `Transport`. + See [PR 4568] [PR 5250]: https://github.com/libp2p/rust-libp2p/pull/5250 +[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.44.0 diff --git a/protocols/ping/Cargo.toml b/protocols/ping/Cargo.toml index c436478668c..fd9c0264944 100644 --- a/protocols/ping/Cargo.toml +++ b/protocols/ping/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-ping" edition = "2021" rust-version = { workspace = true } description = "Ping protocol for libp2p" -version = "0.44.1" +version = "0.45.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/relay/CHANGELOG.md b/protocols/relay/CHANGELOG.md index 83dbf4739ac..830ccbd16b1 100644 --- a/protocols/relay/CHANGELOG.md +++ b/protocols/relay/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.18.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.17.2 - Fix support for unlimited relay connection according to spec. diff --git a/protocols/relay/Cargo.toml b/protocols/relay/Cargo.toml index aa185a42af0..79d37c58d7b 100644 --- a/protocols/relay/Cargo.toml +++ b/protocols/relay/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-relay" edition = "2021" rust-version = { workspace = true } description = "Communications relaying for libp2p" -version = "0.17.2" +version = "0.18.0" authors = ["Parity Technologies ", "Max Inden "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/rendezvous/CHANGELOG.md b/protocols/rendezvous/CHANGELOG.md index e60699da734..9f0404cd101 100644 --- a/protocols/rendezvous/CHANGELOG.md +++ b/protocols/rendezvous/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.15.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.14.0 diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 2d344e5e250..feb3ee68988 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-rendezvous" edition = "2021" rust-version = { workspace = true } description = "Rendezvous protocol for libp2p" -version = "0.14.0" +version = "0.15.0" authors = ["The COMIT guys "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index e9d92b08857..ff00112eb28 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.27.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.26.3 - Report failure when streams are at capacity. diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 794a3e74956..760f6c327f2 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-request-response" edition = "2021" rust-version = { workspace = true } description = "Generic Request/Response Protocols" -version = "0.26.3" +version = "0.27.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/stream/CHANGELOG.md b/protocols/stream/CHANGELOG.md index 1e3b85da0b9..a2123ccdb38 100644 --- a/protocols/stream/CHANGELOG.md +++ b/protocols/stream/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.0-alpha -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.1.0-alpha.1 - Implement Error for `OpenStreamError`. See [PR 5169](https://github.com/libp2p/rust-libp2p/pull/5169). diff --git a/protocols/stream/Cargo.toml b/protocols/stream/Cargo.toml index a8d88399c0b..9aa9559a2d6 100644 --- a/protocols/stream/Cargo.toml +++ b/protocols/stream/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-stream" -version = "0.1.0-alpha.1" +version = "0.2.0-alpha" edition = "2021" rust-version.workspace = true description = "Generic stream protocols for libp2p" diff --git a/protocols/upnp/CHANGELOG.md b/protocols/upnp/CHANGELOG.md index 9472c7153c6..d6a2e0fc29f 100644 --- a/protocols/upnp/CHANGELOG.md +++ b/protocols/upnp/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.2.2 - Fix a panic caused when `upnp::Gateway` is dropped and its events queue receiver is no longer available. diff --git a/protocols/upnp/Cargo.toml b/protocols/upnp/Cargo.toml index 944b3323842..50ed9db0f6f 100644 --- a/protocols/upnp/Cargo.toml +++ b/protocols/upnp/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-upnp" edition = "2021" rust-version = "1.60.0" description = "UPnP support for libp2p transports" -version = "0.2.2" +version = "0.3.0" license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index 75e18a6a5af..e936a0a060a 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.45.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568] +- Move `address_translation` into swarm. + See [PR 4568] + +[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 + ## 0.44.2 - Allow `NetworkBehaviour`s to share addresses of peers. diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index 17a66abba0a..75717ab7bdc 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-swarm" edition = "2021" rust-version = { workspace = true } description = "The libp2p swarm" -version = "0.44.2" +version = "0.45.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/dns/CHANGELOG.md b/transports/dns/CHANGELOG.md index 91cfbc00883..0ea613a2455 100644 --- a/transports/dns/CHANGELOG.md +++ b/transports/dns/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.42.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.41.1 - Add hidden API that removes unnecessary async for `async-std`. diff --git a/transports/dns/Cargo.toml b/transports/dns/Cargo.toml index 105fe0a62d0..9baae2144a2 100644 --- a/transports/dns/Cargo.toml +++ b/transports/dns/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-dns" edition = "2021" rust-version = { workspace = true } description = "DNS transport implementation for libp2p" -version = "0.41.1" +version = "0.42.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/quic/CHANGELOG.md b/transports/quic/CHANGELOG.md index 3ec52ec0ddb..9ea5178fc27 100644 --- a/transports/quic/CHANGELOG.md +++ b/transports/quic/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.11.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.10.3 - Update `quinn` to 0.11 and `libp2p-tls` to 0.4.0. diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index 42296d0c0a7..fe7759d1bf6 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-quic" -version = "0.10.3" +version = "0.11.0" authors = ["Parity Technologies "] edition = "2021" rust-version = { workspace = true } diff --git a/transports/tcp/CHANGELOG.md b/transports/tcp/CHANGELOG.md index 36ed93a0f5b..bbc76ba5c4b 100644 --- a/transports/tcp/CHANGELOG.md +++ b/transports/tcp/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.42.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.41.1 diff --git a/transports/tcp/Cargo.toml b/transports/tcp/Cargo.toml index 7f0cf5ca943..b17d7f3b58e 100644 --- a/transports/tcp/Cargo.toml +++ b/transports/tcp/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-tcp" edition = "2021" rust-version = { workspace = true } description = "TCP/IP transport protocol for libp2p" -version = "0.41.1" +version = "0.42.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/uds/CHANGELOG.md b/transports/uds/CHANGELOG.md index aad61d21547..8f132f528b0 100644 --- a/transports/uds/CHANGELOG.md +++ b/transports/uds/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.41.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.40.0 diff --git a/transports/uds/Cargo.toml b/transports/uds/Cargo.toml index 13642a38a48..df5159f3c02 100644 --- a/transports/uds/Cargo.toml +++ b/transports/uds/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-uds" edition = "2021" rust-version = { workspace = true } description = "Unix domain sockets transport for libp2p" -version = "0.40.0" +version = "0.41.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/webrtc-websys/CHANGELOG.md b/transports/webrtc-websys/CHANGELOG.md index 634120c53c3..df8e3df8cba 100644 --- a/transports/webrtc-websys/CHANGELOG.md +++ b/transports/webrtc-websys/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0-alpha -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.3.0-alpha - Bump version in order to publish a new version dependent on latest `libp2p-core`. diff --git a/transports/webrtc-websys/Cargo.toml b/transports/webrtc-websys/Cargo.toml index 4a8b8dfcdf0..c874b33bfc7 100644 --- a/transports/webrtc-websys/Cargo.toml +++ b/transports/webrtc-websys/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" name = "libp2p-webrtc-websys" repository = "https://github.com/libp2p/rust-libp2p" rust-version = { workspace = true } -version = "0.3.0-alpha" +version = "0.4.0-alpha" publish = true [dependencies] diff --git a/transports/webrtc/CHANGELOG.md b/transports/webrtc/CHANGELOG.md index 930526d58d5..7198d1d66a1 100644 --- a/transports/webrtc/CHANGELOG.md +++ b/transports/webrtc/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.0-alpha -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.7.1-alpha - Bump `libp2p-webrtc-utils` dependency to `0.2.0`. diff --git a/transports/webrtc/Cargo.toml b/transports/webrtc/Cargo.toml index 0450616261e..9cb78d6290c 100644 --- a/transports/webrtc/Cargo.toml +++ b/transports/webrtc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-webrtc" -version = "0.7.1-alpha" +version = "0.8.0-alpha" authors = ["Parity Technologies "] description = "WebRTC transport for libp2p" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/websocket-websys/CHANGELOG.md b/transports/websocket-websys/CHANGELOG.md index f92b76ebeaa..6e8a5afa6e3 100644 --- a/transports/websocket-websys/CHANGELOG.md +++ b/transports/websocket-websys/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.3.2 - Change close code in drop implementation to `1000` given that in browsers only diff --git a/transports/websocket-websys/Cargo.toml b/transports/websocket-websys/Cargo.toml index 48bf8d9818a..0b3148a8b92 100644 --- a/transports/websocket-websys/Cargo.toml +++ b/transports/websocket-websys/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-websocket-websys" edition = "2021" rust-version = "1.60.0" description = "WebSocket for libp2p under WASM environment" -version = "0.3.2" +version = "0.4.0" authors = ["Vince Vasta "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/websocket/CHANGELOG.md b/transports/websocket/CHANGELOG.md index 192b1fa094e..e243aae0927 100644 --- a/transports/websocket/CHANGELOG.md +++ b/transports/websocket/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.44.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.43.0 diff --git a/transports/websocket/Cargo.toml b/transports/websocket/Cargo.toml index 7b83d8284bd..d0b8cfb64c5 100644 --- a/transports/websocket/Cargo.toml +++ b/transports/websocket/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-websocket" edition = "2021" rust-version = { workspace = true } description = "WebSocket transport for libp2p" -version = "0.43.0" +version = "0.44.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/webtransport-websys/CHANGELOG.md b/transports/webtransport-websys/CHANGELOG.md index 0409819a63f..045c3e49e9b 100644 --- a/transports/webtransport-websys/CHANGELOG.md +++ b/transports/webtransport-websys/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 -- unreleased + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.3.0 * Fix unhandled exceptions thrown when calling `Webtransport::close`. diff --git a/transports/webtransport-websys/Cargo.toml b/transports/webtransport-websys/Cargo.toml index 3defdce5203..68ba7091794 100644 --- a/transports/webtransport-websys/Cargo.toml +++ b/transports/webtransport-websys/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-webtransport-websys" edition = "2021" rust-version = { workspace = true } description = "WebTransport for libp2p under WASM environment" -version = "0.3.0" +version = "0.4.0" authors = [ "Yiannis Marangos ", "oblique ", From aaa8c7cfffcb1942f61a4918abac40f635e345b3 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:31:40 +0200 Subject: [PATCH 147/179] Update versions in root Cargo.toml --- Cargo.toml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bcf884c32e1..1fe5adfb52f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,41 +79,41 @@ libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } libp2p-autonat = { version = "0.12.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } libp2p-core = { version = "0.42.0", path = "core" } -libp2p-dcutr = { version = "0.11.0", path = "protocols/dcutr" } -libp2p-dns = { version = "0.41.1", path = "transports/dns" } -libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } -libp2p-gossipsub = { version = "0.46.1", path = "protocols/gossipsub" } -libp2p-identify = { version = "0.44.2", path = "protocols/identify" } +libp2p-dcutr = { version = "0.12.0", path = "protocols/dcutr" } +libp2p-dns = { version = "0.42.0", path = "transports/dns" } +libp2p-floodsub = { version = "0.45.0", path = "protocols/floodsub" } +libp2p-gossipsub = { version = "0.47.0", path = "protocols/gossipsub" } +libp2p-identify = { version = "0.45.0", path = "protocols/identify" } libp2p-identity = { version = "0.2.8" } libp2p-kad = { version = "0.46.0", path = "protocols/kad" } -libp2p-mdns = { version = "0.45.1", path = "protocols/mdns" } +libp2p-mdns = { version = "0.46.0", path = "protocols/mdns" } libp2p-memory-connection-limits = { version = "0.2.0", path = "misc/memory-connection-limits" } libp2p-metrics = { version = "0.14.1", path = "misc/metrics" } libp2p-mplex = { version = "0.41.0", path = "muxers/mplex" } libp2p-noise = { version = "0.44.0", path = "transports/noise" } -libp2p-perf = { version = "0.3.0", path = "protocols/perf" } -libp2p-ping = { version = "0.44.1", path = "protocols/ping" } +libp2p-perf = { version = "0.4.0", path = "protocols/perf" } +libp2p-ping = { version = "0.45.1", path = "protocols/ping" } libp2p-plaintext = { version = "0.41.0", path = "transports/plaintext" } libp2p-pnet = { version = "0.24.0", path = "transports/pnet" } libp2p-quic = { version = "0.11.0", path = "transports/quic" } -libp2p-relay = { version = "0.17.2", path = "protocols/relay" } -libp2p-rendezvous = { version = "0.14.0", path = "protocols/rendezvous" } -libp2p-request-response = { version = "0.26.3", path = "protocols/request-response" } +libp2p-relay = { version = "0.18.0", path = "protocols/relay" } +libp2p-rendezvous = { version = "0.15.0", path = "protocols/rendezvous" } +libp2p-request-response = { version = "0.27.0", path = "protocols/request-response" } libp2p-server = { version = "0.12.7", path = "misc/server" } -libp2p-stream = { version = "0.1.0-alpha.1", path = "protocols/stream" } -libp2p-swarm = { version = "0.44.2", path = "swarm" } +libp2p-stream = { version = "0.2.0-alpha", path = "protocols/stream" } +libp2p-swarm = { version = "0.45.0", path = "swarm" } libp2p-swarm-derive = { version = "=0.34.2", path = "swarm-derive" } # `libp2p-swarm-derive` may not be compatible with different `libp2p-swarm` non-breaking releases. E.g. `libp2p-swarm` might introduce a new enum variant `FromSwarm` (which is `#[non-exhaustive]`) in a non-breaking release. Older versions of `libp2p-swarm-derive` would not forward this enum variant within the `NetworkBehaviour` hierarchy. Thus the version pinning is required. libp2p-swarm-test = { version = "0.3.0", path = "swarm-test" } libp2p-tcp = { version = "0.42.0", path = "transports/tcp" } libp2p-tls = { version = "0.4.0", path = "transports/tls" } -libp2p-uds = { version = "0.40.0", path = "transports/uds" } -libp2p-upnp = { version = "0.2.2", path = "protocols/upnp" } -libp2p-webrtc = { version = "0.7.1-alpha", path = "transports/webrtc" } +libp2p-uds = { version = "0.41.0", path = "transports/uds" } +libp2p-upnp = { version = "0.3.0", path = "protocols/upnp" } +libp2p-webrtc = { version = "0.8.0-alpha", path = "transports/webrtc" } libp2p-webrtc-utils = { version = "0.2.0", path = "misc/webrtc-utils" } -libp2p-webrtc-websys = { version = "0.3.0-alpha", path = "transports/webrtc-websys" } -libp2p-websocket = { version = "0.43.0", path = "transports/websocket" } -libp2p-websocket-websys = { version = "0.3.2", path = "transports/websocket-websys" } -libp2p-webtransport-websys = { version = "0.3.0", path = "transports/webtransport-websys" } +libp2p-webrtc-websys = { version = "0.4.0-alpha", path = "transports/webrtc-websys" } +libp2p-websocket = { version = "0.44.0", path = "transports/websocket" } +libp2p-websocket-websys = { version = "0.4.0", path = "transports/websocket-websys" } +libp2p-webtransport-websys = { version = "0.4.0", path = "transports/webtransport-websys" } libp2p-yamux = { version = "0.45.1", path = "muxers/yamux" } multiaddr = "0.18.1" multihash = "0.19.1" From 725c5641144b4dbb0ac7343dc6c7635930f56bba Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:33:16 +0200 Subject: [PATCH 148/179] Bump autonat version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1fe5adfb52f..0731b9d48c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ futures-bounded = { version = "0.2.3" } futures-rustls = { version = "0.26.0", default-features = false } libp2p = { version = "0.54.0", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } -libp2p-autonat = { version = "0.12.0", path = "protocols/autonat" } +libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } libp2p-core = { version = "0.42.0", path = "core" } libp2p-dcutr = { version = "0.12.0", path = "protocols/dcutr" } From 1253ee3745abaafd2d256648e7a6165bd60116c2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:36:23 +0200 Subject: [PATCH 149/179] Update versions (again) --- Cargo.lock | 46 +++++++++++++++++++------------------- Cargo.toml | 2 +- examples/stream/Cargo.toml | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d16f1f97c1..38dd1949f71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2712,7 +2712,7 @@ dependencies = [ [[package]] name = "libp2p-autonat" -version = "0.12.0" +version = "0.13.0" dependencies = [ "async-std", "async-trait", @@ -2810,7 +2810,7 @@ dependencies = [ [[package]] name = "libp2p-dcutr" -version = "0.11.0" +version = "0.12.0" dependencies = [ "async-std", "asynchronous-codec", @@ -2844,7 +2844,7 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.41.1" +version = "0.42.0" dependencies = [ "async-std", "async-std-resolver", @@ -2862,7 +2862,7 @@ dependencies = [ [[package]] name = "libp2p-floodsub" -version = "0.44.0" +version = "0.45.0" dependencies = [ "asynchronous-codec", "bytes", @@ -2882,7 +2882,7 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" -version = "0.46.1" +version = "0.47.0" dependencies = [ "async-std", "asynchronous-codec", @@ -2919,7 +2919,7 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.44.2" +version = "0.45.0" dependencies = [ "async-std", "asynchronous-codec", @@ -3007,7 +3007,7 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.45.1" +version = "0.46.0" dependencies = [ "async-io 2.3.3", "async-std", @@ -3132,7 +3132,7 @@ dependencies = [ [[package]] name = "libp2p-perf" -version = "0.3.0" +version = "0.4.0" dependencies = [ "anyhow", "clap", @@ -3162,7 +3162,7 @@ dependencies = [ [[package]] name = "libp2p-ping" -version = "0.44.1" +version = "0.45.0" dependencies = [ "async-std", "either", @@ -3221,7 +3221,7 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.10.3" +version = "0.11.0" dependencies = [ "async-std", "bytes", @@ -3250,7 +3250,7 @@ dependencies = [ [[package]] name = "libp2p-relay" -version = "0.17.2" +version = "0.18.0" dependencies = [ "asynchronous-codec", "bytes", @@ -3279,7 +3279,7 @@ dependencies = [ [[package]] name = "libp2p-rendezvous" -version = "0.14.0" +version = "0.15.0" dependencies = [ "async-trait", "asynchronous-codec", @@ -3309,7 +3309,7 @@ dependencies = [ [[package]] name = "libp2p-request-response" -version = "0.26.3" +version = "0.27.0" dependencies = [ "anyhow", "async-std", @@ -3358,7 +3358,7 @@ dependencies = [ [[package]] name = "libp2p-stream" -version = "0.1.0-alpha.1" +version = "0.2.0-alpha" dependencies = [ "futures", "libp2p-core 0.42.0", @@ -3374,7 +3374,7 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.44.2" +version = "0.45.0" dependencies = [ "async-std", "either", @@ -3435,7 +3435,7 @@ dependencies = [ [[package]] name = "libp2p-tcp" -version = "0.41.1" +version = "0.42.0" dependencies = [ "async-io 2.3.3", "async-std", @@ -3494,7 +3494,7 @@ dependencies = [ [[package]] name = "libp2p-uds" -version = "0.40.0" +version = "0.41.0" dependencies = [ "async-std", "futures", @@ -3506,7 +3506,7 @@ dependencies = [ [[package]] name = "libp2p-upnp" -version = "0.2.2" +version = "0.3.0" dependencies = [ "futures", "futures-timer", @@ -3520,7 +3520,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.7.1-alpha" +version = "0.8.0-alpha" dependencies = [ "async-trait", "bytes", @@ -3571,7 +3571,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc-websys" -version = "0.3.0-alpha" +version = "0.4.0-alpha" dependencies = [ "bytes", "futures", @@ -3591,7 +3591,7 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.43.0" +version = "0.44.0" dependencies = [ "async-std", "either", @@ -3613,7 +3613,7 @@ dependencies = [ [[package]] name = "libp2p-websocket-websys" -version = "0.3.2" +version = "0.4.0" dependencies = [ "bytes", "futures", @@ -3632,7 +3632,7 @@ dependencies = [ [[package]] name = "libp2p-webtransport-websys" -version = "0.3.0" +version = "0.4.0" dependencies = [ "futures", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 0731b9d48c1..5f9aba55a38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ libp2p-metrics = { version = "0.14.1", path = "misc/metrics" } libp2p-mplex = { version = "0.41.0", path = "muxers/mplex" } libp2p-noise = { version = "0.44.0", path = "transports/noise" } libp2p-perf = { version = "0.4.0", path = "protocols/perf" } -libp2p-ping = { version = "0.45.1", path = "protocols/ping" } +libp2p-ping = { version = "0.45.0", path = "protocols/ping" } libp2p-plaintext = { version = "0.41.0", path = "transports/plaintext" } libp2p-pnet = { version = "0.24.0", path = "transports/pnet" } libp2p-quic = { version = "0.11.0", path = "transports/quic" } diff --git a/examples/stream/Cargo.toml b/examples/stream/Cargo.toml index de46d204c77..ba31d4d9e13 100644 --- a/examples/stream/Cargo.toml +++ b/examples/stream/Cargo.toml @@ -12,7 +12,7 @@ release = false anyhow = "1" futures = { workspace = true } libp2p = { path = "../../libp2p", features = [ "tokio", "quic"] } -libp2p-stream = { path = "../../protocols/stream", version = "0.1.0-alpha" } +libp2p-stream = { path = "../../protocols/stream", version = "0.2.0-alpha" } rand = "0.8" tokio = { workspace = true, features = ["full"] } tracing = { workspace = true } From df2592bcf32988129940ee659f1235170ecdbb2b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:55:29 +0200 Subject: [PATCH 150/179] Correct bad merge --- transports/tcp/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 383515f3dc3..e2a1aac13d2 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -1028,7 +1028,6 @@ mod tests { #[test] fn port_reuse_listening() { - env_logger::try_init().ok(); let _ = tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .try_init(); From 75e8011beea89cfe541547d9e676054290184f0d Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:02:03 +0200 Subject: [PATCH 151/179] Apply suggestions from code review Co-authored-by: Thomas Eizinger --- core/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 505ebd5d919..14624be3054 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,7 +1,8 @@ ## 0.42.0 -- unreleased -- Refactor `Transport` to allow the behaviour to tell the implementor of `Transport` to reuse an -existing port or a new one. +- Introduce `DialOpts` to `Transport::dial` to: + a. remove `dial_as_listener` + b. introduce `PortReuse`, allowing callers to control port allocation of new connections See [PR 4568](https://https://github.com/libp2p/rust-libp2p/pull/4568) - Add new enum `PortUse`, defaulting on `PortUse::Reuse`. - Add new struct `DialOpts`, containing `PortUse` and `Endpoint`. From 6e0839df2cb76265aa37a4ecbbda14fc50bd905c Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:14:46 +0900 Subject: [PATCH 152/179] Address review comments --- Cargo.lock | 1208 +++++++++---------- core/CHANGELOG.md | 20 +- libp2p/CHANGELOG.md | 9 +- protocols/autonat/CHANGELOG.md | 9 +- protocols/dcutr/CHANGELOG.md | 5 +- protocols/floodsub/CHANGELOG.md | 5 +- protocols/gossipsub/CHANGELOG.md | 5 +- protocols/identify/CHANGELOG.md | 5 +- protocols/identify/src/behaviour.rs | 4 +- protocols/kad/CHANGELOG.md | 5 +- protocols/mdns/CHANGELOG.md | 5 +- protocols/mdns/src/behaviour/iface/query.rs | 4 +- protocols/perf/CHANGELOG.md | 5 +- protocols/ping/CHANGELOG.md | 4 +- protocols/relay/CHANGELOG.md | 5 +- protocols/rendezvous/CHANGELOG.md | 5 +- protocols/request-response/CHANGELOG.md | 5 +- protocols/stream/CHANGELOG.md | 5 +- protocols/upnp/CHANGELOG.md | 5 +- swarm/CHANGELOG.md | 4 +- swarm/src/lib.rs | 3 +- swarm/src/translation.rs | 5 +- transports/dns/CHANGELOG.md | 2 +- transports/quic/CHANGELOG.md | 2 +- transports/quic/src/transport.rs | 59 +- transports/tcp/CHANGELOG.md | 8 +- transports/uds/CHANGELOG.md | 2 +- transports/webrtc-websys/CHANGELOG.md | 2 +- transports/webrtc/CHANGELOG.md | 2 +- transports/websocket-websys/CHANGELOG.md | 2 +- transports/websocket/CHANGELOG.md | 2 +- transports/webtransport-websys/CHANGELOG.md | 2 +- 32 files changed, 676 insertions(+), 737 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38dd1949f71..33018213e5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ "gimli", ] @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "once_cell", @@ -66,18 +66,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "anes" @@ -87,51 +87,50 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -142,15 +141,15 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "arrayref" @@ -269,28 +268,17 @@ dependencies = [ "futures-core", ] -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - [[package]] name = "async-executor" -version = "1.12.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ + "async-lock 2.7.0", "async-task", "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", + "fastrand 1.9.0", + "futures-lite 1.13.0", "slab", ] @@ -300,7 +288,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "async-lock 2.8.0", + "async-lock 2.7.0", "autocfg", "blocking", "futures-lite 1.13.0", @@ -308,16 +296,16 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.4.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ - "async-channel 2.3.1", + "async-channel", "async-executor", - "async-io 2.3.3", - "async-lock 3.4.0", + "async-io 1.13.0", + "async-lock 2.7.0", "blocking", - "futures-lite 2.3.0", + "futures-lite 1.13.0", "once_cell", ] @@ -327,7 +315,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock 2.8.0", + "async-lock 2.7.0", "autocfg", "cfg-if", "concurrent-queue", @@ -335,9 +323,9 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.25", "slab", - "socket2 0.4.10", + "socket2 0.4.9", "waker-fn", ] @@ -347,14 +335,14 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 3.4.0", + "async-lock 3.1.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.0.1", "parking", - "polling 3.7.1", - "rustix 0.38.34", + "polling 3.3.0", + "rustix 0.38.31", "slab", "tracing", "windows-sys 0.52.0", @@ -362,70 +350,54 @@ dependencies = [ [[package]] name = "async-lock" -version = "2.8.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener 2.5.3", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "deb2ab2aa8a746e221ab826c73f48bc6ba41be6763f0855cb249eb6d154cf1d7" dependencies = [ - "event-listener 5.3.1", + "event-listener 3.1.0", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-net" -version = "1.8.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" +checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" dependencies = [ "async-io 1.13.0", + "autocfg", "blocking", "futures-lite 1.13.0", ] [[package]] name = "async-process" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ "async-io 1.13.0", - "async-lock 2.8.0", - "async-signal", + "async-lock 2.7.0", + "autocfg", "blocking", "cfg-if", - "event-listener 3.1.0", + "event-listener 2.5.3", "futures-lite 1.13.0", - "rustix 0.38.34", + "rustix 0.37.25", + "signal-hook", "windows-sys 0.48.0", ] -[[package]] -name = "async-signal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" -dependencies = [ - "async-io 2.3.3", - "async-lock 3.4.0", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 0.38.34", - "signal-hook-registry", - "slab", - "windows-sys 0.52.0", -] - [[package]] name = "async-std" version = "1.12.0" @@ -433,10 +405,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", - "async-channel 1.9.0", + "async-channel", "async-global-executor", "async-io 1.13.0", - "async-lock 2.8.0", + "async-lock 2.7.0", "async-process", "crossbeam-utils", "futures-channel", @@ -456,9 +428,9 @@ dependencies = [ [[package]] name = "async-std-resolver" -version = "0.24.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc3b454643291f9a4a3bbdb35fa62efa4ba7be5ea13fe243e3be4352182ff4b8" +checksum = "3c0ed2b6671c13d2c28756c5a64e04759c1e0b5d3d7ac031f521c3561e21fbcb" dependencies = [ "async-std", "async-trait", @@ -493,9 +465,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.7.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" @@ -523,9 +495,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "attohttpc" @@ -533,16 +505,16 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" dependencies = [ - "http 0.2.12", + "http 0.2.9", "log", "url", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "autonat-example" @@ -567,9 +539,9 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.29", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", "itoa", "matchit", "memchr", @@ -594,10 +566,10 @@ dependencies = [ "axum-core 0.4.3", "bytes", "futures-util", - "http 1.1.0", + "http 1.0.0", "http-body 1.0.0", "http-body-util", - "hyper 1.3.1", + "hyper 1.1.0", "hyper-util", "itoa", "matchit", @@ -610,7 +582,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.0", "tokio", "tower", "tower-layer", @@ -627,8 +599,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", + "http 0.2.9", + "http-body 0.4.5", "mime", "rustversion", "tower-layer", @@ -644,7 +616,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http 1.0.0", "http-body 1.0.0", "http-body-util", "mime", @@ -658,9 +630,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ "addr2line", "cc", @@ -730,9 +702,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "blake2" @@ -772,15 +744,17 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ - "async-channel 2.3.1", + "async-channel", + "async-lock 2.7.0", "async-task", - "futures-io", - "futures-lite 2.3.0", - "piper", + "atomic-waker", + "fastrand 1.9.0", + "futures-lite 1.13.0", + "log", ] [[package]] @@ -820,9 +794,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -830,9 +804,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -875,9 +849,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "ccm" @@ -935,9 +912,9 @@ dependencies = [ [[package]] name = "ciborium" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", @@ -946,15 +923,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", @@ -1013,15 +990,15 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "combine" -version = "4.6.7" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ "bytes", "futures-core", @@ -1033,9 +1010,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.5.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] @@ -1052,15 +1029,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.6" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -1068,9 +1045,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core2" @@ -1083,27 +1060,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] name = "crc" -version = "3.2.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "criterion" @@ -1117,7 +1094,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools 0.10.5", + "itertools", "num-traits", "once_cell", "oorandom", @@ -1138,33 +1115,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools 0.10.5", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ + "autocfg", + "cfg-if", "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] [[package]] name = "crunchy" @@ -1174,9 +1169,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1244,9 +1239,9 @@ dependencies = [ [[package]] name = "curve25519-dalek-derive" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", @@ -1261,9 +1256,9 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-encoding-macro" -version = "0.1.15" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1271,9 +1266,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.13" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" dependencies = [ "data-encoding", "syn 1.0.109", @@ -1295,9 +1290,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ "const-oid", "pem-rfc7468", @@ -1332,15 +1327,6 @@ dependencies = [ "rusticata-macros", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "digest" version = "0.9.0" @@ -1364,23 +1350,22 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "option-ext", "redox_users", - "windows-sys 0.48.0", + "winapi", ] [[package]] @@ -1414,9 +1399,9 @@ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "ecdsa" -version = "0.16.9" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der", "digest 0.10.7", @@ -1428,9 +1413,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.3" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ "pkcs8", "signature", @@ -1480,9 +1465,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] @@ -1530,9 +1515,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1555,24 +1540,13 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" dependencies = [ - "event-listener 5.3.1", + "event-listener 3.1.0", "pin-project-lite", ] @@ -1587,9 +1561,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "ff" @@ -1603,9 +1577,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" [[package]] name = "file-sharing-example" @@ -1668,9 +1642,9 @@ dependencies = [ [[package]] name = "futures-bounded" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91f328e7fb845fc832912fb6a34f40cf6d1888c92f974d1893a54e97b5ff542e" +checksum = "e1e2774cc104e198ef3d3e1ff4ab40f86fa3245d6cb6a3a46174f21463cee173" dependencies = [ "futures-timer", "futures-util", @@ -1727,14 +1701,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" dependencies = [ - "fastrand 2.1.0", "futures-core", - "futures-io", - "parking", "pin-project-lite", ] @@ -1756,7 +1727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.9", + "rustls 0.23.8", "rustls-pki-types", ] @@ -1860,9 +1831,9 @@ dependencies = [ [[package]] name = "ghash" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug", "polyval", @@ -1870,9 +1841,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "glob" @@ -1882,15 +1853,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ "aho-corasick", "bstr", + "fnv", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex", ] [[package]] @@ -1927,8 +1898,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.12", - "indexmap 2.2.6", + "http 0.2.9", + "indexmap 2.2.1", "slab", "tokio", "tokio-util", @@ -1937,17 +1908,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ - "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.2.6", + "futures-util", + "http 1.0.0", + "indexmap 2.2.1", "slab", "tokio", "tokio-util", @@ -1956,13 +1927,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" @@ -1972,9 +1939,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1994,9 +1961,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2018,9 +1985,9 @@ checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf" dependencies = [ "async-trait", "cfg-if", @@ -2043,9 +2010,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8" dependencies = [ "cfg-if", "futures-util", @@ -2130,9 +2097,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -2141,9 +2108,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" dependencies = [ "bytes", "fnv", @@ -2152,12 +2119,12 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http 0.2.12", + "http 0.2.9", "pin-project-lite", ] @@ -2168,27 +2135,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.1.0", + "http 1.0.0", ] [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", - "futures-core", - "http 1.1.0", + "futures-util", + "http 1.0.0", "http-body 1.0.0", "pin-project-lite", ] [[package]] name = "http-range-header" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" [[package]] name = "httparse" @@ -2198,9 +2165,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" @@ -2210,22 +2177,22 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.29" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", + "http 0.2.9", + "http-body 0.4.5", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2234,21 +2201,20 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", - "http 1.1.0", + "h2 0.4.4", + "http 1.0.0", "http-body 1.0.0", "httparse", "httpdate", "itoa", "pin-project-lite", - "smallvec", "tokio", "want", ] @@ -2260,8 +2226,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.3.1", + "http 1.0.0", + "hyper 1.1.0", "hyper-util", "rustls 0.22.4", "rustls-pki-types", @@ -2276,7 +2242,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.29", + "hyper 0.14.27", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -2290,7 +2256,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper 1.1.0", "hyper-util", "native-tls", "tokio", @@ -2300,16 +2266,16 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.5" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.0.0", "http-body 1.0.0", - "hyper 1.3.1", + "hyper 1.1.0", "pin-project-lite", "socket2 0.5.7", "tokio", @@ -2390,8 +2356,8 @@ dependencies = [ "attohttpc", "bytes", "futures", - "http 0.2.12", - "hyper 0.14.29", + "http 0.2.9", + "hyper 0.14.27", "log", "rand 0.8.5", "tokio", @@ -2411,12 +2377,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.14.3", ] [[package]] @@ -2452,7 +2418,7 @@ dependencies = [ "log", "rand 0.8.5", "rtcp", - "rtp 0.9.0", + "rtp", "thiserror", "tokio", "waitgroup", @@ -2547,27 +2513,21 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "libc", - "windows-sys 0.52.0", + "rustix 0.38.31", + "windows-sys 0.48.0", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" - [[package]] name = "itertools" version = "0.10.5" @@ -2577,20 +2537,11 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -2603,9 +2554,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -3240,7 +3191,7 @@ dependencies = [ "quinn", "rand 0.8.5", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.8", "socket2 0.5.7", "thiserror", "tokio", @@ -3465,7 +3416,7 @@ dependencies = [ "libp2p-yamux", "rcgen", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.8", "rustls-webpki 0.101.7", "thiserror", "tokio", @@ -3485,7 +3436,7 @@ dependencies = [ "libp2p-identity", "rcgen", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.8", "rustls-webpki 0.101.7", "thiserror", "x509-parser 0.16.0", @@ -3608,7 +3559,7 @@ dependencies = [ "soketto", "tracing", "url", - "webpki-roots 0.25.4", + "webpki-roots 0.25.2", ] [[package]] @@ -3662,17 +3613,7 @@ dependencies = [ "thiserror", "tracing", "yamux 0.12.1", - "yamux 0.13.2", -] - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.5.0", - "libc", + "yamux 0.13.1", ] [[package]] @@ -3737,15 +3678,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -3766,7 +3707,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.14.3", ] [[package]] @@ -3795,25 +3736,24 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "67827e6ea8ee8a7c4a72227ef4fc08957040acffdb5f122733b24fa12daff41b" [[package]] name = "md-5" -version = "0.10.6" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "cfg-if", "digest 0.10.7", ] [[package]] name = "memchr" -version = "2.7.2" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memoffset" @@ -3824,6 +3764,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "memory-stats" version = "1.1.0" @@ -3875,9 +3824,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] @@ -3971,10 +3920,11 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ + "lazy_static", "libc", "log", "openssl", @@ -4041,9 +3991,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.6" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "async-io 1.13.0", "bytes", @@ -4073,7 +4023,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", ] @@ -4114,26 +4064,22 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ + "autocfg", "num-integer", "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ + "autocfg", "num-traits", ] @@ -4158,9 +4104,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] @@ -4197,17 +4143,17 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opaque-debug" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -4235,9 +4181,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -4267,7 +4213,7 @@ checksum = "a94c69209c05319cdf7460c6d4c055ed102be242a0a6245835d7bc42c6ec7f54" dependencies = [ "async-trait", "futures-core", - "http 0.2.12", + "http 0.2.9", "opentelemetry", "opentelemetry-proto", "opentelemetry_sdk", @@ -4311,12 +4257,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "ordered-float" version = "4.2.0" @@ -4358,9 +4298,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -4374,30 +4314,30 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] name = "paste" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pem" -version = "3.0.4" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "3163d2912b7c3b52d651a055f2c7eec9ba5cd22d26ef75b8dd3a59980b185923" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "serde", ] @@ -4459,17 +4399,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "piper" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" -dependencies = [ - "atomic-waker", - "fastrand 2.1.0", - "futures-io", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -4482,21 +4411,21 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "platforms" -version = "3.4.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", @@ -4507,15 +4436,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] @@ -4538,17 +4467,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" +checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", "pin-project-lite", - "rustix 0.38.34", + "rustix 0.38.31", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -4564,9 +4492,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if", "cpufeatures", @@ -4574,12 +4502,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4588,9 +4510,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primeorder" -version = "0.13.6" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" dependencies = [ "elliptic-curve", ] @@ -4653,9 +4575,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.6" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", "prost-derive", @@ -4663,12 +4585,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools", "proc-macro2", "quote", "syn 2.0.66", @@ -4736,7 +4658,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.9", + "rustls 0.23.8", "thiserror", "tokio", "tracing", @@ -4752,7 +4674,7 @@ dependencies = [ "rand 0.8.5", "ring 0.17.8", "rustc-hash", - "rustls 0.23.9", + "rustls 0.23.8", "slab", "thiserror", "tinyvec", @@ -4761,9 +4683,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f0def2590301f4f667db5a77f9694fb004f82796dc1a8b1508fafa3d0e8b72" +checksum = "cb7ad7bc932e4968523fa7d9c320ee135ff779de720e9350fee8728838551764" dependencies = [ "libc", "once_cell", @@ -4854,9 +4776,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -4864,12 +4786,14 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ + "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", + "num_cpus", ] [[package]] @@ -4906,21 +4830,30 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 2.5.0", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.15", - "libredox", + "redox_syscall 0.2.16", "thiserror", ] @@ -4932,8 +4865,8 @@ checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.4", + "regex-syntax 0.8.2", ] [[package]] @@ -4947,13 +4880,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.2", ] [[package]] @@ -4964,9 +4897,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "relay-server-example" @@ -5005,11 +4938,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.1.0", + "h2 0.4.4", + "http 1.0.0", "http-body 1.0.0", "http-body-util", - "hyper 1.3.1", + "hyper 1.1.0", "hyper-rustls", "hyper-tls", "hyper-util", @@ -5124,9 +5057,9 @@ dependencies = [ [[package]] name = "rtcp" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33648a781874466a62d89e265fee9f17e32bc7d05a256e6cca41bf97eadcd8aa" +checksum = "3677908cadfbecb4cc1da9a56a32524fae4ebdfa7c2ea93886e1b1e846488cb9" dependencies = [ "bytes", "thiserror", @@ -5162,19 +5095,6 @@ dependencies = [ "webrtc-util", ] -[[package]] -name = "rtp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47fca9bd66ae0b1f3f649b8f5003d6176433d7293b78b0fce7e1031816bdd99d" -dependencies = [ - "bytes", - "rand 0.8.5", - "serde", - "thiserror", - "webrtc-util", -] - [[package]] name = "rust-embed" version = "8.4.0" @@ -5213,9 +5133,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -5243,9 +5163,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" dependencies = [ "bitflags 1.3.2", "errno", @@ -5257,22 +5177,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys 0.4.12", "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.21.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", "ring 0.17.8", @@ -5296,9 +5216,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "79adb16721f56eb2d843e67676896a61ce7a0fa622dc18d3e372477a029d2740" dependencies = [ "once_cell", "ring 0.17.8", @@ -5347,9 +5267,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "rw-stream-sink" @@ -5374,9 +5294,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa20" @@ -5398,11 +5318,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -5419,19 +5339,19 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "sdp" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13254db766b17451aced321e7397ebf0a446ef0c8d2942b6e67a95815421093f" +checksum = "4653054c30ebce63658762eb0d64e27673868a95564474811ae6c220cf767640" dependencies = [ "rand 0.8.5", "substring", @@ -5455,11 +5375,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags 2.5.0", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -5468,9 +5388,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -5478,9 +5398,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "send_wrapper" @@ -5523,7 +5443,7 @@ version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.2.1", "itoa", "ryu", "serde", @@ -5531,9 +5451,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.16" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" dependencies = [ "itoa", "serde", @@ -5541,9 +5461,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", @@ -5552,9 +5472,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -5573,9 +5493,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", @@ -5618,9 +5538,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.7" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", ] @@ -5634,20 +5554,30 @@ dependencies = [ "dirs", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "signature" -version = "2.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest 0.10.7", "rand_core 0.6.4", @@ -5655,9 +5585,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -5674,11 +5604,11 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" dependencies = [ - "async-channel 1.9.0", + "async-channel", "async-executor", "async-fs", "async-io 1.13.0", - "async-lock 2.8.0", + "async-lock 2.7.0", "async-net", "async-process", "blocking", @@ -5687,9 +5617,9 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.2.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" dependencies = [ "serde", ] @@ -5713,9 +5643,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -5760,9 +5690,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -5799,9 +5729,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "strum" @@ -5814,11 +5744,11 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.3" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -5889,9 +5819,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" [[package]] name = "synstructure" @@ -5959,16 +5889,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.1.0", - "rustix 0.38.34", + "fastrand 2.0.1", + "rustix 0.38.31", "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.4.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -5982,8 +5912,8 @@ dependencies = [ "async-trait", "base64 0.22.1", "futures", - "http 1.1.0", - "indexmap 2.2.6", + "http 1.0.0", + "indexmap 2.2.1", "parking_lot", "paste", "reqwest", @@ -6001,9 +5931,9 @@ dependencies = [ [[package]] name = "thirtyfour-macros" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c98f6ff7045a00ce7ef2fb2f5ebd5c3c6dfe8fc0c1858249e32555fa8059df" +checksum = "9cae91d1c7c61ec65817f1064954640ee350a50ae6548ff9a1bdd2489d6ffbb0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -6033,9 +5963,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ "cfg-if", "once_cell", @@ -6043,14 +5973,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ - "deranged", "itoa", - "num-conv", - "powerfmt", "serde", "time-core", "time-macros", @@ -6058,17 +5985,16 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ - "num-conv", "time-core", ] @@ -6160,9 +6086,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -6185,9 +6111,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" dependencies = [ "serde", "serde_spanned", @@ -6197,20 +6123,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.2.1", "serde", "serde_spanned", "toml_datetime", @@ -6229,9 +6155,9 @@ dependencies = [ "base64 0.21.7", "bytes", "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.29", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", "hyper-timeout", "percent-encoding", "pin-project", @@ -6270,10 +6196,10 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.4.1", "bytes", "futures-util", - "http 1.1.0", + "http 1.0.0", "http-body 1.0.0", "http-body-util", "http-range-header", @@ -6394,9 +6320,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "trybuild" @@ -6414,9 +6340,9 @@ dependencies = [ [[package]] name = "turn" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb2ac4f331064513ad510b7a36edc0df555bd61672986607f7c9ff46f98f415" +checksum = "58f4fcb97da0426e8146fe0e9b78cc13120161087256198701d12d9df77f7701" dependencies = [ "async-trait", "base64 0.21.7", @@ -6424,19 +6350,18 @@ dependencies = [ "log", "md-5", "rand 0.8.5", - "ring 0.17.8", + "ring 0.16.20", "stun", "thiserror", "tokio", - "tokio-util", "webrtc-util", ] [[package]] name = "typenum" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "uint" @@ -6452,30 +6377,30 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ "version_check", ] [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -6553,9 +6478,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.8.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.15", ] @@ -6568,9 +6493,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.9.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" [[package]] name = "vcpkg" @@ -6601,15 +6526,15 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.5.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", "winapi-util", @@ -6760,9 +6685,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "webpki-roots" @@ -6793,8 +6718,8 @@ dependencies = [ "regex", "ring 0.16.20", "rtcp", - "rtp 0.9.0", - "rustls 0.21.12", + "rtp", + "rustls 0.21.11", "sdp", "serde", "serde_json", @@ -6819,9 +6744,9 @@ dependencies = [ [[package]] name = "webrtc-data" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c08e648e10572b9edbe741074e0f4d3cb221aa7cdf9a814ee71606de312f33" +checksum = "a45d2461d0e0bf93f181e30eb0b40df32b8bf3efb89c53cebb1990e603e2067d" dependencies = [ "bytes", "log", @@ -6855,7 +6780,7 @@ dependencies = [ "rand_core 0.6.4", "rcgen", "ring 0.16.20", - "rustls 0.21.12", + "rustls 0.21.11", "sec1", "serde", "sha1", @@ -6870,9 +6795,9 @@ dependencies = [ [[package]] name = "webrtc-ice" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bbd6b3dea22cc6e961e22b012e843d8869e2ac8e76b96e54d4a25e311857ad" +checksum = "66eb4b85646f1c52225779db3e1e7e873dede6db68cc9be080b648f1713083a3" dependencies = [ "arc-swap", "async-trait", @@ -6894,9 +6819,9 @@ dependencies = [ [[package]] name = "webrtc-mdns" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce981f93104a8debb3563bb0cedfe4aa2f351fdf6b53f346ab50009424125c08" +checksum = "62bebbd40e7f8b630a0f1a74783dbfff1edfc0ccaae891c4689891156a8c4d8c" dependencies = [ "log", "socket2 0.5.7", @@ -6907,22 +6832,22 @@ dependencies = [ [[package]] name = "webrtc-media" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280017b6b9625ef7329146332518b339c3cceff231cc6f6a9e0e6acab25ca4af" +checksum = "1cfde3c7b9450b67d466bb2f02c6d9ff9514d33535eb9994942afd1f828839d1" dependencies = [ "byteorder", "bytes", "rand 0.8.5", - "rtp 0.10.0", + "rtp", "thiserror", ] [[package]] name = "webrtc-sctp" -version = "0.9.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df75ec042002fe995194712cbeb2029107a60a7eab646f1b789eb1be94d0e367" +checksum = "1af6116b7f9703560c3ad0b32f67220b171bb1b59633b03563db8404d0e482ea" dependencies = [ "arc-swap", "async-trait", @@ -6950,7 +6875,7 @@ dependencies = [ "hmac 0.12.1", "log", "rtcp", - "rtp 0.9.0", + "rtp", "sha1", "subtle", "thiserror", @@ -6998,9 +6923,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.1.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "winapi" @@ -7020,11 +6945,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "windows-sys 0.52.0", + "winapi", ] [[package]] @@ -7067,7 +6992,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.0", ] [[package]] @@ -7087,18 +7012,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -7109,9 +7033,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" @@ -7121,9 +7045,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" @@ -7133,15 +7057,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" @@ -7151,9 +7069,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" @@ -7163,9 +7081,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" @@ -7175,9 +7093,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" @@ -7187,15 +7105,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.6.11" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -7269,9 +7187,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.20" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +checksum = "1eee6bf5926be7cf998d7381a9a23d833fd493f6a8034658a9505a4dc4b20444" [[package]] name = "xmltree" @@ -7299,9 +7217,9 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f97202f6b125031b95d83e01dc57292b529384f80bfae4677e4bbc10178cf72" +checksum = "ad1d0148b89300047e72994bee99ecdabd15a9166a7b70c8b8c37c314dcc9002" dependencies = [ "futures", "instant", @@ -7324,18 +7242,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 14624be3054..c2b9ff3a854 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,29 +1,19 @@ -## 0.42.0 -- unreleased +## 0.42.0 - Introduce `DialOpts` to `Transport::dial` to: a. remove `dial_as_listener` b. introduce `PortReuse`, allowing callers to control port allocation of new connections - See [PR 4568](https://https://github.com/libp2p/rust-libp2p/pull/4568) + See [PR 4568] - Add new enum `PortUse`, defaulting on `PortUse::Reuse`. - Add new struct `DialOpts`, containing `PortUse` and `Endpoint`. - Add field `port_use` to `ConnectedPoint`. - Add new parameter to `Transport::dial`: `DialOpts`. - Remove `Transport::dial_as_listener` and move into `Transport::dial`. To dial as listener, one has to set the `endpoint` field in `DialOpts` to `Endpoint::Listener`. - - Adjust utility transports: - - `AndThen` - - `Boxed` - - `Choice` - - `Dummy` - - `GlobalOnly` - - `Map` - - `MapErr` - - `Memory` - - `Optional` - - `Timeout` - - `Upgrade` - Remove `Transport::address_translation` and move this functionality into `libp2p_swarm`. - See [umgefahren/libp2p PR 4](https://github.com/umgefahren/rust-libp2p/pull/4) + See [PR 4568]. + +[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.41.2 diff --git a/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md index a41586c6814..b976472863d 100644 --- a/libp2p/CHANGELOG.md +++ b/libp2p/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.54.0 -- unreleased +## 0.54.0 - Update individual crates. - Update to [`libp2p-kad` `v0.46.0`](protocols/kad/CHANGELOG.md#0460). @@ -7,7 +7,12 @@ See [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). - Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + See [PR 4568]. + +- Move `address_translation` from `libp2p-core` to `libp2p-swarm` and `libp2p-identify`. + See [PR 4568]. + +[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.53.2 diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index b75a76d4db8..5e1c4b02bbd 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -1,7 +1,10 @@ -## 0.13.0 -- unreleased +## 0.13.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) +- Due to the refactor of `Transport` it's no longer required to create a seperate transport for +AutoNAT where port reuse is disabled. This information is now passed by the behaviour. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568). + + ## 0.12.0 diff --git a/protocols/dcutr/CHANGELOG.md b/protocols/dcutr/CHANGELOG.md index fcdd4d60bd2..030142b46f3 100644 --- a/protocols/dcutr/CHANGELOG.md +++ b/protocols/dcutr/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.12.0 -- unreleased +## 0.12.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.11.0 diff --git a/protocols/floodsub/CHANGELOG.md b/protocols/floodsub/CHANGELOG.md index e741d5aef35..4192e0ea58d 100644 --- a/protocols/floodsub/CHANGELOG.md +++ b/protocols/floodsub/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.45.0 -- unreleased +## 0.45.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.44.0 diff --git a/protocols/gossipsub/CHANGELOG.md b/protocols/gossipsub/CHANGELOG.md index e1059b13bf3..f9c88246f4c 100644 --- a/protocols/gossipsub/CHANGELOG.md +++ b/protocols/gossipsub/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.47.0 -- unreleased +## 0.47.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.46.1 diff --git a/protocols/identify/CHANGELOG.md b/protocols/identify/CHANGELOG.md index b9448f71b4e..ff6aca8e09f 100644 --- a/protocols/identify/CHANGELOG.md +++ b/protocols/identify/CHANGELOG.md @@ -1,7 +1,8 @@ -## 0.45.0 -- unreleased +## 0.45.0 -- Implement refactored `Transport`. +- Address translation is moved here from `libp2p-core`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.44.2 diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 10cd338e543..dd840df6ff0 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -27,7 +27,7 @@ use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p_swarm::{ - address_translation, ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, + _address_translation, ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, }; use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent}; @@ -284,7 +284,7 @@ impl Behaviour { || (is_quic_addr(server, true) && is_quic_addr(observed, true)) || (is_quic_addr(server, false) && is_quic_addr(observed, false)) { - address_translation(server, observed) + _address_translation(server, observed) } else { None } diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 20301689db9..96e0589ee03 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.46.0 -- unreleased +## 0.46.0 - Changed `FIND_NODE` response: now includes a list of closest peers when querying the recipient peer ID. Previously, this request yielded an empty response. See [PR 5270](https://github.com/libp2p/rust-libp2p/pull/5270) @@ -17,8 +17,7 @@ See [PR 5148](https://github.com/libp2p/rust-libp2p/pull/5148). - Derive `Copy` for `kbucket::key::Key`. See [PR 5317](https://github.com/libp2p/rust-libp2p/pull/5317). -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.45.3 diff --git a/protocols/mdns/CHANGELOG.md b/protocols/mdns/CHANGELOG.md index 44db763adf6..4a94dfb8a83 100644 --- a/protocols/mdns/CHANGELOG.md +++ b/protocols/mdns/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.46.0 -- unreleased +## 0.46.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.45.1 diff --git a/protocols/mdns/src/behaviour/iface/query.rs b/protocols/mdns/src/behaviour/iface/query.rs index f28c39fb245..70b84816d0f 100644 --- a/protocols/mdns/src/behaviour/iface/query.rs +++ b/protocols/mdns/src/behaviour/iface/query.rs @@ -25,7 +25,7 @@ use hickory_proto::{ rr::{Name, RData}, }; use libp2p_core::multiaddr::{Multiaddr, Protocol}; -use libp2p_swarm::address_translation; +use libp2p_swarm::_address_translation; use libp2p_identity::PeerId; use std::time::Instant; @@ -178,7 +178,7 @@ impl MdnsResponse { let new_expiration = now + peer.ttl(); peer.addresses().iter().filter_map(move |address| { - let new_addr = address_translation(address, &observed)?; + let new_addr = _address_translation(address, &observed)?; let new_addr = new_addr.with_p2p(*peer.id()).ok()?; Some((*peer.id(), new_addr, new_expiration)) diff --git a/protocols/perf/CHANGELOG.md b/protocols/perf/CHANGELOG.md index 22066844e34..40234d6f6a3 100644 --- a/protocols/perf/CHANGELOG.md +++ b/protocols/perf/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.4.0 -- unreleased +## 0.4.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.3.0 diff --git a/protocols/ping/CHANGELOG.md b/protocols/ping/CHANGELOG.md index b1609d2a70d..f9a43ebd678 100644 --- a/protocols/ping/CHANGELOG.md +++ b/protocols/ping/CHANGELOG.md @@ -3,11 +3,9 @@ - Impose `Sync` on `ping::Failure::Other`. `ping::Event` can now be shared between threads. See [PR 5250] -- Implement refactored `Transport`. - See [PR 4568] + [PR 5250]: https://github.com/libp2p/rust-libp2p/pull/5250 -[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.44.0 diff --git a/protocols/relay/CHANGELOG.md b/protocols/relay/CHANGELOG.md index 830ccbd16b1..353b03790e3 100644 --- a/protocols/relay/CHANGELOG.md +++ b/protocols/relay/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.18.0 -- unreleased +## 0.18.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.17.2 diff --git a/protocols/rendezvous/CHANGELOG.md b/protocols/rendezvous/CHANGELOG.md index 9f0404cd101..f7c1d2dcba1 100644 --- a/protocols/rendezvous/CHANGELOG.md +++ b/protocols/rendezvous/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.15.0 -- unreleased +## 0.15.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.14.0 diff --git a/protocols/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index ff00112eb28..349bfdb94e1 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.27.0 -- unreleased +## 0.27.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.26.3 diff --git a/protocols/stream/CHANGELOG.md b/protocols/stream/CHANGELOG.md index a2123ccdb38..2532970d3c6 100644 --- a/protocols/stream/CHANGELOG.md +++ b/protocols/stream/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.2.0-alpha -- unreleased +## 0.2.0-alpha -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.1.0-alpha.1 - Implement Error for `OpenStreamError`. diff --git a/protocols/upnp/CHANGELOG.md b/protocols/upnp/CHANGELOG.md index d6a2e0fc29f..21e90f9534b 100644 --- a/protocols/upnp/CHANGELOG.md +++ b/protocols/upnp/CHANGELOG.md @@ -1,7 +1,6 @@ -## 0.3.0 -- unreleased +## 0.3.0 -- Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + ## 0.2.2 - Fix a panic caused when `upnp::Gateway` is dropped and its events queue receiver is no longer diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index e936a0a060a..af0413592a7 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -1,8 +1,8 @@ -## 0.45.0 -- unreleased +## 0.45.0 - Implement refactored `Transport`. See [PR 4568] -- Move `address_translation` into swarm. +- Move `address_translation` into swarm and into `libp2p-identify`. See [PR 4568] [PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index b3effa4fef5..7d0360646d6 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -154,7 +154,8 @@ use std::{ task::{Context, Poll}, }; use tracing::Instrument; -pub use translation::address_translation; +#[doc(hidden)] +pub use translation::_address_translation; /// Event generated by the [`NetworkBehaviour`] that the swarm will report back. type TBehaviourOutEvent = ::ToSwarm; diff --git a/swarm/src/translation.rs b/swarm/src/translation.rs index fd448bfaf23..baa80c907b5 100644 --- a/swarm/src/translation.rs +++ b/swarm/src/translation.rs @@ -35,7 +35,8 @@ use libp2p_core::{multiaddr::Protocol, Multiaddr}; /// address and vice versa. /// /// If the first [`Protocol`]s are not IP addresses, `None` is returned instead. -pub fn address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option { +#[doc(hidden)] +pub fn _address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option { original.replace(0, move |proto| match proto { Protocol::Ip4(_) | Protocol::Ip6(_) @@ -106,7 +107,7 @@ mod tests { for test in tests.iter() { assert_eq!( - address_translation(&test.original, &test.observed), + _address_translation(&test.original, &test.observed), Some(test.expected.clone()) ); } diff --git a/transports/dns/CHANGELOG.md b/transports/dns/CHANGELOG.md index 0ea613a2455..e4f951f157f 100644 --- a/transports/dns/CHANGELOG.md +++ b/transports/dns/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.42.0 -- unreleased +## 0.42.0 - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/quic/CHANGELOG.md b/transports/quic/CHANGELOG.md index 9ea5178fc27..2593af605df 100644 --- a/transports/quic/CHANGELOG.md +++ b/transports/quic/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.11.0 -- unreleased +## 0.11.0 - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 5887102e6e2..b016a4c78bd 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -197,6 +197,21 @@ impl GenTransport

{ Ok(socket.into()) } + + fn binded_socket(&mut self, socket_addr: SocketAddr) -> Result { + let socket_family = socket_addr.ip().into(); + if let Some(waker) = self.waker.take() { + waker.wake(); + } + let listen_socket_addr = match socket_family { + SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), + }; + let socket = UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; + let endpoint_config = self.quinn_config.endpoint_config.clone(); + let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; + Ok(endpoint) + } } impl Transport for GenTransport

{ @@ -259,13 +274,19 @@ impl Transport for GenTransport

{ match (dial_opts.role, dial_opts.port_use) { (Endpoint::Dialer, _) | (Endpoint::Listener, PortUse::Reuse) => { - let endpoint = match self.eligible_listener(&socket_addr) { - None => { - // No listener. Get or create an explicit dialer. - let socket_family = socket_addr.ip().into(); - let dialer = match (dial_opts.port_use, self.dialer.entry(socket_family)) { - (PortUse::Reuse, Entry::Occupied(occupied)) => occupied.get().clone(), - (_, entry) => { + let endpoint = if let Some(listener) = dial_opts + .port_use + .eq(&PortUse::Reuse) + .then(|| self.eligible_listener(&socket_addr)) + .flatten() + { + listener.endpoint.clone() + } else { + let socket_family = socket_addr.ip().into(); + let dialer = if dial_opts.port_use == PortUse::Reuse { + match self.dialer.entry(socket_family) { + Entry::Occupied(occupied) => occupied.get().clone(), + Entry::Vacant(entry) => { if let Some(waker) = self.waker.take() { waker.wake(); } @@ -281,18 +302,26 @@ impl Transport for GenTransport

{ .map_err(Self::Error::from)?; let endpoint_config = self.quinn_config.endpoint_config.clone(); let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; - - if dial_opts.port_use == PortUse::Reuse { - if let Entry::Vacant(vacant) = entry { - vacant.insert(endpoint.clone()); - } + if let Entry::Vacant(vacant) = entry { + vacant.insert(endpoint.clone()); } endpoint } + } + } else { + if let Some(waker) = self.waker.take() { + waker.wake(); + } + let listen_socket_addr = match socket_family { + SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; - dialer - } - Some(listener) => listener.endpoint.clone(), + let socket = + UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; + let endpoint_config = self.quinn_config.endpoint_config.clone(); + Self::new_endpoint(endpoint_config, None, socket)? + }; + dialer }; let handshake_timeout = self.handshake_timeout; let mut client_config = self.quinn_config.client_config.clone(); diff --git a/transports/tcp/CHANGELOG.md b/transports/tcp/CHANGELOG.md index bbc76ba5c4b..6171d9f7d4b 100644 --- a/transports/tcp/CHANGELOG.md +++ b/transports/tcp/CHANGELOG.md @@ -1,7 +1,11 @@ -## 0.42.0 -- unreleased +## 0.42.0 - Implement refactored `Transport`. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + See [PR 4568] +- Deprecate `port_reuse` setting, as this is now decided by the behaviour, not the transport. + See [PR 4568] + +[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.41.1 diff --git a/transports/uds/CHANGELOG.md b/transports/uds/CHANGELOG.md index 8f132f528b0..aa068fe3877 100644 --- a/transports/uds/CHANGELOG.md +++ b/transports/uds/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.41.0 -- unreleased +## 0.41.0 - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/webrtc-websys/CHANGELOG.md b/transports/webrtc-websys/CHANGELOG.md index df8e3df8cba..475b13727e6 100644 --- a/transports/webrtc-websys/CHANGELOG.md +++ b/transports/webrtc-websys/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.4.0-alpha -- unreleased +## 0.4.0-alpha - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/webrtc/CHANGELOG.md b/transports/webrtc/CHANGELOG.md index 7198d1d66a1..90d4ce83df3 100644 --- a/transports/webrtc/CHANGELOG.md +++ b/transports/webrtc/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.8.0-alpha -- unreleased +## 0.8.0-alpha - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/websocket-websys/CHANGELOG.md b/transports/websocket-websys/CHANGELOG.md index 6e8a5afa6e3..a8e12764cf2 100644 --- a/transports/websocket-websys/CHANGELOG.md +++ b/transports/websocket-websys/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.4.0 -- unreleased +## 0.4.0 - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/websocket/CHANGELOG.md b/transports/websocket/CHANGELOG.md index e243aae0927..41a891b7c92 100644 --- a/transports/websocket/CHANGELOG.md +++ b/transports/websocket/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.44.0 -- unreleased +## 0.44.0 - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) diff --git a/transports/webtransport-websys/CHANGELOG.md b/transports/webtransport-websys/CHANGELOG.md index 045c3e49e9b..2aab226ab12 100644 --- a/transports/webtransport-websys/CHANGELOG.md +++ b/transports/webtransport-websys/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.4.0 -- unreleased +## 0.4.0 - Implement refactored `Transport`. See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) From 158c0175d4826c4eaf408f2989b6f6338c247902 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:03:11 +0900 Subject: [PATCH 153/179] Remove HEAD --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9345a08fd7f..13bd301f322 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,6 @@ libp2p = { version = "0.54.0", path = "libp2p" } libp2p-allow-block-list = { version = "0.3.0", path = "misc/allow-block-list" } libp2p-autonat = { version = "0.13.0", path = "protocols/autonat" } libp2p-connection-limits = { version = "0.3.1", path = "misc/connection-limits" } -<<<<<<< HEAD libp2p-core = { version = "0.42.0", path = "core" } libp2p-dcutr = { version = "0.12.0", path = "protocols/dcutr" } libp2p-dns = { version = "0.42.0", path = "transports/dns" } From 1dc9f4b76e42434540a653363a1e1cda374a40e8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:04:26 +0900 Subject: [PATCH 154/179] Format --- protocols/identify/src/behaviour.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index dd840df6ff0..abfade189e8 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -27,8 +27,9 @@ use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p_swarm::{ - _address_translation, ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, - NetworkBehaviour, NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, + ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, + NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, + _address_translation, }; use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent}; From 3dbc6c950b89abcda04d7f6fbfdaab995e834477 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 9 Jun 2024 18:30:58 +0900 Subject: [PATCH 155/179] Make compilable --- Cargo.lock | 48 ++++++++++++++++---------------- transports/quic/src/transport.rs | 43 ++++++---------------------- 2 files changed, 32 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7366606fe15..1609ea15c7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2659,7 +2659,7 @@ dependencies = [ [[package]] name = "libp2p-autonat" -version = "0.12.1" +version = "0.13.0" dependencies = [ "async-std", "async-trait", @@ -2698,7 +2698,7 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.41.3" +version = "0.42.0" dependencies = [ "async-std", "either", @@ -2729,7 +2729,7 @@ dependencies = [ [[package]] name = "libp2p-dcutr" -version = "0.11.1" +version = "0.12.0" dependencies = [ "async-std", "asynchronous-codec", @@ -2763,7 +2763,7 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.41.1" +version = "0.42.0" dependencies = [ "async-std", "async-std-resolver", @@ -2781,7 +2781,7 @@ dependencies = [ [[package]] name = "libp2p-floodsub" -version = "0.44.0" +version = "0.45.0" dependencies = [ "asynchronous-codec", "bytes", @@ -2801,7 +2801,7 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" -version = "0.46.2" +version = "0.47.0" dependencies = [ "async-std", "asynchronous-codec", @@ -2838,7 +2838,7 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.44.2" +version = "0.45.0" dependencies = [ "async-std", "asynchronous-codec", @@ -2926,7 +2926,7 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.45.1" +version = "0.46.0" dependencies = [ "async-io 2.3.3", "async-std", @@ -3051,7 +3051,7 @@ dependencies = [ [[package]] name = "libp2p-perf" -version = "0.3.1" +version = "0.4.0" dependencies = [ "anyhow", "clap", @@ -3081,7 +3081,7 @@ dependencies = [ [[package]] name = "libp2p-ping" -version = "0.44.2" +version = "0.45.0" dependencies = [ "async-std", "either", @@ -3140,7 +3140,7 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.10.3" +version = "0.11.0" dependencies = [ "async-std", "bytes", @@ -3169,7 +3169,7 @@ dependencies = [ [[package]] name = "libp2p-relay" -version = "0.17.3" +version = "0.18.0" dependencies = [ "asynchronous-codec", "bytes", @@ -3198,7 +3198,7 @@ dependencies = [ [[package]] name = "libp2p-rendezvous" -version = "0.14.1" +version = "0.15.0" dependencies = [ "async-trait", "asynchronous-codec", @@ -3228,7 +3228,7 @@ dependencies = [ [[package]] name = "libp2p-request-response" -version = "0.26.4" +version = "0.27.0" dependencies = [ "anyhow", "async-std", @@ -3277,7 +3277,7 @@ dependencies = [ [[package]] name = "libp2p-stream" -version = "0.1.0-alpha.1" +version = "0.2.0-alpha" dependencies = [ "futures", "libp2p-core", @@ -3293,7 +3293,7 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.44.3" +version = "0.45.0" dependencies = [ "async-std", "either", @@ -3354,7 +3354,7 @@ dependencies = [ [[package]] name = "libp2p-tcp" -version = "0.41.1" +version = "0.42.0" dependencies = [ "async-io 2.3.3", "async-std", @@ -3394,7 +3394,7 @@ dependencies = [ [[package]] name = "libp2p-uds" -version = "0.40.0" +version = "0.41.0" dependencies = [ "async-std", "futures", @@ -3406,7 +3406,7 @@ dependencies = [ [[package]] name = "libp2p-upnp" -version = "0.2.2" +version = "0.3.0" dependencies = [ "futures", "futures-timer", @@ -3420,7 +3420,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.7.1-alpha" +version = "0.8.0-alpha" dependencies = [ "async-trait", "bytes", @@ -3471,7 +3471,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc-websys" -version = "0.3.0-alpha" +version = "0.4.0-alpha" dependencies = [ "bytes", "futures", @@ -3491,7 +3491,7 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.43.1" +version = "0.44.0" dependencies = [ "async-std", "either", @@ -3513,7 +3513,7 @@ dependencies = [ [[package]] name = "libp2p-websocket-websys" -version = "0.3.2" +version = "0.4.0" dependencies = [ "bytes", "futures", @@ -3532,7 +3532,7 @@ dependencies = [ [[package]] name = "libp2p-webtransport-websys" -version = "0.3.0" +version = "0.4.0" dependencies = [ "futures", "js-sys", diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index b016a4c78bd..9aa51140c90 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -207,7 +207,7 @@ impl GenTransport

{ SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; - let socket = UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; + let socket = UdpSocket::bind(listen_socket_addr)?; let endpoint_config = self.quinn_config.endpoint_config.clone(); let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; Ok(endpoint) @@ -284,42 +284,15 @@ impl Transport for GenTransport

{ } else { let socket_family = socket_addr.ip().into(); let dialer = if dial_opts.port_use == PortUse::Reuse { - match self.dialer.entry(socket_family) { - Entry::Occupied(occupied) => occupied.get().clone(), - Entry::Vacant(entry) => { - if let Some(waker) = self.waker.take() { - waker.wake(); - } - let listen_socket_addr = match socket_family { - SocketFamily::Ipv4 => { - SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0) - } - SocketFamily::Ipv6 => { - SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0) - } - }; - let socket = UdpSocket::bind(listen_socket_addr) - .map_err(Self::Error::from)?; - let endpoint_config = self.quinn_config.endpoint_config.clone(); - let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; - if let Entry::Vacant(vacant) = entry { - vacant.insert(endpoint.clone()); - } - endpoint - } + if let Some(occupied) = self.dialer.get(&socket_family) { + occupied.clone() + } else { + let endpoint = self.binded_socket(socket_addr)?; + self.dialer.insert(socket_family, endpoint.clone()); + endpoint } } else { - if let Some(waker) = self.waker.take() { - waker.wake(); - } - let listen_socket_addr = match socket_family { - SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), - }; - let socket = - UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; - let endpoint_config = self.quinn_config.endpoint_config.clone(); - Self::new_endpoint(endpoint_config, None, socket)? + self.binded_socket(socket_addr)? }; dialer }; From 0b20c211518843737305c7ad91c95faa87e18a04 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 9 Jun 2024 18:39:17 +0900 Subject: [PATCH 156/179] Correct the webrtc-utils version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 13bd301f322..510309b7850 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,7 +109,7 @@ libp2p-tls = { version = "0.4.0", path = "transports/tls" } libp2p-uds = { version = "0.41.0", path = "transports/uds" } libp2p-upnp = { version = "0.3.0", path = "protocols/upnp" } libp2p-webrtc = { version = "0.8.0-alpha", path = "transports/webrtc" } -libp2p-webrtc-utils = { version = "0.2.0", path = "misc/webrtc-utils" } +libp2p-webrtc-utils = { version = "0.2.1", path = "misc/webrtc-utils" } libp2p-webrtc-websys = { version = "0.4.0-alpha", path = "transports/webrtc-websys" } libp2p-websocket = { version = "0.44.0", path = "transports/websocket" } libp2p-websocket-websys = { version = "0.4.0", path = "transports/websocket-websys" } From 7fc55464e0828c45ef9f5ebd3f362beabb9b79ee Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:47:17 +0900 Subject: [PATCH 157/179] Address @thomaseizinger comments --- libp2p/CHANGELOG.md | 1842 ++++++++++++++++++++++++------------------- swarm/src/lib.rs | 2 +- 2 files changed, 1027 insertions(+), 817 deletions(-) diff --git a/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md index 1d82dd393eb..22d2bce439a 100644 --- a/libp2p/CHANGELOG.md +++ b/libp2p/CHANGELOG.md @@ -1,64 +1,69 @@ ## 0.54.0 -- Update individual crates. - - Update to [`libp2p-kad` `v0.46.0`](protocols/kad/CHANGELOG.md#0460). +- Update individual crates. -- Raise MSRV to 1.73. - See [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). + - Update to [`libp2p-kad` `v0.46.0`](protocols/kad/CHANGELOG.md#0460). -- Implement refactored `Transport`. - See [PR 4568]. +- Raise MSRV to 1.73. See + [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). -- Move `address_translation` from `libp2p-core` to `libp2p-swarm` and `libp2p-identify`. - See [PR 4568]. +- Implement refactored `Transport`. See [PR 4568]. -- Use `web-time` instead of `instant`. - See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). +- Move `address_translation` from `libp2p-core` to `libp2p-swarm` and + `libp2p-identify`. To now get address translation behaviour, i.e. discovery of + extern address (candidates) based on connecting to other peers, one needs to + use `libp2p-identify` now. This pertains to you if your nodes can be behind + NATs and they aren't aware of their true external address. See [PR 4568]. + +- Use `web-time` instead of `instant`. See + [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). [PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.53.2 -- Allow `SwarmBuilder::with_bandwidth_metrics` after `SwarmBuilder::with_websocket`. - See [PR 4937](https://github.com/libp2p/rust-libp2p/pull/4937). +- Allow `SwarmBuilder::with_bandwidth_metrics` after + `SwarmBuilder::with_websocket`. See + [PR 4937](https://github.com/libp2p/rust-libp2p/pull/4937). ## 0.53.1 -- Allow `SwarmBuilder::with_quic_config` to be called without `with_tcp` first. - See [PR 4821](https://github.com/libp2p/rust-libp2p/pull/4821). -- Introduce `SwarmBuilder::with_dns_config`. - See [PR 4808](https://github.com/libp2p/rust-libp2p/pull/4808). +- Allow `SwarmBuilder::with_quic_config` to be called without `with_tcp` first. + See [PR 4821](https://github.com/libp2p/rust-libp2p/pull/4821). +- Introduce `SwarmBuilder::with_dns_config`. See + [PR 4808](https://github.com/libp2p/rust-libp2p/pull/4808). ## 0.53.0 -- Raise MSRV to 1.73. - See [PR 4692](https://github.com/libp2p/rust-libp2p/pull/4692). -- Remove deprecated `libp2p-wasm-ext`. - Users should use `libp2p-websocket-websys` instead. - See [PR 4694](https://github.com/libp2p/rust-libp2p/pull/4694). -- Remove deprecated `libp2p-deflate`. - See [issue 4522](https://github.com/libp2p/rust-libp2p/issues/4522) for details. - See [PR 4729](https://github.com/libp2p/rust-libp2p/pull/4729). -- Remove deprecated `development_transport`. - Use `libp2p::SwarmBuilder` instead. - See [PR 4732](https://github.com/libp2p/rust-libp2p/pull/4732). -- Introduce `SwarmBuilder::with_bandwidth_metrics` exposing Prometheus bandwidth metrics per transport protocol stack and direction (in-/ outbound). - Deprecate `Transport::with_bandwidth_logging` and `SwarmBuilder::with_bandwidth_logging` in favor of the new `SwarmBuilder::with_bandwidth_metrics`. - See [PR 4727](https://github.com/libp2p/rust-libp2p/pull/4727). +- Raise MSRV to 1.73. See + [PR 4692](https://github.com/libp2p/rust-libp2p/pull/4692). +- Remove deprecated `libp2p-wasm-ext`. Users should use + `libp2p-websocket-websys` instead. See + [PR 4694](https://github.com/libp2p/rust-libp2p/pull/4694). +- Remove deprecated `libp2p-deflate`. See + [issue 4522](https://github.com/libp2p/rust-libp2p/issues/4522) for details. + See [PR 4729](https://github.com/libp2p/rust-libp2p/pull/4729). +- Remove deprecated `development_transport`. Use `libp2p::SwarmBuilder` instead. + See [PR 4732](https://github.com/libp2p/rust-libp2p/pull/4732). +- Introduce `SwarmBuilder::with_bandwidth_metrics` exposing Prometheus bandwidth + metrics per transport protocol stack and direction (in-/ outbound). Deprecate + `Transport::with_bandwidth_logging` and `SwarmBuilder::with_bandwidth_logging` + in favor of the new `SwarmBuilder::with_bandwidth_metrics`. See + [PR 4727](https://github.com/libp2p/rust-libp2p/pull/4727). ## 0.52.4 -- Introduce `libp2p::websocket_websys` module behind `websocket-websys` feature flag. - This supersedes the existing `libp2p::wasm_ext` module which is now deprecated. - See [PR 3679]. +- Introduce `libp2p::websocket_websys` module behind `websocket-websys` feature + flag. This supersedes the existing `libp2p::wasm_ext` module which is now + deprecated. See [PR 3679]. -- Introduce a new `libp2p::SwarmBuilder` in favor of the now deprecated `libp2p::swarm::SwarmBuilder`. - See `libp2p::SwarmBuilder` docs on how to use the new builder. - Also see [PR 4120]. +- Introduce a new `libp2p::SwarmBuilder` in favor of the now deprecated + `libp2p::swarm::SwarmBuilder`. See `libp2p::SwarmBuilder` docs on how to use + the new builder. Also see [PR 4120]. -- Update `libp2p-identity` version to 0.2.6. - Under the hood, we feature-flagged `libp2p-identity`'s `rand` dependency but it is enabled by default when using `libp2p`. - See [PR 4349]. +- Update `libp2p-identity` version to 0.2.6. Under the hood, we feature-flagged + `libp2p-identity`'s `rand` dependency but it is enabled by default when using + `libp2p`. See [PR 4349]. [PR 3679]: https://github.com/libp2p/rust-libp2p/pull/3679 [PR 4120]: https://github.com/libp2p/rust-libp2p/pull/4120 @@ -66,21 +71,18 @@ ## 0.52.3 -- Add `libp2p-quic` stable release. +- Add `libp2p-quic` stable release. ## 0.52.2 -- Include gossipsub when compiling for wasm. - See [PR 4217]. +- Include gossipsub when compiling for wasm. See [PR 4217]. -- Add `json` feature which exposes `request_response::json`. - See [PR 4188]. +- Add `json` feature which exposes `request_response::json`. See [PR 4188]. -- Add support for UPnP via the IGD protocol. - See [PR 4156]. +- Add support for UPnP via the IGD protocol. See [PR 4156]. -- Add `libp2p-memory-connection-limits` providing memory usage based connection limit configurations. - See [PR 4281]. +- Add `libp2p-memory-connection-limits` providing memory usage based connection + limit configurations. See [PR 4281]. [PR 4188]: https://github.com/libp2p/rust-libp2p/pull/4188 [PR 4156]: https://github.com/libp2p/rust-libp2p/pull/4156 @@ -89,36 +91,37 @@ ## 0.52.1 -- Add `libp2p-webtransport-websys` providing WebTransport for WASM environments. - See [PR 4015]. +- Add `libp2p-webtransport-websys` providing WebTransport for WASM environments. + See [PR 4015]. [PR 4015]: https://github.com/libp2p/rust-libp2p/pull/4015 ## 0.52.0 -- Raise MSRV to 1.65. - See [PR 3715]. +- Raise MSRV to 1.65. See [PR 3715]. -- Protocol names are now required to be valid UTF8 strings. - We delete the `ProtocolName` trait from `libp2p::core` and replace it with a requirement for `AsRef`. - At the same time, we introduce `StreamProtocol`, a newtype in `libp2p::swarm`. - This newtype enforces additional variants like a leading forward-slash. - We encourage users to use `StreamProtocol` when implementing `UpgradeInfo`. - See [PR 3746]. +- Protocol names are now required to be valid UTF8 strings. We delete the + `ProtocolName` trait from `libp2p::core` and replace it with a requirement for + `AsRef`. At the same time, we introduce `StreamProtocol`, a newtype in + `libp2p::swarm`. This newtype enforces additional variants like a leading + forward-slash. We encourage users to use `StreamProtocol` when implementing + `UpgradeInfo`. See [PR 3746]. -- Rename `NetworkBehaviour::OutEvent` to `NetworkBehaviour::ToSwarm`, `ConnectionHandler::InEvent` to `ConnectionHandler::FromBehaviour`, `ConnectionHandler::OutEvent` to `ConnectionHandler::ToBehaviour`. See [PR 3848]. +- Rename `NetworkBehaviour::OutEvent` to `NetworkBehaviour::ToSwarm`, + `ConnectionHandler::InEvent` to `ConnectionHandler::FromBehaviour`, + `ConnectionHandler::OutEvent` to `ConnectionHandler::ToBehaviour`. See + [PR 3848]. -- Remove deprecated `mplex` module. - You can still depend on `libp2p-mplex` directly but we strongly encourage to migrate to `yamux`. - This also removes `mplex` from the `development_transport` and `tokio_development_transport` functions. - See [PR 3920]. +- Remove deprecated `mplex` module. You can still depend on `libp2p-mplex` + directly but we strongly encourage to migrate to `yamux`. This also removes + `mplex` from the `development_transport` and `tokio_development_transport` + functions. See [PR 3920]. -- Remove `libp2p-perf` protocol. To use `libp2p-perf` one needs to import it directly. - See [PR 3990]. +- Remove `libp2p-perf` protocol. To use `libp2p-perf` one needs to import it + directly. See [PR 3990]. -- Remove `libp2p-quic` and `libp2p-webrtc` protocols. - These are in alpha status and should be depended on directly. - See [PR 4041]. +- Remove `libp2p-quic` and `libp2p-webrtc` protocols. These are in alpha status + and should be depended on directly. See [PR 4041]. [PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715 [PR 3746]: https://github.com/libp2p/rust-libp2p/pull/3746 @@ -129,27 +132,23 @@ ## 0.51.3 -- Deprecate the `mplex` feature. - The recommended baseline stream multiplexer is `yamux`. - See [PR 3689]. +- Deprecate the `mplex` feature. The recommended baseline stream multiplexer is + `yamux`. See [PR 3689]. [PR 3689]: https://github.com/libp2p/rust-libp2p/pull/3689 ## 0.51.2 -- Introduce `libp2p::connection_limits` module. - See [PR 3386]. +- Introduce `libp2p::connection_limits` module. See [PR 3386]. -- Deprecate the `quic` and `webrtc` feature. - These two crates are only in alpha state. - To properly communicate this to users, we want them to add the dependency directly which makes the `alpha` version visible. - See [PR 3580]. +- Deprecate the `quic` and `webrtc` feature. These two crates are only in alpha + state. To properly communicate this to users, we want them to add the + dependency directly which makes the `alpha` version visible. See [PR 3580]. -- Introduce `libp2p::allow_block_list` module and deprecate `libp2p::Swarm::ban_peer_id`. - See [PR 3590]. +- Introduce `libp2p::allow_block_list` module and deprecate + `libp2p::Swarm::ban_peer_id`. See [PR 3590]. -- Introduce `libp2p::perf` module. - See [PR 3693]. +- Introduce `libp2p::perf` module. See [PR 3693]. [PR 3386]: https://github.com/libp2p/rust-libp2p/pull/3386 [PR 3580]: https://github.com/libp2p/rust-libp2p/pull/3580 @@ -158,114 +157,147 @@ ## 0.51.1 -- Depend on `libp2p-tls` `v0.1.0`. +- Depend on `libp2p-tls` `v0.1.0`. -- Introduce `ed25519` feature. - For backwards-compatibility, the `ed25519` identity keys are still available without activating this feature. - However, going forward, you should explicitly activate it to avoid compile errors going forward. - See [PR 3350]. +- Introduce `ed25519` feature. For backwards-compatibility, the `ed25519` + identity keys are still available without activating this feature. However, + going forward, you should explicitly activate it to avoid compile errors going + forward. See [PR 3350]. [PR 3350]: https://github.com/libp2p/rust-libp2p/pull/3350 ## 0.51.0 -- Enable `NetworkBehaviour`s to manage connections. - This deprecates `NetworkBehaviour::new_handler` and `NetworkBehaviour::addresses_of_peer`. - Due to limitations in the Rust compiler, these deprecations may not show up for you, nevertheless they will be removed in a future release. - See [`libp2p-swarm`'s CHANGELOG](swarm/CHANGELOG.md#0420) for details. +- Enable `NetworkBehaviour`s to manage connections. This deprecates + `NetworkBehaviour::new_handler` and `NetworkBehaviour::addresses_of_peer`. Due + to limitations in the Rust compiler, these deprecations may not show up for + you, nevertheless they will be removed in a future release. See + [`libp2p-swarm`'s CHANGELOG](swarm/CHANGELOG.md#0420) for details. + +- Count bandwidth at the application level. Previously `BandwidthLogging` would + implement `Transport` and now implements `StreamMuxer` + ([PR 3180](https://github.com/libp2p/rust-libp2p/pull/3180)). + - `BandwidthLogging::new` now requires a 2nd argument: `Arc` + - Remove `BandwidthFuture` + - Rename `BandwidthConnecLogging` to `InstrumentedStream` +- Remove `SimpleProtocol` due to being unused. See + [`libp2p::core::upgrade`](https://docs.rs/libp2p/0.50.0/libp2p/core/upgrade/index.html) + for alternatives. See [PR 3191]. -- Count bandwidth at the application level. Previously `BandwidthLogging` would implement `Transport` and now implements `StreamMuxer` ([PR 3180](https://github.com/libp2p/rust-libp2p/pull/3180)). - - `BandwidthLogging::new` now requires a 2nd argument: `Arc` - - Remove `BandwidthFuture` - - Rename `BandwidthConnecLogging` to `InstrumentedStream` -- Remove `SimpleProtocol` due to being unused. See [`libp2p::core::upgrade`](https://docs.rs/libp2p/0.50.0/libp2p/core/upgrade/index.html) for alternatives. See [PR 3191]. +- Bump MSRV to 1.65.0. -- Bump MSRV to 1.65.0. +- Update individual crates. -- Update individual crates. - - Update to [`libp2p-dcutr` `v0.9.0`](protocols/dcutr/CHANGELOG.md#090). + - Update to [`libp2p-dcutr` `v0.9.0`](protocols/dcutr/CHANGELOG.md#090). - - Update to [`libp2p-ping` `v0.42.0`](protocols/ping/CHANGELOG.md#0420). + - Update to [`libp2p-ping` `v0.42.0`](protocols/ping/CHANGELOG.md#0420). - - Update to [`libp2p-request-response` `v0.24.0`](protocols/request-response/CHANGELOG.md#0240). + - Update to + [`libp2p-request-response` `v0.24.0`](protocols/request-response/CHANGELOG.md#0240). - - Update to [`libp2p-kad` `v0.43.0`](protocols/kad/CHANGELOG.md#0430). + - Update to [`libp2p-kad` `v0.43.0`](protocols/kad/CHANGELOG.md#0430). - - Update to [`libp2p-floodsub` `v0.42.0`](protocols/floodsub/CHANGELOG.md#0420). + - Update to + [`libp2p-floodsub` `v0.42.0`](protocols/floodsub/CHANGELOG.md#0420). - - Update to [`libp2p-autonat` `v0.10.0`](protocols/autonat/CHANGELOG.md#0100). + - Update to [`libp2p-autonat` `v0.10.0`](protocols/autonat/CHANGELOG.md#0100). - - Update to [`libp2p-relay` `v0.15.0`](protocols/relay/CHANGELOG.md#0150). + - Update to [`libp2p-relay` `v0.15.0`](protocols/relay/CHANGELOG.md#0150). - - Update to [`libp2p-identify` `v0.42.0`](protocols/identify/CHANGELOG.md#0420). + - Update to + [`libp2p-identify` `v0.42.0`](protocols/identify/CHANGELOG.md#0420). - - Update to [`libp2p-rendezvous` `v0.12.0`](protocols/rendezvous/CHANGELOG.md#0120). + - Update to + [`libp2p-rendezvous` `v0.12.0`](protocols/rendezvous/CHANGELOG.md#0120). - - Update to [`libp2p-metrics` `v0.12.0`](misc/metrics/CHANGELOG.md#0120). + - Update to [`libp2p-metrics` `v0.12.0`](misc/metrics/CHANGELOG.md#0120). - - Update to [`libp2p-swarm` `v0.42.0`](swarm/CHANGELOG.md#0420). + - Update to [`libp2p-swarm` `v0.42.0`](swarm/CHANGELOG.md#0420). - - Update to [`libp2p-mdns` `v0.43.0`](protocols/mdns/CHANGELOG.md#0430). + - Update to [`libp2p-mdns` `v0.43.0`](protocols/mdns/CHANGELOG.md#0430). - - Update to [`libp2p-gossipsub` `v0.44.0`](protocols/gossipsub/CHANGELOG.md#0440). + - Update to + [`libp2p-gossipsub` `v0.44.0`](protocols/gossipsub/CHANGELOG.md#0440). - - Update to [`libp2p-yamux` `v0.43.0`](muxers/yamux/CHANGELOG.md#0430). + - Update to [`libp2p-yamux` `v0.43.0`](muxers/yamux/CHANGELOG.md#0430). - - Update to [`libp2p-mplex` `v0.39.0`](muxers/mplex/CHANGELOG.md#0390). + - Update to [`libp2p-mplex` `v0.39.0`](muxers/mplex/CHANGELOG.md#0390). - - Update to [`libp2p-wasm-ext` `v0.39.0`](transports/wasm-ext/CHANGELOG.md#0390). + - Update to + [`libp2p-wasm-ext` `v0.39.0`](transports/wasm-ext/CHANGELOG.md#0390). - - Update to [`libp2p-plaintext` `v0.39.0`](transports/plaintext/CHANGELOG.md#0390). + - Update to + [`libp2p-plaintext` `v0.39.0`](transports/plaintext/CHANGELOG.md#0390). - - Update to [`libp2p-noise` `v0.42.0`](transports/noise/CHANGELOG.md#0420). + - Update to [`libp2p-noise` `v0.42.0`](transports/noise/CHANGELOG.md#0420). - - Update to [`libp2p-core` `v0.39.0`](core/CHANGELOG.md#0390). + - Update to [`libp2p-core` `v0.39.0`](core/CHANGELOG.md#0390). [PR 3191]: https://github.com/libp2p/rust-libp2p/pull/3191 ## 0.50.0 -This is a large release. After > 4 years, rust-libp2p ships with an [(alpha) QUIC -implementation](transports/quic/CHANGELOG.md#070-alpha). The [necessary TLS logic is extracted into -its own crate](transports/tls/CHANGELOG.md#010-alpha), and can thus be used detached from QUIC, e.g. -on top of TCP as an alternative to Noise. In addition to these two transports, this release adds -a third, namely [WebRTC (browser-to-server)](transports/webrtc/CHANGELOG.md#040-alpha). But that is -definitely not it. See below for the many other changes packed into this release. - -- Introduce [`libp2p-tls` `v0.1.0-alpha`](transports/tls/CHANGELOG.md#010-alpha). See [PR 2945]. -- Introduce [`libp2p-quic` `v0.7.0-alpha`](transports/quic/CHANGELOG.md#070-alpha). See [PR 2289]. -- Introduce [`libp2p-webrtc` `v0.4.0-alpha`](transports/webrtc/CHANGELOG.md#040-alpha). See [PR 2289]. -- Remove deprecated features: `tcp-tokio`, `mdns-tokio`, `dns-tokio`, `tcp-async-io`, `mdns-async-io`, `dns-async-std`. - See [PR 3001]. -- Remove `NetworkBehaviour` macro export from root crate in favor of re-exported macro from `libp2p::swarm`. - Change your import from `libp2p::NetworkBehaviour` to `libp2p::swarm::NetworkBehaviour`. See [PR 3055]. -- Feature-gate `NetworkBehaviour` macro behind `macros` feature flag. See [PR 3055]. -- Update individual crates. - - Update to [`libp2p-autonat` `v0.89.0`](protocols/autonat/CHANGELOG.md#090). - - Update to [`libp2p-core` `v0.38.0`](core/CHANGELOG.md#0380). - - Update to [`libp2p-dcutr` `v0.8.0`](protocols/dcutr/CHANGELOG.md#080). - - Update to [`libp2p-deflate` `v0.38.0`](transports/deflate/CHANGELOG.md#0380). - - Update to [`libp2p-dns` `v0.38.0`](transports/dns/CHANGELOG.md#0380). - - Update to [`libp2p-floodsub` `v0.41.0`](protocols/floodsub/CHANGELOG.md#0410). - - Update to [`libp2p-gossipsub` `v0.43.0`](protocols/gossipsub/CHANGELOG.md#0430). - - Update to [`libp2p-identify` `v0.41.0`](protocols/identify/CHANGELOG.md#0410). - - Update to [`libp2p-kad` `v0.42.0`](protocols/kad/CHANGELOG.md#0420). - - Update to [`libp2p-mdns` `v0.42.0`](protocols/mdns/CHANGELOG.md#0420). - - Update to [`libp2p-metrics` `v0.11.0`](misc/metrics/CHANGELOG.md#0110). - - Update to [`libp2p-mplex` `v0.38.0`](muxers/mplex/CHANGELOG.md#0380). - - Update to [`libp2p-noise` `v0.41.0`](transports/noise/CHANGELOG.md#0410). - - Update to [`libp2p-ping` `v0.41.0`](protocols/ping/CHANGELOG.md#0410). - - Update to [`libp2p-plaintext` `v0.38.0`](transports/plaintext/CHANGELOG.md#0380). - - Update to [`libp2p-pnet` `v0.22.2`](transports/pnet/CHANGELOG.md#0222). - - Update to [`libp2p-relay` `v0.14.0`](protocols/relay/CHANGELOG.md#0140). - - Update to [`libp2p-rendezvous` `v0.11.0`](protocols/rendezovus/CHANGELOG.md#0110). - - Update to [`libp2p-request-response` `v0.23.0`](protocols/request-response/CHANGELOG.md#0230). - - Update to [`libp2p-swarm` `v0.41.0`](swarm/CHANGELOG.md#0410). - - Update to [`libp2p-tcp` `v0.38.0`](transports/tcp/CHANGELOG.md#0380). - - Update to [`libp2p-uds` `v0.37.0`](transports/uds/CHANGELOG.md#0370). - - Update to [`libp2p-wasm-ext` `v0.38.0`](transports/wasm-ext/CHANGELOG.md#0380). - - Update to [`libp2p-websocket` `v0.40.0`](transports/websocket/CHANGELOG.md#0400). - - Update to [`libp2p-yamux` `v0.42.0`](muxers/yamux/CHANGELOG.md#0420). +This is a large release. After > 4 years, rust-libp2p ships with an +[(alpha) QUIC implementation](transports/quic/CHANGELOG.md#070-alpha). The +[necessary TLS logic is extracted into +its own crate](transports/tls/CHANGELOG.md#010-alpha), and can thus be used +detached from QUIC, e.g. on top of TCP as an alternative to Noise. In addition +to these two transports, this release adds a third, namely +[WebRTC (browser-to-server)](transports/webrtc/CHANGELOG.md#040-alpha). But that +is definitely not it. See below for the many other changes packed into this +release. + +- Introduce + [`libp2p-tls` `v0.1.0-alpha`](transports/tls/CHANGELOG.md#010-alpha). See + [PR 2945]. +- Introduce + [`libp2p-quic` `v0.7.0-alpha`](transports/quic/CHANGELOG.md#070-alpha). See + [PR 2289]. +- Introduce + [`libp2p-webrtc` `v0.4.0-alpha`](transports/webrtc/CHANGELOG.md#040-alpha). + See [PR 2289]. +- Remove deprecated features: `tcp-tokio`, `mdns-tokio`, `dns-tokio`, + `tcp-async-io`, `mdns-async-io`, `dns-async-std`. See [PR 3001]. +- Remove `NetworkBehaviour` macro export from root crate in favor of re-exported + macro from `libp2p::swarm`. Change your import from `libp2p::NetworkBehaviour` + to `libp2p::swarm::NetworkBehaviour`. See [PR 3055]. +- Feature-gate `NetworkBehaviour` macro behind `macros` feature flag. See + [PR 3055]. +- Update individual crates. + - Update to [`libp2p-autonat` `v0.89.0`](protocols/autonat/CHANGELOG.md#090). + - Update to [`libp2p-core` `v0.38.0`](core/CHANGELOG.md#0380). + - Update to [`libp2p-dcutr` `v0.8.0`](protocols/dcutr/CHANGELOG.md#080). + - Update to + [`libp2p-deflate` `v0.38.0`](transports/deflate/CHANGELOG.md#0380). + - Update to [`libp2p-dns` `v0.38.0`](transports/dns/CHANGELOG.md#0380). + - Update to + [`libp2p-floodsub` `v0.41.0`](protocols/floodsub/CHANGELOG.md#0410). + - Update to + [`libp2p-gossipsub` `v0.43.0`](protocols/gossipsub/CHANGELOG.md#0430). + - Update to + [`libp2p-identify` `v0.41.0`](protocols/identify/CHANGELOG.md#0410). + - Update to [`libp2p-kad` `v0.42.0`](protocols/kad/CHANGELOG.md#0420). + - Update to [`libp2p-mdns` `v0.42.0`](protocols/mdns/CHANGELOG.md#0420). + - Update to [`libp2p-metrics` `v0.11.0`](misc/metrics/CHANGELOG.md#0110). + - Update to [`libp2p-mplex` `v0.38.0`](muxers/mplex/CHANGELOG.md#0380). + - Update to [`libp2p-noise` `v0.41.0`](transports/noise/CHANGELOG.md#0410). + - Update to [`libp2p-ping` `v0.41.0`](protocols/ping/CHANGELOG.md#0410). + - Update to + [`libp2p-plaintext` `v0.38.0`](transports/plaintext/CHANGELOG.md#0380). + - Update to [`libp2p-pnet` `v0.22.2`](transports/pnet/CHANGELOG.md#0222). + - Update to [`libp2p-relay` `v0.14.0`](protocols/relay/CHANGELOG.md#0140). + - Update to + [`libp2p-rendezvous` `v0.11.0`](protocols/rendezovus/CHANGELOG.md#0110). + - Update to + [`libp2p-request-response` `v0.23.0`](protocols/request-response/CHANGELOG.md#0230). + - Update to [`libp2p-swarm` `v0.41.0`](swarm/CHANGELOG.md#0410). + - Update to [`libp2p-tcp` `v0.38.0`](transports/tcp/CHANGELOG.md#0380). + - Update to [`libp2p-uds` `v0.37.0`](transports/uds/CHANGELOG.md#0370). + - Update to + [`libp2p-wasm-ext` `v0.38.0`](transports/wasm-ext/CHANGELOG.md#0380). + - Update to + [`libp2p-websocket` `v0.40.0`](transports/websocket/CHANGELOG.md#0400). + - Update to [`libp2p-yamux` `v0.42.0`](muxers/yamux/CHANGELOG.md#0420). [PR 2945]: https://github.com/libp2p/rust-libp2p/pull/2945 [PR 3001]: https://github.com/libp2p/rust-libp2p/pull/3001 @@ -275,939 +307,1117 @@ definitely not it. See below for the many other changes packed into this release ## 0.49.0 -- Remove default features. You need to enable required features explicitly now. As a quick workaround, you may want to use the - new `full` feature which activates all features. See [PR 2918]. - -- Introduce `tokio` and `async-std` features and deprecate the following ones: - - `tcp-tokio` in favor of `tcp` + `tokio` - - `mdns-tokio` in favor of `mdns` + `tokio` - - `dns-tokio` in favor of `dns` + `tokio` - - `tcp-async-io` in favor of `tcp` + `async-std` - - `mdns-async-io` in favor of `mdns` + `async-std` - - `dns-async-std` in favor of `dns` + `async-std` - - See [PR 2962]. - -- Update individual crates. - - Update to [`libp2p-autonat` `v0.8.0`](protocols/autonat/CHANGELOG.md#0080). - - Update to [`libp2p-core` `v0.37.0`](core/CHANGELOG.md#0370). - - Update to [`libp2p-dcutr` `v0.7.0`](protocols/dcutr/CHANGELOG.md#0070). - - Update to [`libp2p-deflate` `v0.37.0`](transports/deflate/CHANGELOG.md#0370). - - Update to [`libp2p-dns` `v0.37.0`](transports/dns/CHANGELOG.md#0370). - - Update to [`libp2p-floodsub` `v0.40.0`](protocols/floodsub/CHANGELOG.md#0400). - - Update to [`libp2p-gossipsub` `v0.42.0`](protocols/gossipsub/CHANGELOG.md#0420). - - Update to [`libp2p-identify` `v0.40.0`](protocols/identify/CHANGELOG.md#0400). - - Update to [`libp2p-kad` `v0.41.0`](protocols/kad/CHANGELOG.md#0410). - - Update to [`libp2p-mdns` `v0.41.0`](protocols/mdns/CHANGELOG.md#0410). - - Update to [`libp2p-metrics` `v0.10.0`](misc/metrics/CHANGELOG.md#0100). - - Update to [`libp2p-mplex` `v0.37.0`](muxers/mplex/CHANGELOG.md#0370). - - Update to [`libp2p-noise` `v0.40.0`](transports/noise/CHANGELOG.md#0400). - - Update to [`libp2p-ping` `v0.40.0`](protocols/ping/CHANGELOG.md#0400). - - Update to [`libp2p-plaintext` `v0.37.0`](transports/plaintext/CHANGELOG.md#0370). - - Update to [`libp2p-relay` `v0.13.0`](protocols/relay/CHANGELOG.md#0130). - - Update to [`libp2p-rendezvous` `v0.10.0`](protocols/rendezovus/CHANGELOG.md#0100). - - Update to [`libp2p-request-response` `v0.22.0`](protocols/request-response/CHANGELOG.md#0220). - - Update to [`libp2p-swarm-derive` `v0.30.1`](swarm-derive/CHANGELOG.md#0301). - - Update to [`libp2p-swarm` `v0.40.0`](swarm/CHANGELOG.md#0400). - - Update to [`libp2p-tcp` `v0.37.0`](transports/tcp/CHANGELOG.md#0370). - - Update to [`libp2p-uds` `v0.36.0`](transports/uds/CHANGELOG.md#0360). - - Update to [`libp2p-wasm-ext` `v0.37.0`](transports/wasm-ext/CHANGELOG.md#0370). - - Update to [`libp2p-websocket` `v0.39.0`](transports/websocket/CHANGELOG.md#0390). - - Update to [`libp2p-yamux` `v0.41.0`](muxers/mplex/CHANGELOG.md#0410). +- Remove default features. You need to enable required features explicitly now. + As a quick workaround, you may want to use the new `full` feature which + activates all features. See [PR 2918]. + +- Introduce `tokio` and `async-std` features and deprecate the following ones: + + - `tcp-tokio` in favor of `tcp` + `tokio` + - `mdns-tokio` in favor of `mdns` + `tokio` + - `dns-tokio` in favor of `dns` + `tokio` + - `tcp-async-io` in favor of `tcp` + `async-std` + - `mdns-async-io` in favor of `mdns` + `async-std` + - `dns-async-std` in favor of `dns` + `async-std` + + See [PR 2962]. + +- Update individual crates. + - Update to [`libp2p-autonat` `v0.8.0`](protocols/autonat/CHANGELOG.md#0080). + - Update to [`libp2p-core` `v0.37.0`](core/CHANGELOG.md#0370). + - Update to [`libp2p-dcutr` `v0.7.0`](protocols/dcutr/CHANGELOG.md#0070). + - Update to + [`libp2p-deflate` `v0.37.0`](transports/deflate/CHANGELOG.md#0370). + - Update to [`libp2p-dns` `v0.37.0`](transports/dns/CHANGELOG.md#0370). + - Update to + [`libp2p-floodsub` `v0.40.0`](protocols/floodsub/CHANGELOG.md#0400). + - Update to + [`libp2p-gossipsub` `v0.42.0`](protocols/gossipsub/CHANGELOG.md#0420). + - Update to + [`libp2p-identify` `v0.40.0`](protocols/identify/CHANGELOG.md#0400). + - Update to [`libp2p-kad` `v0.41.0`](protocols/kad/CHANGELOG.md#0410). + - Update to [`libp2p-mdns` `v0.41.0`](protocols/mdns/CHANGELOG.md#0410). + - Update to [`libp2p-metrics` `v0.10.0`](misc/metrics/CHANGELOG.md#0100). + - Update to [`libp2p-mplex` `v0.37.0`](muxers/mplex/CHANGELOG.md#0370). + - Update to [`libp2p-noise` `v0.40.0`](transports/noise/CHANGELOG.md#0400). + - Update to [`libp2p-ping` `v0.40.0`](protocols/ping/CHANGELOG.md#0400). + - Update to + [`libp2p-plaintext` `v0.37.0`](transports/plaintext/CHANGELOG.md#0370). + - Update to [`libp2p-relay` `v0.13.0`](protocols/relay/CHANGELOG.md#0130). + - Update to + [`libp2p-rendezvous` `v0.10.0`](protocols/rendezovus/CHANGELOG.md#0100). + - Update to + [`libp2p-request-response` `v0.22.0`](protocols/request-response/CHANGELOG.md#0220). + - Update to [`libp2p-swarm-derive` `v0.30.1`](swarm-derive/CHANGELOG.md#0301). + - Update to [`libp2p-swarm` `v0.40.0`](swarm/CHANGELOG.md#0400). + - Update to [`libp2p-tcp` `v0.37.0`](transports/tcp/CHANGELOG.md#0370). + - Update to [`libp2p-uds` `v0.36.0`](transports/uds/CHANGELOG.md#0360). + - Update to + [`libp2p-wasm-ext` `v0.37.0`](transports/wasm-ext/CHANGELOG.md#0370). + - Update to + [`libp2p-websocket` `v0.39.0`](transports/websocket/CHANGELOG.md#0390). + - Update to [`libp2p-yamux` `v0.41.0`](muxers/mplex/CHANGELOG.md#0410). [PR 2918]: https://github.com/libp2p/rust-libp2p/pull/2918 [PR 2962]: https://github.com/libp2p/rust-libp2p/pull/2962 ## 0.48.0 -- Update to [`libp2p-core` `v0.36.0`](core/CHANGELOG.md#0360). +- Update to [`libp2p-core` `v0.36.0`](core/CHANGELOG.md#0360). -- Update to [`libp2p-swarm-derive` `v0.30.0`](swarm-derive/CHANGELOG.md#0300). +- Update to [`libp2p-swarm-derive` `v0.30.0`](swarm-derive/CHANGELOG.md#0300). -- Update to [`libp2p-dcutr` `v0.6.0`](protocols/dcutr/CHANGELOG.md#060). +- Update to [`libp2p-dcutr` `v0.6.0`](protocols/dcutr/CHANGELOG.md#060). -- Update to [`libp2p-rendezvous` `v0.9.0`](protocols/rendezvous/CHANGELOG.md#090). +- Update to + [`libp2p-rendezvous` `v0.9.0`](protocols/rendezvous/CHANGELOG.md#090). -- Update to [`libp2p-ping` `v0.39.0`](protocols/ping/CHANGELOG.md#0390). +- Update to [`libp2p-ping` `v0.39.0`](protocols/ping/CHANGELOG.md#0390). -- Update to [`libp2p-identify` `v0.39.0`](protocols/identify/CHANGELOG.md#0390). +- Update to [`libp2p-identify` `v0.39.0`](protocols/identify/CHANGELOG.md#0390). -- Update to [`libp2p-floodsub` `v0.39.0`](protocols/floodsub/CHANGELOG.md#0390). +- Update to [`libp2p-floodsub` `v0.39.0`](protocols/floodsub/CHANGELOG.md#0390). -- Update to [`libp2p-relay` `v0.12.0`](protocols/relay/CHANGELOG.md#0120). +- Update to [`libp2p-relay` `v0.12.0`](protocols/relay/CHANGELOG.md#0120). -- Update to [`libp2p-metrics` `v0.9.0`](misc/metrics/CHANGELOG.md#090). +- Update to [`libp2p-metrics` `v0.9.0`](misc/metrics/CHANGELOG.md#090). -- Update to [`libp2p-kad` `v0.40.0`](protocols/kad/CHANGELOG.md#0400). +- Update to [`libp2p-kad` `v0.40.0`](protocols/kad/CHANGELOG.md#0400). -- Update to [`libp2p-autonat` `v0.7.0`](protocols/autonat/CHANGELOG.md#070). +- Update to [`libp2p-autonat` `v0.7.0`](protocols/autonat/CHANGELOG.md#070). -- Update to [`libp2p-request-response` `v0.21.0`](protocols/request-response/CHANGELOG.md#0210). +- Update to + [`libp2p-request-response` `v0.21.0`](protocols/request-response/CHANGELOG.md#0210). ## 0.47.0 -- Update to [`libp2p-dcutr` `v0.5.0`](protocols/dcutr/CHANGELOG.md#050). +- Update to [`libp2p-dcutr` `v0.5.0`](protocols/dcutr/CHANGELOG.md#050). -- Update to [`libp2p-derive` `v0.29.0`](swarm-derive/CHANGELOG.md#0290). +- Update to [`libp2p-derive` `v0.29.0`](swarm-derive/CHANGELOG.md#0290). -- Update to [`libp2p-rendezvous` `v0.8.0`](protocols/rendezvous/CHANGELOG.md#080). +- Update to + [`libp2p-rendezvous` `v0.8.0`](protocols/rendezvous/CHANGELOG.md#080). -- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380). +- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380). -- Update to [`libp2p-identify` `v0.38.0`](protocols/identify/CHANGELOG.md#0380). +- Update to [`libp2p-identify` `v0.38.0`](protocols/identify/CHANGELOG.md#0380). -- Update to [`libp2p-floodsub` `v0.38.0`](protocols/floodsub/CHANGELOG.md#0380). +- Update to [`libp2p-floodsub` `v0.38.0`](protocols/floodsub/CHANGELOG.md#0380). -- Update to [`libp2p-relay` `v0.11.0`](protocols/relay/CHANGELOG.md#0110). +- Update to [`libp2p-relay` `v0.11.0`](protocols/relay/CHANGELOG.md#0110). -- Update to [`libp2p-metrics` `v0.8.0`](misc/metrics/CHANGELOG.md#080). +- Update to [`libp2p-metrics` `v0.8.0`](misc/metrics/CHANGELOG.md#080). -- Update to [`libp2p-kad` `v0.39.0`](protocols/kad/CHANGELOG.md#0390). +- Update to [`libp2p-kad` `v0.39.0`](protocols/kad/CHANGELOG.md#0390). -- Update to [`libp2p-autonat` `v0.6.0`](protocols/autonat/CHANGELOG.md#060). +- Update to [`libp2p-autonat` `v0.6.0`](protocols/autonat/CHANGELOG.md#060). -- Update to [`libp2p-request-response` `v0.20.0`](protocols/request-response/CHANGELOG.md#0200). +- Update to + [`libp2p-request-response` `v0.20.0`](protocols/request-response/CHANGELOG.md#0200). -- Update to [`libp2p-swarm` `v0.38.0`](swarm/CHANGELOG.md#0380). +- Update to [`libp2p-swarm` `v0.38.0`](swarm/CHANGELOG.md#0380). ## 0.46.1 -- Update to `libp2p-derive` [`v0.28.0`](swarm-derive/CHANGELOG.md#0280). +- Update to `libp2p-derive` [`v0.28.0`](swarm-derive/CHANGELOG.md#0280). ## 0.46.0 -- Semver bump Rust from `1.56.1` to `1.60.0` . See [PR 2646]. -- Added weak dependencies for features. See [PR 2646]. -- Update individual crates. - - Update to [`libp2p-autonat` `v0.5.0`](protocols/autonat/CHANGELOG.md#050). - - Update to [`libp2p-core` `v0.34.0`](core/CHANGELOG.md#0340). - - Update to [`libp2p-dcutr` `v0.4.0`](protocols/dcutr/CHANGELOG.md#040). - - Update to [`libp2p-floodsub` `v0.37.0`](protocols/floodsub/CHANGELOG.md#0370). - - Update to [`libp2p-identify` `v0.37.0`](protocols/identify/CHANGELOG.md#0370). - - Update to [`libp2p-kad` `v0.38.0`](protocols/kad/CHANGELOG.md#0380). - - Update to [`libp2p-metrics` `v0.7.0`](misc/metrics/CHANGELOG.md#070). - - Update to [`libp2p-mplex` `v0.34.0`](muxers/mplex/CHANGELOG.md). - - Update to [`libp2p-noise` `v0.37.0`](transports/noise/CHANGELOG.md#0370). - - Update to [`libp2p-ping` `v0.37.0`](protocols/ping/CHANGELOG.md#0370). - - Update to [`libp2p-plaintext` `v0.34.0`](transports/plaintext/CHANGELOG.md#0340). - - Update to [`libp2p-relay` `v0.10.0`](protocols/relay/CHANGELOG.md#0100). - - Update to [`libp2p-rendezvous` `v0.7.0`](protocols/rendezvous/CHANGELOG.md#070). - - Update to [`libp2p-request-response` `v0.19.0`](protocols/request-response/CHANGELOG.md#0190). - - Update to [`libp2p-swarm` `v0.37.0`](swarm/CHANGELOG.md#0370). - - Update to [`libp2p-wasm-ext` `v0.34.0`](transports/wasm-ext/CHANGELOG.md#0340). - - Update to [`libp2p-yamux` `v0.38.0`](muxers/yamux/CHANGELOG.md#0380). - - Update to `libp2p-uds` [`v0.33.0`](transports/uds/CHANGELOG.md). +- Semver bump Rust from `1.56.1` to `1.60.0` . See [PR 2646]. +- Added weak dependencies for features. See [PR 2646]. +- Update individual crates. + - Update to [`libp2p-autonat` `v0.5.0`](protocols/autonat/CHANGELOG.md#050). + - Update to [`libp2p-core` `v0.34.0`](core/CHANGELOG.md#0340). + - Update to [`libp2p-dcutr` `v0.4.0`](protocols/dcutr/CHANGELOG.md#040). + - Update to + [`libp2p-floodsub` `v0.37.0`](protocols/floodsub/CHANGELOG.md#0370). + - Update to + [`libp2p-identify` `v0.37.0`](protocols/identify/CHANGELOG.md#0370). + - Update to [`libp2p-kad` `v0.38.0`](protocols/kad/CHANGELOG.md#0380). + - Update to [`libp2p-metrics` `v0.7.0`](misc/metrics/CHANGELOG.md#070). + - Update to [`libp2p-mplex` `v0.34.0`](muxers/mplex/CHANGELOG.md). + - Update to [`libp2p-noise` `v0.37.0`](transports/noise/CHANGELOG.md#0370). + - Update to [`libp2p-ping` `v0.37.0`](protocols/ping/CHANGELOG.md#0370). + - Update to + [`libp2p-plaintext` `v0.34.0`](transports/plaintext/CHANGELOG.md#0340). + - Update to [`libp2p-relay` `v0.10.0`](protocols/relay/CHANGELOG.md#0100). + - Update to + [`libp2p-rendezvous` `v0.7.0`](protocols/rendezvous/CHANGELOG.md#070). + - Update to + [`libp2p-request-response` `v0.19.0`](protocols/request-response/CHANGELOG.md#0190). + - Update to [`libp2p-swarm` `v0.37.0`](swarm/CHANGELOG.md#0370). + - Update to + [`libp2p-wasm-ext` `v0.34.0`](transports/wasm-ext/CHANGELOG.md#0340). + - Update to [`libp2p-yamux` `v0.38.0`](muxers/yamux/CHANGELOG.md#0380). + - Update to `libp2p-uds` [`v0.33.0`](transports/uds/CHANGELOG.md). [PR 2646]: https://github.com/libp2p/rust-libp2p/pull/2646 ## 0.45.1 -- Update individual crates. - - Update to [`libp2p-dcutr` `v0.3.1`](protocols/dcutr/CHANGELOG.md). - - Update to [`libp2p-identify` `v0.36.1`](protocols/identify/CHANGELOG.md). - - Update to [`libp2p-kad` `v0.37.1`](protocols/kad/CHANGELOG.md). - - Update to [`libp2p-relay` `v0.9.1`](protocols/relay/CHANGELOG.md). - - Update to [`libp2p-swarm` `v0.36.1`](swarm/CHANGELOG.md). +- Update individual crates. + - Update to [`libp2p-dcutr` `v0.3.1`](protocols/dcutr/CHANGELOG.md). + - Update to [`libp2p-identify` `v0.36.1`](protocols/identify/CHANGELOG.md). + - Update to [`libp2p-kad` `v0.37.1`](protocols/kad/CHANGELOG.md). + - Update to [`libp2p-relay` `v0.9.1`](protocols/relay/CHANGELOG.md). + - Update to [`libp2p-swarm` `v0.36.1`](swarm/CHANGELOG.md). ## 0.45.0 -- Update individual crates. - - Update to [`libp2p-plaintext` `v0.33.0`](transports/plaintext/CHANGELOG.md). - - Update to [`libp2p-noise` `v0.36.0`](transports/noise/CHANGELOG.md). - - Update to [`libp2p-wasm-ext` `v0.33.0`](transports/wasm-ext/CHANGELOG.md). - - Update to [`libp2p-yamux` `v0.37.0`](muxers/yamux/CHANGELOG.md). - - Update to [`libp2p-mplex` `v0.33.0`](muxers/mplex/CHANGELOG.md). - - Update to [`libp2p-dcutr` `v0.3.0`](protocols/dcutr/CHANGELOG.md). - - Update to [`libp2p-rendezvous` `v0.6.0`](protocols/rendezvous/CHANGELOG.md). - - Update to [`libp2p-ping` `v0.36.0`](protocols/ping/CHANGELOG.md). - - Update to [`libp2p-identify` `v0.36.0`](protocols/identify/CHANGELOG.md). - - Update to [`libp2p-floodsub` `v0.36.0`](protocols/floodsub/CHANGELOG.md). - - Update to [`libp2p-relay` `v0.9.0`](protocols/relay/CHANGELOG.md). - - Update to [`libp2p-metrics` `v0.6.0`](misc/metrics/CHANGELOG.md). - - Update to [`libp2p-kad` `v0.37.0`](protocols/kad/CHANGELOG.md). - - Update to [`libp2p-autonat` `v0.4.0`](protocols/autonat/CHANGELOG.md). - - Update to [`libp2p-request-response` `v0.18.0`](protocols/request-response/CHANGELOG.md). - - Update to [`libp2p-swarm` `v0.36.0`](swarm/CHANGELOG.md). - - Update to [`libp2p-core` `v0.33.0`](core/CHANGELOG.md). + +- Update individual crates. + - Update to [`libp2p-plaintext` `v0.33.0`](transports/plaintext/CHANGELOG.md). + - Update to [`libp2p-noise` `v0.36.0`](transports/noise/CHANGELOG.md). + - Update to [`libp2p-wasm-ext` `v0.33.0`](transports/wasm-ext/CHANGELOG.md). + - Update to [`libp2p-yamux` `v0.37.0`](muxers/yamux/CHANGELOG.md). + - Update to [`libp2p-mplex` `v0.33.0`](muxers/mplex/CHANGELOG.md). + - Update to [`libp2p-dcutr` `v0.3.0`](protocols/dcutr/CHANGELOG.md). + - Update to [`libp2p-rendezvous` `v0.6.0`](protocols/rendezvous/CHANGELOG.md). + - Update to [`libp2p-ping` `v0.36.0`](protocols/ping/CHANGELOG.md). + - Update to [`libp2p-identify` `v0.36.0`](protocols/identify/CHANGELOG.md). + - Update to [`libp2p-floodsub` `v0.36.0`](protocols/floodsub/CHANGELOG.md). + - Update to [`libp2p-relay` `v0.9.0`](protocols/relay/CHANGELOG.md). + - Update to [`libp2p-metrics` `v0.6.0`](misc/metrics/CHANGELOG.md). + - Update to [`libp2p-kad` `v0.37.0`](protocols/kad/CHANGELOG.md). + - Update to [`libp2p-autonat` `v0.4.0`](protocols/autonat/CHANGELOG.md). + - Update to + [`libp2p-request-response` `v0.18.0`](protocols/request-response/CHANGELOG.md). + - Update to [`libp2p-swarm` `v0.36.0`](swarm/CHANGELOG.md). + - Update to [`libp2p-core` `v0.33.0`](core/CHANGELOG.md). ## 0.44.0 -- Update individual crates. - - Update to [`libp2p-dcutr` `v0.2.0`](protocols/dcutr/CHANGELOG.md). - - Update to [`libp2p-dns` `v0.32.1`](transports/dns/CHANGELOG.md). - - Update to [`libp2p-rendezvous` `v0.5.0`](protocols/rendezvous/CHANGELOG.md). - - Update to [`libp2p-ping` `v0.35.0`](protocols/ping/CHANGELOG.md). - - Update to [`libp2p-identify` `v0.35.0`](protocols/identify/CHANGELOG.md). - - Update to [`libp2p-floodsub` `v0.35.0`](protocols/floodsub/CHANGELOG.md). - - Update to [`libp2p-relay` `v0.8.0`](protocols/relay/CHANGELOG.md). - - Update to [`libp2p-metrics` `v0.5.0`](misc/metrics/CHANGELOG.md). - - Update to [`libp2p-kad` `v0.36.0`](protocols/kad/CHANGELOG.md). - - Update to [`libp2p-autonat` `v0.3.0`](protocols/autonat/CHANGELOG.md). - - Update to [`libp2p-request-response` `v0.17.0`](protocols/request-response/CHANGELOG.md). - - Update to [`libp2p-swarm` `v0.35.0`](swarm/CHANGELOG.md). +- Update individual crates. + - Update to [`libp2p-dcutr` `v0.2.0`](protocols/dcutr/CHANGELOG.md). + - Update to [`libp2p-dns` `v0.32.1`](transports/dns/CHANGELOG.md). + - Update to [`libp2p-rendezvous` `v0.5.0`](protocols/rendezvous/CHANGELOG.md). + - Update to [`libp2p-ping` `v0.35.0`](protocols/ping/CHANGELOG.md). + - Update to [`libp2p-identify` `v0.35.0`](protocols/identify/CHANGELOG.md). + - Update to [`libp2p-floodsub` `v0.35.0`](protocols/floodsub/CHANGELOG.md). + - Update to [`libp2p-relay` `v0.8.0`](protocols/relay/CHANGELOG.md). + - Update to [`libp2p-metrics` `v0.5.0`](misc/metrics/CHANGELOG.md). + - Update to [`libp2p-kad` `v0.36.0`](protocols/kad/CHANGELOG.md). + - Update to [`libp2p-autonat` `v0.3.0`](protocols/autonat/CHANGELOG.md). + - Update to + [`libp2p-request-response` `v0.17.0`](protocols/request-response/CHANGELOG.md). + - Update to [`libp2p-swarm` `v0.35.0`](swarm/CHANGELOG.md). ## Version 0.43.0 [2022-02-22] -- Update individual crates. - - Update to `libp2p-autonat` [`v0.2.0`](protocols/autonat/CHANGELOG.md#020-2022-02-22). - - Update to `libp2p-core` [`v0.32.0`](core/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-deflate` [`v0.32.0`](transports/deflate/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-dns` [`v0.32.0`](transports/dns/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-floodsub` [`v0.34.0`](protocols/floodsub/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-gossipsub` [`v0.36.0`](protocols/gossipsub/CHANGELOG.md#0360-2022-02-22). - - Update to `libp2p-identify` [`v0.34.0`](protocols/identify/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-kad` [`v0.35.0`](protocols/kad/CHANGELOG.md#0350-2022-02-22). - - Update to `libp2p-mdns` [`v0.35.0`](protocols/mdns/CHANGELOG.md#0350-2022-02-22). - - Update to `libp2p-metrics` [`v0.4.0`](misc/metrics/CHANGELOG.md#040-2022-02-22). - - Update to `libp2p-mplex` [`v0.32.0`](muxers/mplex/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-noise` [`v0.35.0`](transports/noise/CHANGELOG.md#0350-2022-02-22). - - Update to `libp2p-ping` [`v0.34.0`](protocols/ping/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-plaintext` [`v0.32.0`](transports/plaintext/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-relay` [`v0.7.0`](protocols/relay/CHANGELOG.md#070-2022-02-22). - - Update to `libp2p-rendezvous` [`v0.4.0`](protocols/rendezvous/CHANGELOG.md#040-2022-02-22). - - Update to `libp2p-request-response` [`v0.16.0`](protocols/request-response/CHANGELOG.md#0160-2022-02-22). - - Update to `libp2p-swarm` [`v0.34.0`](swarm/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-derive` [`v0.27.0`](swarm-derive/CHANGELOG.md#0270-2022-02-22). - - Update to `libp2p-tcp` [`v0.32.0`](transports/tcp/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-uds` [`v0.32.0`](transports/uds/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-wasm-ext` [`v0.32.0`](transports/wasm-ext/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-websocket` [`v0.34.0`](transports/websocket/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-yamux` [`v0.36.0`](muxers/yamux/CHANGELOG.md#0360-2022-02-22). - -- Update to `parking_lot` `v0.12.0`. See [PR 2463]. - -- Drop support for gossipsub in the wasm32-unknown-unknown target (see [PR 2506]). +- Update individual crates. + + - Update to `libp2p-autonat` + [`v0.2.0`](protocols/autonat/CHANGELOG.md#020-2022-02-22). + - Update to `libp2p-core` [`v0.32.0`](core/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-deflate` + [`v0.32.0`](transports/deflate/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-dns` + [`v0.32.0`](transports/dns/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-floodsub` + [`v0.34.0`](protocols/floodsub/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-gossipsub` + [`v0.36.0`](protocols/gossipsub/CHANGELOG.md#0360-2022-02-22). + - Update to `libp2p-identify` + [`v0.34.0`](protocols/identify/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-kad` + [`v0.35.0`](protocols/kad/CHANGELOG.md#0350-2022-02-22). + - Update to `libp2p-mdns` + [`v0.35.0`](protocols/mdns/CHANGELOG.md#0350-2022-02-22). + - Update to `libp2p-metrics` + [`v0.4.0`](misc/metrics/CHANGELOG.md#040-2022-02-22). + - Update to `libp2p-mplex` + [`v0.32.0`](muxers/mplex/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-noise` + [`v0.35.0`](transports/noise/CHANGELOG.md#0350-2022-02-22). + - Update to `libp2p-ping` + [`v0.34.0`](protocols/ping/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-plaintext` + [`v0.32.0`](transports/plaintext/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-relay` + [`v0.7.0`](protocols/relay/CHANGELOG.md#070-2022-02-22). + - Update to `libp2p-rendezvous` + [`v0.4.0`](protocols/rendezvous/CHANGELOG.md#040-2022-02-22). + - Update to `libp2p-request-response` + [`v0.16.0`](protocols/request-response/CHANGELOG.md#0160-2022-02-22). + - Update to `libp2p-swarm` [`v0.34.0`](swarm/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-derive` + [`v0.27.0`](swarm-derive/CHANGELOG.md#0270-2022-02-22). + - Update to `libp2p-tcp` + [`v0.32.0`](transports/tcp/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-uds` + [`v0.32.0`](transports/uds/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-wasm-ext` + [`v0.32.0`](transports/wasm-ext/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-websocket` + [`v0.34.0`](transports/websocket/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-yamux` + [`v0.36.0`](muxers/yamux/CHANGELOG.md#0360-2022-02-22). + +- Update to `parking_lot` `v0.12.0`. See [PR 2463]. + +- Drop support for gossipsub in the wasm32-unknown-unknown target (see + [PR 2506]). [PR 2506]: https://github.com/libp2p/rust-libp2p/pull/2506 [PR 2463]: https://github.com/libp2p/rust-libp2p/pull/2463/ ## Version 0.42.1 [2022-02-02] -- Update individual crates. - - `libp2p-relay` - - [v0.6.1](protocols/relay/CHANGELOG.md#061-2022-02-02) - - `libp2p-tcp` - - [v0.31.1](transports/tcp/CHANGELOG.md#0311-2022-02-02) +- Update individual crates. + - `libp2p-relay` + - [v0.6.1](protocols/relay/CHANGELOG.md#061-2022-02-02) + - `libp2p-tcp` + - [v0.31.1](transports/tcp/CHANGELOG.md#0311-2022-02-02) ## Version 0.42.0 [2022-01-27] -- Update individual crates. - - `libp2p-autonat` - - [v0.1.0](protocols/autonat/CHANGELOG.md#010-2022-01-27) - - `libp2p-core` - - [v0.31.0](core/CHANGELOG.md#0310-2022-01-27) - - `libp2p-deflate` - - [v0.31.0](transports/deflate/CHANGELOG.md#0310-2022-01-27) - - `libp2p-dns` - - [v0.31.0](transports/dns/CHANGELOG.md#0310-2022-01-27) - - `libp2p-floodsub` - - [v0.33.0](protocols/floodsub/CHANGELOG.md#0330-2022-01-27) - - `libp2p-gossipsub` - - [v0.35.0](protocols/gossipsub/CHANGELOG.md#0350-2022-01-27) - - `libp2p-identify` - - [v0.33.0](protocols/identify/CHANGELOG.md#0330-2022-01-27) - - `libp2p-kad` - - [v0.34.0](protocols/kad/CHANGELOG.md#0340-2022-01-27) - - `libp2p-mdns` (breaking compatibility with previous versions) - - [v0.34.0](protocols/mdns/CHANGELOG.md#0340-2022-01-27) - - `libp2p-metrics` - - [v0.3.0](misc/metrics/CHANGELOG.md#030-2022-01-27) - - `libp2p-mplex` - - [v0.31.0](muxers/mplex/CHANGELOG.md#0310-2022-01-27) - - `libp2p-noise` - - [v0.34.0](transports/noise/CHANGELOG.md#0340-2022-01-27) - - `libp2p-ping` - - [v0.33.0](protocols/ping/CHANGELOG.md#0330-2022-01-27) - - `libp2p-plaintext` - - [v0.31.0](transports/plaintext/CHANGELOG.md#0310-2022-01-27) - - `libp2p-relay` - - [v0.6.0](protocols/relay/CHANGELOG.md#060-2022-01-27) - - `libp2p-rendezvous` - - [v0.3.0](protocols/rendezvous/CHANGELOG.md#030-2022-01-27) - - `libp2p-request-response` - - [v0.15.0](protocols/request-response/CHANGELOG.md#0150-2022-01-27) - - `libp2p-swarm-derive` - - [v0.26.1](swarm-derive/CHANGELOG.md#0261-2022-01-27) - - `libp2p-swarm` - - [v0.33.0](swarm/CHANGELOG.md#0330-2022-01-27) - - `libp2p-tcp` - - [v0.31.0](transports/tcp/CHANGELOG.md#0310-2022-01-27) - - `libp2p-uds` - - [v0.31.0](transports/uds/CHANGELOG.md#0310-2022-01-27) - - `libp2p-wasm-ext` - - [v0.31.0](transports/wasm-ext/CHANGELOG.md#0310-2022-01-27) - - `libp2p-websocket` - - [v0.33.0](transports/websocket/CHANGELOG.md#0330-2022-01-27) - - `libp2p-yamux` - - [v0.35.0](muxers/yamux/CHANGELOG.md#0350-2022-01-27) - -- Migrate to Rust edition 2021 (see [PR 2339]). +- Update individual crates. + + - `libp2p-autonat` + - [v0.1.0](protocols/autonat/CHANGELOG.md#010-2022-01-27) + - `libp2p-core` + - [v0.31.0](core/CHANGELOG.md#0310-2022-01-27) + - `libp2p-deflate` + - [v0.31.0](transports/deflate/CHANGELOG.md#0310-2022-01-27) + - `libp2p-dns` + - [v0.31.0](transports/dns/CHANGELOG.md#0310-2022-01-27) + - `libp2p-floodsub` + - [v0.33.0](protocols/floodsub/CHANGELOG.md#0330-2022-01-27) + - `libp2p-gossipsub` + - [v0.35.0](protocols/gossipsub/CHANGELOG.md#0350-2022-01-27) + - `libp2p-identify` + - [v0.33.0](protocols/identify/CHANGELOG.md#0330-2022-01-27) + - `libp2p-kad` + - [v0.34.0](protocols/kad/CHANGELOG.md#0340-2022-01-27) + - `libp2p-mdns` (breaking compatibility with previous versions) + - [v0.34.0](protocols/mdns/CHANGELOG.md#0340-2022-01-27) + - `libp2p-metrics` + - [v0.3.0](misc/metrics/CHANGELOG.md#030-2022-01-27) + - `libp2p-mplex` + - [v0.31.0](muxers/mplex/CHANGELOG.md#0310-2022-01-27) + - `libp2p-noise` + - [v0.34.0](transports/noise/CHANGELOG.md#0340-2022-01-27) + - `libp2p-ping` + - [v0.33.0](protocols/ping/CHANGELOG.md#0330-2022-01-27) + - `libp2p-plaintext` + - [v0.31.0](transports/plaintext/CHANGELOG.md#0310-2022-01-27) + - `libp2p-relay` + - [v0.6.0](protocols/relay/CHANGELOG.md#060-2022-01-27) + - `libp2p-rendezvous` + - [v0.3.0](protocols/rendezvous/CHANGELOG.md#030-2022-01-27) + - `libp2p-request-response` + - [v0.15.0](protocols/request-response/CHANGELOG.md#0150-2022-01-27) + - `libp2p-swarm-derive` + - [v0.26.1](swarm-derive/CHANGELOG.md#0261-2022-01-27) + - `libp2p-swarm` + - [v0.33.0](swarm/CHANGELOG.md#0330-2022-01-27) + - `libp2p-tcp` + - [v0.31.0](transports/tcp/CHANGELOG.md#0310-2022-01-27) + - `libp2p-uds` + - [v0.31.0](transports/uds/CHANGELOG.md#0310-2022-01-27) + - `libp2p-wasm-ext` + - [v0.31.0](transports/wasm-ext/CHANGELOG.md#0310-2022-01-27) + - `libp2p-websocket` + - [v0.33.0](transports/websocket/CHANGELOG.md#0330-2022-01-27) + - `libp2p-yamux` + - [v0.35.0](muxers/yamux/CHANGELOG.md#0350-2022-01-27) + +- Migrate to Rust edition 2021 (see [PR 2339]). [PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339 ## Version 0.41.0 [2021-11-16] -- Update individual crates. - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-identify` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-metrics` - - `libp2p-ping` - - `libp2p-relay` - - `libp2p-rendezvous` - - `libp2p-request-response` - - `libp2p-swarm-derive` - - `libp2p-swarm` - - `libp2p-websocket` -- Forward `wasm-bindgen` feature to `futures-timer`, `instant`, `parking_lot`, `getrandom/js` and `rand/wasm-bindgen`. +- Update individual crates. + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-identify` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-metrics` + - `libp2p-ping` + - `libp2p-relay` + - `libp2p-rendezvous` + - `libp2p-request-response` + - `libp2p-swarm-derive` + - `libp2p-swarm` + - `libp2p-websocket` +- Forward `wasm-bindgen` feature to `futures-timer`, `instant`, `parking_lot`, + `getrandom/js` and `rand/wasm-bindgen`. ## Version 0.40.0 [2021-11-01] -- Update individual crates. - - `libp2p-core` - - `libp2p-deflate` - - `libp2p-dns` - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-identify` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-mplex` - - `libp2p-noise` - - `libp2p-ping` - - `libp2p-plaintext` - - `libp2p-relay` - - `libp2p-request-response` - - `libp2p-swarm` - - `libp2p-tcp` - - `libp2p-uds` - - `libp2p-wasm-ext` - - `libp2p-websocket` - - `libp2p-yamux` - -- Re-export the `wasm-bindgen` feature from `parking_lot`, so - `libp2p` users can opt-in to that crate's Wasm support. See [PR 2180]. - -- Add `libp2p-metrics`. +- Update individual crates. + + - `libp2p-core` + - `libp2p-deflate` + - `libp2p-dns` + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-identify` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-mplex` + - `libp2p-noise` + - `libp2p-ping` + - `libp2p-plaintext` + - `libp2p-relay` + - `libp2p-request-response` + - `libp2p-swarm` + - `libp2p-tcp` + - `libp2p-uds` + - `libp2p-wasm-ext` + - `libp2p-websocket` + - `libp2p-yamux` + +- Re-export the `wasm-bindgen` feature from `parking_lot`, so `libp2p` users can + opt-in to that crate's Wasm support. See [PR 2180]. + +- Add `libp2p-metrics`. [PR 2180]: https://github.com/libp2p/rust-libp2p/pull/2180/ ## Version 0.39.1 [2021-07-12] -- Update individual crates. - - `libp2p-swarm-derive` +- Update individual crates. + - `libp2p-swarm-derive` ## Version 0.39.0 [2021-07-12] -- Update individual crates. - - `libp2p-core` - - `libp2p-deflate` - - `libp2p-dns` - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-identify` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-mplex` - - `libp2p-noise` - - `libp2p-ping` - - `libp2p-plaintext` - - `libp2p-relay` - - `libp2p-request-response` - - `libp2p-swarm` - - `libp2p-tcp` - - `libp2p-uds` - - `libp2p-wasm-ext` - - `libp2p-websocket` - - `libp2p-yamux` +- Update individual crates. + - `libp2p-core` + - `libp2p-deflate` + - `libp2p-dns` + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-identify` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-mplex` + - `libp2p-noise` + - `libp2p-ping` + - `libp2p-plaintext` + - `libp2p-relay` + - `libp2p-request-response` + - `libp2p-swarm` + - `libp2p-tcp` + - `libp2p-uds` + - `libp2p-wasm-ext` + - `libp2p-websocket` + - `libp2p-yamux` ## Version 0.38.0 [2021-05-17] -- Update individual crates. - - `libp2p-core` - - `libp2p-gossipsub` - - `libp2p-noise` - - `libp2p-pnet` - - `libp2p-wasm-ext` +- Update individual crates. + - `libp2p-core` + - `libp2p-gossipsub` + - `libp2p-noise` + - `libp2p-pnet` + - `libp2p-wasm-ext` ## Version 0.37.1 [2021-04-14] -- Update individual crates. - - `libp2p-swarm-derive` +- Update individual crates. + - `libp2p-swarm-derive` ## Version 0.37.0 [2021-04-13] -- Update individual crates. - - `libp2p-core` - - `libp2p-dns` - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-ping` - - `libp2p-relay` - - `libp2p-request-response` - - `libp2p-swarm` - - `libp2p-wasm-ext` - - `libp2p-yamux` - -- Drop support for `wasm32-unknown-unknown` in favor of - `wasm32-unknown-emscripten` and `wasm32-wasi` [PR - 2038](https://github.com/libp2p/rust-libp2p/pull/2038). +- Update individual crates. + + - `libp2p-core` + - `libp2p-dns` + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-ping` + - `libp2p-relay` + - `libp2p-request-response` + - `libp2p-swarm` + - `libp2p-wasm-ext` + - `libp2p-yamux` + +- Drop support for `wasm32-unknown-unknown` in favor of + `wasm32-unknown-emscripten` and `wasm32-wasi` + [PR 2038](https://github.com/libp2p/rust-libp2p/pull/2038). ## Version 0.36.0 [2021-03-17] -- Consolidate top-level utility functions for constructing development - transports. There is now just `development_transport()` (available with default features) - and `tokio_development_transport()` (available when the corresponding tokio features are enabled). - Furthermore, these are now `async fn`s. The minor variations that also included `pnet` - support have been removed. - [PR 1927](https://github.com/libp2p/rust-libp2p/pull/1927) +- Consolidate top-level utility functions for constructing development + transports. There is now just `development_transport()` (available with + default features) and `tokio_development_transport()` (available when the + corresponding tokio features are enabled). Furthermore, these are now + `async fn`s. The minor variations that also included `pnet` support have been + removed. [PR 1927](https://github.com/libp2p/rust-libp2p/pull/1927) -- Update libp2p crates. +- Update libp2p crates. -- Do not leak default features from libp2p crates. - [PR 1986](https://github.com/libp2p/rust-libp2p/pull/1986). +- Do not leak default features from libp2p crates. + [PR 1986](https://github.com/libp2p/rust-libp2p/pull/1986). -- Add `libp2p-relay` to `libp2p` facade crate. +- Add `libp2p-relay` to `libp2p` facade crate. ## Version 0.35.1 [2021-02-17] -- Update `libp2p-yamux` to latest patch version. +- Update `libp2p-yamux` to latest patch version. ## Version 0.35.0 [2021-02-15] -- Use `libp2p-swarm-derive`, the former `libp2p-core-derive`. +- Use `libp2p-swarm-derive`, the former `libp2p-core-derive`. -- Update `libp2p-deflate`, `libp2p-gossipsub`, `libp2p-mdns`, `libp2p-request-response`, - `libp2p-swarm` and `libp2p-tcp`. +- Update `libp2p-deflate`, `libp2p-gossipsub`, `libp2p-mdns`, + `libp2p-request-response`, `libp2p-swarm` and `libp2p-tcp`. ## Version 0.34.0 [2021-01-12] -- Update `libp2p-core` and all dependent crates. +- Update `libp2p-core` and all dependent crates. -- The `tcp-async-std` feature is now `tcp-async-io`, still - enabled by default. +- The `tcp-async-std` feature is now `tcp-async-io`, still enabled by default. ## Version 0.33.0 [2020-12-17] -- Update `libp2p-core` and all dependent crates. +- Update `libp2p-core` and all dependent crates. ## Version 0.32.2 [2020-12-10] -- Update `libp2p-websocket`. +- Update `libp2p-websocket`. ## Version 0.32.1 [2020-12-09] -- Update minimum patch version of `libp2p-websocket`. +- Update minimum patch version of `libp2p-websocket`. ## Version 0.32.0 [2020-12-08] -- Update `libp2p-request-response`. +- Update `libp2p-request-response`. -- Update to `libp2p-mdns-0.26`. +- Update to `libp2p-mdns-0.26`. -- Update `libp2p-websocket` minimum patch version. +- Update `libp2p-websocket` minimum patch version. ## Version 0.31.2 [2020-12-02] -- Bump minimum `libp2p-core` patch version. +- Bump minimum `libp2p-core` patch version. ## Version 0.31.1 [2020-11-26] -- Bump minimum `libp2p-tcp` patch version. +- Bump minimum `libp2p-tcp` patch version. ## Version 0.31.0 [2020-11-25] -- Update `multistream-select` and all dependent crates. +- Update `multistream-select` and all dependent crates. ## Version 0.30.1 [2020-11-11] -- Update `libp2p-plaintext`. +- Update `libp2p-plaintext`. ## Version 0.30.0 [2020-11-09] -- Update `libp2p-mdns`, `libp2p-tcp` and `libp2p-uds` as well as `libp2p-core` - and all its dependers. +- Update `libp2p-mdns`, `libp2p-tcp` and `libp2p-uds` as well as `libp2p-core` + and all its dependers. ## Version 0.29.1 [2020-10-20] -- Update `libp2p-core`. +- Update `libp2p-core`. ## Version 0.29.0 [2020-10-16] -- Update `libp2p-core`, `libp2p-floodsub`, `libp2p-gossipsub`, `libp2p-mplex`, - `libp2p-noise`, `libp2p-plaintext`, `libp2p-pnet`, `libp2p-request-response`, - `libp2p-swarm`, `libp2p-tcp`, `libp2p-websocket` and `parity-multiaddr`. +- Update `libp2p-core`, `libp2p-floodsub`, `libp2p-gossipsub`, `libp2p-mplex`, + `libp2p-noise`, `libp2p-plaintext`, `libp2p-pnet`, `libp2p-request-response`, + `libp2p-swarm`, `libp2p-tcp`, `libp2p-websocket` and `parity-multiaddr`. ## Version 0.28.1 [2020-09-10] -- Update to `libp2p-core` `0.22.1`. +- Update to `libp2p-core` `0.22.1`. ## Version 0.28.0 [2020-09-09] -- Update `libp2p-yamux` to `0.25.0`. *Step 4 of 4 in a multi-release - upgrade process.* See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.25.0`. _Step 4 of 4 in a multi-release upgrade + process._ See the `libp2p-yamux` CHANGELOG for details. ## Version 0.27.0 [2020-09-09] -- Update `libp2p-yamux` to `0.24.0`. *Step 3 of 4 in a multi-release - upgrade process.* See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.24.0`. _Step 3 of 4 in a multi-release upgrade + process._ See the `libp2p-yamux` CHANGELOG for details. ## Version 0.26.0 [2020-09-09] -- Update `libp2p-yamux` to `0.23.0`. *Step 2 of 4 in a multi-release - upgrade process.* See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.23.0`. _Step 2 of 4 in a multi-release upgrade + process._ See the `libp2p-yamux` CHANGELOG for details. ## Version 0.25.0 [2020-09-09] -- Remove the deprecated `libp2p-secio` dependency. To continue to use - SECIO, add an explicit dependency on `libp2p-secio`. However, - transitioning to `libp2p-noise` is strongly recommended. +- Remove the deprecated `libp2p-secio` dependency. To continue to use SECIO, add + an explicit dependency on `libp2p-secio`. However, transitioning to + `libp2p-noise` is strongly recommended. -- Update `libp2p-yamux` to `0.22.0`. *This version starts a multi-release - upgrade process.* See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.22.0`. _This version starts a multi-release + upgrade process._ See the `libp2p-yamux` CHANGELOG for details. -- Bump `libp2p-noise` to `0.24`. See the `libp2p-noise` -changelog for details about the `LegacyConfig`. +- Bump `libp2p-noise` to `0.24`. See the `libp2p-noise` changelog for details + about the `LegacyConfig`. -- The `ProtocolsHandler` in `libp2p-swarm` has a new associated type - `InboundOpenInfo` ([PR 1714]). +- The `ProtocolsHandler` in `libp2p-swarm` has a new associated type + `InboundOpenInfo` ([PR 1714]). [PR 1714]: https://github.com/libp2p/rust-libp2p/pull/1714 ## Version 0.24.0 [2020-08-18] -- Update `libp2p-core`, `libp2p-gossipsub`, `libp2p-kad`, `libp2p-mdns`, - `libp2p-ping`, `libp2p-request-response`, `libp2p-swarm` and dependent crates. +- Update `libp2p-core`, `libp2p-gossipsub`, `libp2p-kad`, `libp2p-mdns`, + `libp2p-ping`, `libp2p-request-response`, `libp2p-swarm` and dependent crates. ## Version 0.23.0 (2020-08-03) -**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` -on an existing deployment, this version must not be skipped -or the provided legacy configuration for `libp2p-noise` used! +**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` on an existing +deployment, this version must not be skipped or the provided legacy +configuration for `libp2p-noise` used! -- Bump `libp2p-noise` dependency to `0.22`. See the `libp2p-noise` -changelog for details about the `LegacyConfig`. +- Bump `libp2p-noise` dependency to `0.22`. See the `libp2p-noise` changelog for + details about the `LegacyConfig`. -- Refactored bandwidth logging ([PR 1670](https://github.com/libp2p/rust-libp2p/pull/1670)). +- Refactored bandwidth logging + ([PR 1670](https://github.com/libp2p/rust-libp2p/pull/1670)). ## Version 0.22.0 (2020-07-17) -**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` -on an existing deployment using `libp2p-noise`, this version -must not be skipped! +**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` on an existing +deployment using `libp2p-noise`, this version must not be skipped! -- Bump `libp2p-noise` dependency to `0.21`. +- Bump `libp2p-noise` dependency to `0.21`. ## Version 0.21.1 (2020-07-02) -- Bump `libp2p-websockets` lower bound. +- Bump `libp2p-websockets` lower bound. ## Version 0.21.0 (2020-07-01) -- Conditional compilation fixes for the `wasm32-wasi` target - ([PR 1633](https://github.com/libp2p/rust-libp2p/pull/1633)). +- Conditional compilation fixes for the `wasm32-wasi` target + ([PR 1633](https://github.com/libp2p/rust-libp2p/pull/1633)). -- New `libp2p-request-response` crate - ([PR 1596](https://github.com/libp2p/rust-libp2p/pull/1596)). +- New `libp2p-request-response` crate + ([PR 1596](https://github.com/libp2p/rust-libp2p/pull/1596)). -- Updated libp2p dependencies. +- Updated libp2p dependencies. ## Version 0.19.1 (2020-05-25) -- Temporarily pin all `async-std` dependencies to `< 1.6`. - [PR 1589](https://github.com/libp2p/rust-libp2p/pull/1589) +- Temporarily pin all `async-std` dependencies to `< 1.6`. + [PR 1589](https://github.com/libp2p/rust-libp2p/pull/1589) -- `libp2p-core-derive`: Fully qualified std::result::Result in macro - [PR 1587](https://github.com/libp2p/rust-libp2p/pull/1587) +- `libp2p-core-derive`: Fully qualified std::result::Result in macro + [PR 1587](https://github.com/libp2p/rust-libp2p/pull/1587) ## Version 0.19.0 (2020-05-18) -- `libp2p-core`, `libp2p-swarm`: Added support for multiple dialing - attempts per peer, with a configurable limit. - [PR 1506](https://github.com/libp2p/rust-libp2p/pull/1506) - -- `libp2p-core`: `PeerId`s that use the identity hashing will now be properly - displayed using the string representation of an identity multihash, rather - than the canonical SHA 256 representation. - [PR 1576](https://github.com/libp2p/rust-libp2p/pull/1576) - -- `libp2p-core`: Updated to multihash 0.11.0. - [PR 1566](https://github.com/libp2p/rust-libp2p/pull/1566) - -- `libp2p-core`: Make the number of events buffered to/from tasks configurable. - [PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574) - -- `libp2p-dns`, `parity-multiaddr`: Added support for the `/dns` multiaddr - protocol. Additionally, the `multiaddr::from_url` function will now use - `/dns` instead of `/dns4`. - [PR 1575](https://github.com/libp2p/rust-libp2p/pull/1575) - -- `libp2p-noise`: Added the `X25519Spec` protocol suite which uses - libp2p-noise-spec compliant signatures on static keys as well as the - `/noise` protocol upgrade, hence providing a libp2p-noise-spec compliant - `XX` handshake. `IK` and `IX` are still supported with `X25519Spec` - though not guaranteed to be interoperable with other libp2p - implementations as these handshake patterns are not currently - included in the libp2p-noise-spec. The `X25519Spec` implementation - will eventually replace the current `X25519` implementation, with - the former being removed. To upgrade without interruptions, you may - temporarily include `NoiseConfig`s for both implementations as - alternatives in your transport upgrade pipeline. - -- `libp2p-kad`: Consider fixed (K_VALUE) amount of peers at closest query - initialization. Unless `KademliaConfig::set_replication_factor` is used change - has no effect. - [PR 1536](https://github.com/libp2p/rust-libp2p/pull/1536) - -- `libp2p-kad`: Provide more insight into, and control of, the execution of - queries. All query results are now wrapped in `KademliaEvent::QueryResult`. - As a side-effect of these changes and for as long as the record storage - API is not asynchronous, local storage errors on `put_record` are reported - synchronously in a `Result`, instead of being reported asynchronously by - an event. - [PR 1567](https://github.com/libp2p/rust-libp2p/pull/1567) - -- `libp2p-tcp`, `libp2p`: Made the `libp2p-tcp/async-std` feature flag - disabled by default, and split the `libp2p/tcp` feature in two: - `tcp-async-std` and `tcp-tokio`. `tcp-async-std` is still enabled by default. - [PR 1471](https://github.com/libp2p/rust-libp2p/pull/1471) - -- `libp2p-tcp`: On listeners started with an IPv6 multi-address the socket - option `IPV6_V6ONLY` is set to true. Instead of relying on IPv4-mapped IPv6 - address support, two listeners can be started if IPv4 and IPv6 should both - be supported. IPv4 listener addresses are not affected by this change. - [PR 1555](https://github.com/libp2p/rust-libp2p/pull/1555) +- `libp2p-core`, `libp2p-swarm`: Added support for multiple dialing attempts per + peer, with a configurable limit. + [PR 1506](https://github.com/libp2p/rust-libp2p/pull/1506) + +- `libp2p-core`: `PeerId`s that use the identity hashing will now be properly + displayed using the string representation of an identity multihash, rather + than the canonical SHA 256 representation. + [PR 1576](https://github.com/libp2p/rust-libp2p/pull/1576) + +- `libp2p-core`: Updated to multihash 0.11.0. + [PR 1566](https://github.com/libp2p/rust-libp2p/pull/1566) + +- `libp2p-core`: Make the number of events buffered to/from tasks configurable. + [PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574) + +- `libp2p-dns`, `parity-multiaddr`: Added support for the `/dns` multiaddr + protocol. Additionally, the `multiaddr::from_url` function will now use `/dns` + instead of `/dns4`. [PR 1575](https://github.com/libp2p/rust-libp2p/pull/1575) + +- `libp2p-noise`: Added the `X25519Spec` protocol suite which uses + libp2p-noise-spec compliant signatures on static keys as well as the `/noise` + protocol upgrade, hence providing a libp2p-noise-spec compliant `XX` + handshake. `IK` and `IX` are still supported with `X25519Spec` though not + guaranteed to be interoperable with other libp2p implementations as these + handshake patterns are not currently included in the libp2p-noise-spec. The + `X25519Spec` implementation will eventually replace the current `X25519` + implementation, with the former being removed. To upgrade without + interruptions, you may temporarily include `NoiseConfig`s for both + implementations as alternatives in your transport upgrade pipeline. + +- `libp2p-kad`: Consider fixed (K_VALUE) amount of peers at closest query + initialization. Unless `KademliaConfig::set_replication_factor` is used change + has no effect. [PR 1536](https://github.com/libp2p/rust-libp2p/pull/1536) + +- `libp2p-kad`: Provide more insight into, and control of, the execution of + queries. All query results are now wrapped in `KademliaEvent::QueryResult`. As + a side-effect of these changes and for as long as the record storage API is + not asynchronous, local storage errors on `put_record` are reported + synchronously in a `Result`, instead of being reported asynchronously by an + event. [PR 1567](https://github.com/libp2p/rust-libp2p/pull/1567) + +- `libp2p-tcp`, `libp2p`: Made the `libp2p-tcp/async-std` feature flag disabled + by default, and split the `libp2p/tcp` feature in two: `tcp-async-std` and + `tcp-tokio`. `tcp-async-std` is still enabled by default. + [PR 1471](https://github.com/libp2p/rust-libp2p/pull/1471) + +- `libp2p-tcp`: On listeners started with an IPv6 multi-address the socket + option `IPV6_V6ONLY` is set to true. Instead of relying on IPv4-mapped IPv6 + address support, two listeners can be started if IPv4 and IPv6 should both be + supported. IPv4 listener addresses are not affected by this change. + [PR 1555](https://github.com/libp2p/rust-libp2p/pull/1555) ## Version 0.18.1 (2020-04-17) -- `libp2p-swarm`: Make sure inject_dial_failure is called in all situations. - [PR 1549](https://github.com/libp2p/rust-libp2p/pull/1549) +- `libp2p-swarm`: Make sure inject_dial_failure is called in all situations. + [PR 1549](https://github.com/libp2p/rust-libp2p/pull/1549) ## Version 0.18.0 (2020-04-09) -- `libp2p-core`: Treat connection limit errors as pending connection errors. - [PR 1546](https://github.com/libp2p/rust-libp2p/pull/1546) +- `libp2p-core`: Treat connection limit errors as pending connection errors. + [PR 1546](https://github.com/libp2p/rust-libp2p/pull/1546) -- `libp2p-core-derive`: Disambiguate calls to `NetworkBehaviour::inject_event`. - [PR 1543](https://github.com/libp2p/rust-libp2p/pull/1543) +- `libp2p-core-derive`: Disambiguate calls to `NetworkBehaviour::inject_event`. + [PR 1543](https://github.com/libp2p/rust-libp2p/pull/1543) -- `libp2p-floodsub`: Allow sent messages seen as subscribed. - [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) +- `libp2p-floodsub`: Allow sent messages seen as subscribed. + [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) -- `libp2p-kad`: Return peers independent of record existence. - [PR 1544](https://github.com/libp2p/rust-libp2p/pull/1544) +- `libp2p-kad`: Return peers independent of record existence. + [PR 1544](https://github.com/libp2p/rust-libp2p/pull/1544) -- `libp2p-wasm-ext`: Fix "parsed is null" errors being thrown. - [PR 1535](https://github.com/libp2p/rust-libp2p/pull/1535) +- `libp2p-wasm-ext`: Fix "parsed is null" errors being thrown. + [PR 1535](https://github.com/libp2p/rust-libp2p/pull/1535) ## Version 0.17.0 (2020-04-02) -- `libp2p-core`: Finished "identity hashing" for peer IDs migration. - [PR 1460](https://github.com/libp2p/rust-libp2p/pull/1460) -- `libp2p-core`: Remove `poll_broadcast`. - [PR 1527](https://github.com/libp2p/rust-libp2p/pull/1527) -- `libp2p-core`, `libp2p-swarm`: Report addresses of closed listeners. - [PR 1485](https://github.com/libp2p/rust-libp2p/pull/1485) -- `libp2p-core`: Support for multiple connections per peer and configurable connection limits. - See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), - [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and - [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. - -- `libp2p-swarm`: Pass the cause of closing a listener to `inject_listener_closed`. - [PR 1517](https://github.com/libp2p/rust-libp2p/pull/1517) -- `libp2p-swarm`: Support for multiple connections per peer and configurable connection limits. - See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), - [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and - [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. -- `libp2p-swarm`: The `SwarmEvent` now returns more events. - [PR 1515](https://github.com/libp2p/rust-libp2p/pull/1515) -- `libp2p-swarm`: New `protocols_handler::multi` module. - [PR 1497](https://github.com/libp2p/rust-libp2p/pull/1497) -- `libp2p-swarm`: Allow configuration of outbound substreams. - [PR 1521](https://github.com/libp2p/rust-libp2p/pull/1521) - -- `libp2p-kad`: Providers returned from a lookup are now deduplicated. - [PR 1528](https://github.com/libp2p/rust-libp2p/pull/1528) -- `libp2p-kad`: Allow customising the maximum packet size. - [PR 1502](https://github.com/libp2p/rust-libp2p/pull/1502) -- `libp2p-kad`: Allow customising the (libp2p) connection keep-alive timeout. - [PR 1477](https://github.com/libp2p/rust-libp2p/pull/1477) -- `libp2p-kad`: Avoid storing records that are expired upon receipt (optimisation). - [PR 1496](https://github.com/libp2p/rust-libp2p/pull/1496) -- `libp2p-kad`: Fixed potential panic on computing record expiry. - [PR 1492](https://github.com/libp2p/rust-libp2p/pull/1492) - -- `libp2p-mplex`: Guard against use of underlying `Sink` upon - error or connection close. - [PR 1529](https://github.com/libp2p/rust-libp2p/pull/1529) - -- `multistream-select`: Upgrade to stable futures. - [PR 1484](https://github.com/libp2p/rust-libp2p/pull/1484) - -- `multihash`: Removed the crate in favour of the upstream crate. - [PR 1472](https://github.com/libp2p/rust-libp2p/pull/1472) +- `libp2p-core`: Finished "identity hashing" for peer IDs migration. + [PR 1460](https://github.com/libp2p/rust-libp2p/pull/1460) +- `libp2p-core`: Remove `poll_broadcast`. + [PR 1527](https://github.com/libp2p/rust-libp2p/pull/1527) +- `libp2p-core`, `libp2p-swarm`: Report addresses of closed listeners. + [PR 1485](https://github.com/libp2p/rust-libp2p/pull/1485) +- `libp2p-core`: Support for multiple connections per peer and configurable + connection limits. See + [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), + [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and + [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. + +- `libp2p-swarm`: Pass the cause of closing a listener to + `inject_listener_closed`. + [PR 1517](https://github.com/libp2p/rust-libp2p/pull/1517) +- `libp2p-swarm`: Support for multiple connections per peer and configurable + connection limits. See + [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), + [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and + [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. +- `libp2p-swarm`: The `SwarmEvent` now returns more events. + [PR 1515](https://github.com/libp2p/rust-libp2p/pull/1515) +- `libp2p-swarm`: New `protocols_handler::multi` module. + [PR 1497](https://github.com/libp2p/rust-libp2p/pull/1497) +- `libp2p-swarm`: Allow configuration of outbound substreams. + [PR 1521](https://github.com/libp2p/rust-libp2p/pull/1521) + +- `libp2p-kad`: Providers returned from a lookup are now deduplicated. + [PR 1528](https://github.com/libp2p/rust-libp2p/pull/1528) +- `libp2p-kad`: Allow customising the maximum packet size. + [PR 1502](https://github.com/libp2p/rust-libp2p/pull/1502) +- `libp2p-kad`: Allow customising the (libp2p) connection keep-alive timeout. + [PR 1477](https://github.com/libp2p/rust-libp2p/pull/1477) +- `libp2p-kad`: Avoid storing records that are expired upon receipt + (optimisation). [PR 1496](https://github.com/libp2p/rust-libp2p/pull/1496) +- `libp2p-kad`: Fixed potential panic on computing record expiry. + [PR 1492](https://github.com/libp2p/rust-libp2p/pull/1492) + +- `libp2p-mplex`: Guard against use of underlying `Sink` upon error or + connection close. [PR 1529](https://github.com/libp2p/rust-libp2p/pull/1529) + +- `multistream-select`: Upgrade to stable futures. + [PR 1484](https://github.com/libp2p/rust-libp2p/pull/1484) + +- `multihash`: Removed the crate in favour of the upstream crate. + [PR 1472](https://github.com/libp2p/rust-libp2p/pull/1472) ## Version 0.16.2 (2020-02-28) -- Fixed yamux connections not properly closing and being stuck in the `CLOSE_WAIT` state. -- Added a `websocket_transport()` function in `libp2p-wasm-ext`, behind a Cargo feature. -- Fixed ambiguity in `IntoProtocolsHandler::select` vs `ProtocolsHandler::select` in the `NetworkBehaviour` custom derive. +- Fixed yamux connections not properly closing and being stuck in the + `CLOSE_WAIT` state. +- Added a `websocket_transport()` function in `libp2p-wasm-ext`, behind a Cargo + feature. +- Fixed ambiguity in `IntoProtocolsHandler::select` vs + `ProtocolsHandler::select` in the `NetworkBehaviour` custom derive. ## Version 0.16.1 (2020-02-18) -- Fixed wrong representation of `PeerId`s being used in `Kademlia::get_closest_peers`. -- Implemented `FusedStream` for `Swarm`. +- Fixed wrong representation of `PeerId`s being used in + `Kademlia::get_closest_peers`. +- Implemented `FusedStream` for `Swarm`. ## Version 0.16.0 (2020-02-13) -- Removed the `Substream` associated type from the `ProtocolsHandler` trait. The type of the substream is now always `libp2p::swarm::NegotiatedSubstream`. -- As a consequence of the previous change, most of the implementations of the `NetworkBehaviour` trait provided by libp2p (`Ping`, `Identify`, `Kademlia`, `Floodsub`, `Gossipsub`) have lost a generic parameter. -- Removed the first generic parameter (the transport) from `Swarm` and `ExpandedSwarm`. The transport is now abstracted away in the internals of the swarm. -- The `Send` and `'static` bounds are now enforced directly on the `ProtocolsHandler` trait and its associated `InboundUpgrade` and `OutboundUpgrade` implementations. -- Modified `PeerId`s to compare equal across the identity and SHA256 hashes. As a consequence, the `Borrow` implementation of `PeerId` now always returns the bytes representation of a multihash with a SHA256 hash. -- Modified libp2p-floodsub to no longer hash the topic. The new behaviour is now compatible with go-libp2p and js-libp2p, but is a breaking change with regards to rust-libp2p. -- Added libp2p-pnet. It makes it possible to protect networks with a pre-shared key (PSK). -- Modified the `poll_method` parameter of the `NetworkBehaviour` custom derive. The expected method now takes an additional parameter of type `impl PollParameters` to be consistent with the `NetworkBehaviour::poll` method. -- libp2p-noise now compiles for WASM targets. -- Changed libp2p-noise to grow its memory buffers dynamically. This should reduce the overall memory usage of connections that use the noise encryption. -- Fixed libp2p-gossipsub to no longer close the connection if the inbound substream is closed by the remote. -- All crates prefixed with `libp2p-` now use the same version number. -- Added a new variant `ListenerEvent::Error` for listeners to report non-fatal errors. `libp2p-tcp` uses this variant to report errors that happen on remote sockets before they have been accepted and errors when trying to determine the local machine's IP address. +- Removed the `Substream` associated type from the `ProtocolsHandler` trait. The + type of the substream is now always `libp2p::swarm::NegotiatedSubstream`. +- As a consequence of the previous change, most of the implementations of the + `NetworkBehaviour` trait provided by libp2p (`Ping`, `Identify`, `Kademlia`, + `Floodsub`, `Gossipsub`) have lost a generic parameter. +- Removed the first generic parameter (the transport) from `Swarm` and + `ExpandedSwarm`. The transport is now abstracted away in the internals of the + swarm. +- The `Send` and `'static` bounds are now enforced directly on the + `ProtocolsHandler` trait and its associated `InboundUpgrade` and + `OutboundUpgrade` implementations. +- Modified `PeerId`s to compare equal across the identity and SHA256 hashes. As + a consequence, the `Borrow` implementation of `PeerId` now always returns the + bytes representation of a multihash with a SHA256 hash. +- Modified libp2p-floodsub to no longer hash the topic. The new behaviour is now + compatible with go-libp2p and js-libp2p, but is a breaking change with regards + to rust-libp2p. +- Added libp2p-pnet. It makes it possible to protect networks with a pre-shared + key (PSK). +- Modified the `poll_method` parameter of the `NetworkBehaviour` custom derive. + The expected method now takes an additional parameter of type + `impl PollParameters` to be consistent with the `NetworkBehaviour::poll` + method. +- libp2p-noise now compiles for WASM targets. +- Changed libp2p-noise to grow its memory buffers dynamically. This should + reduce the overall memory usage of connections that use the noise encryption. +- Fixed libp2p-gossipsub to no longer close the connection if the inbound + substream is closed by the remote. +- All crates prefixed with `libp2p-` now use the same version number. +- Added a new variant `ListenerEvent::Error` for listeners to report non-fatal + errors. `libp2p-tcp` uses this variant to report errors that happen on remote + sockets before they have been accepted and errors when trying to determine the + local machine's IP address. ## Version 0.15.0 (2020-01-24) -- Added `libp2p-gossipsub`. -- Added `SwarmBuilder::executor` to allow configuring which tasks executor to use. -- Added `TokioTcpConfig` in `libp2p-tcp` and `TokioUdsConfig` in `libp2p-uds` behind `tokio` features. These structs use `tokio` and require a `tokio` runtime executor to be configured via `SwarmBuilder::executor`. -- Changed the `OutboundUpgrade` and `InboundUpgrade` traits to no longer be passed a `Negotiated` but just a `C`. The `Negotiated` is now in the trait bounds requirements of `ProtocolsHandler`. -- Fixed `libp2p-wasm-ext` returning `Err(WouldBlock)` rather than `Pending`. -- Fixed `libp2p-dns` not segregating DNS4 and DNS6. -- Removed some unnecessary `Unpin` requirements on futures. -- Changed `Mdns::new` to no longer be `async`. -- Fixed `libp2p-kad` keeping connections alive when it shouldn't. -- Fixed `InboundUpgrade` not always properly implemented on `NoiseConfig`. +- Added `libp2p-gossipsub`. +- Added `SwarmBuilder::executor` to allow configuring which tasks executor to + use. +- Added `TokioTcpConfig` in `libp2p-tcp` and `TokioUdsConfig` in `libp2p-uds` + behind `tokio` features. These structs use `tokio` and require a `tokio` + runtime executor to be configured via `SwarmBuilder::executor`. +- Changed the `OutboundUpgrade` and `InboundUpgrade` traits to no longer be + passed a `Negotiated` but just a `C`. The `Negotiated` is now in the trait + bounds requirements of `ProtocolsHandler`. +- Fixed `libp2p-wasm-ext` returning `Err(WouldBlock)` rather than `Pending`. +- Fixed `libp2p-dns` not segregating DNS4 and DNS6. +- Removed some unnecessary `Unpin` requirements on futures. +- Changed `Mdns::new` to no longer be `async`. +- Fixed `libp2p-kad` keeping connections alive when it shouldn't. +- Fixed `InboundUpgrade` not always properly implemented on `NoiseConfig`. ## Version 0.14.0-alpha.1 (2020-01-07) -- Upgraded the crate to stable futures. -- Use varints instead of fixed sized (4 byte) integers to delimit plaintext 2.0 messages to align implementation with the specification. -- Refactored the `core::upgrade` module to provide async functions. -- Changed the `Stream` trait implementation of `Swarm` to no longer return a `Result`. -- Added the `Swarm::next` and `Swarm::next_event` functions and the `SwarmEvent` enum. -- Changed `ProtocolsHandler::poll` to no longer return an error. Instead, `ProtocolsHandlerEvent` has a new `Close` variant which corresponds to what an error represented before. -- Changed all the traits that have a `poll` function (i.e. `NetworkBehaviour`, `ProtocolsHandler`, `NodeHandler`) to have an additional `&mut Context` parameter, to reflect the changes in the `Future` trait. -- Revamped the API of `libp2p_websockets::framed`. -- Added protocol string to `Error::UnknownProtocolString`. +- Upgraded the crate to stable futures. +- Use varints instead of fixed sized (4 byte) integers to delimit plaintext 2.0 + messages to align implementation with the specification. +- Refactored the `core::upgrade` module to provide async functions. +- Changed the `Stream` trait implementation of `Swarm` to no longer return a + `Result`. +- Added the `Swarm::next` and `Swarm::next_event` functions and the `SwarmEvent` + enum. +- Changed `ProtocolsHandler::poll` to no longer return an error. Instead, + `ProtocolsHandlerEvent` has a new `Close` variant which corresponds to what an + error represented before. +- Changed all the traits that have a `poll` function (i.e. `NetworkBehaviour`, + `ProtocolsHandler`, `NodeHandler`) to have an additional `&mut Context` + parameter, to reflect the changes in the `Future` trait. +- Revamped the API of `libp2p_websockets::framed`. +- Added protocol string to `Error::UnknownProtocolString`. ## Version 0.13.2 (2020-01-02) -- Fixed the `libp2p-noise` handshake not flushing the underlying stream before waiting for a response. -- Fixed semver issue with the `protobuf` crate. +- Fixed the `libp2p-noise` handshake not flushing the underlying stream before + waiting for a response. +- Fixed semver issue with the `protobuf` crate. ## Version 0.13.1 (2019-11-13) -- Maintenance release to bump dependencies and deal with an accidental breaking change in multihash 0.1.4. +- Maintenance release to bump dependencies and deal with an accidental breaking + change in multihash 0.1.4. ## Version 0.13.0 (2019-11-05) -- Reworked the transport upgrade API. See https://github.com/libp2p/rust-libp2p/pull/1240 for more information. -- Added a parameter allowing to choose the protocol negotiation protocol when upgrading a connection or a substream. See https://github.com/libp2p/rust-libp2p/pull/1245 for more information. -- Added an alternative `multistream-select` protocol called `V1Lazy`. -- Added `PlainText2Config` that implements the `/plaintext/2.0.0` protocol. -- Refactored `libp2p-identify`. Some items have been renamed. -- Now accepting `PeerId`s using the `identity` hashing algorithm as valid. -- Removed `libp2p-observed` and `libp2p-ratelimit`. -- Fixed mDNS long peer IDs not being transmitted properly. -- Added some `Debug` trait implementations. -- Fixed potential arithmetic overflows in `libp2p-kad` and `multistream-select`. +- Reworked the transport upgrade API. See + https://github.com/libp2p/rust-libp2p/pull/1240 for more information. +- Added a parameter allowing to choose the protocol negotiation protocol when + upgrading a connection or a substream. See + https://github.com/libp2p/rust-libp2p/pull/1245 for more information. +- Added an alternative `multistream-select` protocol called `V1Lazy`. +- Added `PlainText2Config` that implements the `/plaintext/2.0.0` protocol. +- Refactored `libp2p-identify`. Some items have been renamed. +- Now accepting `PeerId`s using the `identity` hashing algorithm as valid. +- Removed `libp2p-observed` and `libp2p-ratelimit`. +- Fixed mDNS long peer IDs not being transmitted properly. +- Added some `Debug` trait implementations. +- Fixed potential arithmetic overflows in `libp2p-kad` and `multistream-select`. ## Version 0.12.0 (2019-08-15) -- In some situations, `multistream-select` will now assume that protocol negotiation immediately succeeds. If it turns out that it failed, an error is generated when reading or writing from/to the stream. -- Replaced `listen_addr` with `local_addr` in events related to incoming connections. The address no longer has to match a previously-reported address. -- Listeners now have an identifier and can be stopped. -- Added `NetworkBehaviour::inject_listener_error` and `NetworkBehaviour::inject_listener_closed`. For diagnostic purposes, listeners can now report errors on incoming connections, such as when calling `accept(2)` fails. -- Fixed tasks sometimes not being notified when a network event happens in `libp2p-mplex`. -- Fixed a memory leak in `libp2p-kad`. -- Added `Toggle::is_enabled()`. -- Removed `IdentifyTransport`. +- In some situations, `multistream-select` will now assume that protocol + negotiation immediately succeeds. If it turns out that it failed, an error is + generated when reading or writing from/to the stream. +- Replaced `listen_addr` with `local_addr` in events related to incoming + connections. The address no longer has to match a previously-reported address. +- Listeners now have an identifier and can be stopped. +- Added `NetworkBehaviour::inject_listener_error` and + `NetworkBehaviour::inject_listener_closed`. For diagnostic purposes, listeners + can now report errors on incoming connections, such as when calling + `accept(2)` fails. +- Fixed tasks sometimes not being notified when a network event happens in + `libp2p-mplex`. +- Fixed a memory leak in `libp2p-kad`. +- Added `Toggle::is_enabled()`. +- Removed `IdentifyTransport`. ## Version 0.11.0 (2019-07-18) -- `libp2p-kad`: Completed the core functionality of the record storage API, thereby extending the `RecordStore` for provider records. All records expire by default and are subject to regular republication and caching as per the Kademlia spec(s). Expiration and publication intervals are configurable through the `KademliaConfig`. -- `libp2p-kad`: The routing table now never stores peers without a known (listen) address. In particular, on receiving a new inbound connection, the Kademlia behaviour now emits `KademliaEvent::UnroutablePeer` to indicate that in order for the peer to be added to the routing table and hence considered a reachable node in the DHT, a listen address of the peer must be discovered and reported via `Kademlia::add_address`. This is usually achieved through the use of the `Identify` protocol on the same connection(s). -- `libp2p-kad`: Documentation updates. -- Extracted the `swarm` and `protocols_handler`-related contents from `libp2p-core` to a new `libp2p-swarm` crate. -- Renamed `RawSwarm` to `Network`. -- Added `Floodsub::publish_any`. -- Replaced unbounded channels with bounded ones at the boundary between the `Network` (formerly `RawSwarm`) and `NodeHandler`. The node handlers will now wait if the main task is busy, instead of continuing to push events to the channel. -- Fixed the `address_translation` function ignoring `/dns` addresses. +- `libp2p-kad`: Completed the core functionality of the record storage API, + thereby extending the `RecordStore` for provider records. All records expire + by default and are subject to regular republication and caching as per the + Kademlia spec(s). Expiration and publication intervals are configurable + through the `KademliaConfig`. +- `libp2p-kad`: The routing table now never stores peers without a known + (listen) address. In particular, on receiving a new inbound connection, the + Kademlia behaviour now emits `KademliaEvent::UnroutablePeer` to indicate that + in order for the peer to be added to the routing table and hence considered a + reachable node in the DHT, a listen address of the peer must be discovered and + reported via `Kademlia::add_address`. This is usually achieved through the use + of the `Identify` protocol on the same connection(s). +- `libp2p-kad`: Documentation updates. +- Extracted the `swarm` and `protocols_handler`-related contents from + `libp2p-core` to a new `libp2p-swarm` crate. +- Renamed `RawSwarm` to `Network`. +- Added `Floodsub::publish_any`. +- Replaced unbounded channels with bounded ones at the boundary between the + `Network` (formerly `RawSwarm`) and `NodeHandler`. The node handlers will now + wait if the main task is busy, instead of continuing to push events to the + channel. +- Fixed the `address_translation` function ignoring `/dns` addresses. ## Version 0.10.0 (2019-06-25) -- `PollParameters` is now a trait instead of a struct. -- The `Swarm` can now be customized with connection information. -- Fixed write-only substreams now delivering data properly. -- Fixed the TCP listener accidentally shutting down if an incoming socket was closed too quickly. -- Improved the heuristics for determining external multiaddresses based on reports. -- Various fixes to Kademlia iterative queries and the WebSockets transport. +- `PollParameters` is now a trait instead of a struct. +- The `Swarm` can now be customized with connection information. +- Fixed write-only substreams now delivering data properly. +- Fixed the TCP listener accidentally shutting down if an incoming socket was + closed too quickly. +- Improved the heuristics for determining external multiaddresses based on + reports. +- Various fixes to Kademlia iterative queries and the WebSockets transport. ## Version 0.9.1 (2019-06-05) -- `EitherOutput` now implements `Stream` and `Sink` if their variants also implement these traits. -- `libp2p::websocket::error::Error` now implements `Sync`. +- `EitherOutput` now implements `Stream` and `Sink` if their variants also + implement these traits. +- `libp2p::websocket::error::Error` now implements `Sync`. ## Version 0.9.0 (2019-06-04) -- Major fixes and performance improvements to libp2p-kad. -- Initial prototype for record storage in libp2p-kad. -- Rewrote the implementation of WebSockets. It now properly supports WebSockets Secure (WSS). -- Removed `BrowserWsConfig`. Please use `libp2p::wasm_ext::ExtTransport` instead. -- Added a `Path` parameter to `multiaddr::Protocol::WS` and `WSS`. The string representation when a path is present is respectively `x-parity-ws/` and `x-parity-wss/` where `` is percent-encoded. -- Fixed an issue with `libp2p-tcp` where the wrong listened address was returned, if the actual address was loopback. -- Added `core::upgrade::OptionalUpgrade`. -- Added some utility functions in `core::identity::secp256k1`. -- It is now possible to inject an artificial connection in the `RawSwarm`. +- Major fixes and performance improvements to libp2p-kad. +- Initial prototype for record storage in libp2p-kad. +- Rewrote the implementation of WebSockets. It now properly supports WebSockets + Secure (WSS). +- Removed `BrowserWsConfig`. Please use `libp2p::wasm_ext::ExtTransport` + instead. +- Added a `Path` parameter to `multiaddr::Protocol::WS` and `WSS`. The string + representation when a path is present is respectively `x-parity-ws/` and + `x-parity-wss/` where `` is percent-encoded. +- Fixed an issue with `libp2p-tcp` where the wrong listened address was + returned, if the actual address was loopback. +- Added `core::upgrade::OptionalUpgrade`. +- Added some utility functions in `core::identity::secp256k1`. +- It is now possible to inject an artificial connection in the `RawSwarm`. ## Version 0.8.1 (2019-05-15) -- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. +- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. ## Version 0.8.0 (2019-05-15) -- Crate now successfully runs from within the browser when compiled to WASM. -- Modified the constructors of `NoiseConfig` to accept any type of public key. The Noise handshake has consequently been modified. -- Changed the `StreamMuxer` trait to have an `Error` associated type. -- The `Swarm` now ranks externally-visible multiaddresses by how often they have been reported, ensuring that weird or malicious reports don't affect connectivity too much. -- Added `IntoProtocolsHandler::inbound_protocol`. Must return the same value as what `ProtocolsHandler::listen_protocol` would return. -- `IntoProtocolsHandler::into_handler` now takes a second parameter with the `&ConnectedPoint` to the node we are connected to. -- Replaced the `secp256k1` crate with `libsecp256k1`. -- Fixed `Kademlia::add_providing` taking a `PeerId` instead of a `Multihash`. -- Fixed various bugs in the implementation of `Kademlia`. -- Added `OneSubstreamMuxer`. -- Added the `libp2p-wasm-ext` crate. -- Added `multiaddr::from_url`. -- Added `OptionalTransport`. +- Crate now successfully runs from within the browser when compiled to WASM. +- Modified the constructors of `NoiseConfig` to accept any type of public key. + The Noise handshake has consequently been modified. +- Changed the `StreamMuxer` trait to have an `Error` associated type. +- The `Swarm` now ranks externally-visible multiaddresses by how often they have + been reported, ensuring that weird or malicious reports don't affect + connectivity too much. +- Added `IntoProtocolsHandler::inbound_protocol`. Must return the same value as + what `ProtocolsHandler::listen_protocol` would return. +- `IntoProtocolsHandler::into_handler` now takes a second parameter with the + `&ConnectedPoint` to the node we are connected to. +- Replaced the `secp256k1` crate with `libsecp256k1`. +- Fixed `Kademlia::add_providing` taking a `PeerId` instead of a `Multihash`. +- Fixed various bugs in the implementation of `Kademlia`. +- Added `OneSubstreamMuxer`. +- Added the `libp2p-wasm-ext` crate. +- Added `multiaddr::from_url`. +- Added `OptionalTransport`. ## Version 0.7.1 (2019-05-15) -- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. +- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. ## Version 0.7.0 (2019-04-23) -- Fixed the inactive connections shutdown mechanism not working. -- `Transport::listen_on` must now return a `Stream` that produces `ListenEvent`s. This makes it possible to notify about listened addresses at a later point in time. -- `Transport::listen_on` no longer returns an address we're listening on. This is done through `ListenEvent`s. All other `listen_on` methods have been updated accordingly. -- Added `NetworkBehaviour::inject_new_listen_addr`, `NetworkBehaviour::inject_expired_listen_addr` and `NetworkBehaviour::inject_new_external_addr`. -- `ProtocolsHandler::listen_protocol` and `ProtocolsHandlerEvent::OutboundSubstreamRequest` must now return a `SubstreamProtocol` struct containing a timeout for the upgrade. -- `Ping::new` now requires a `PingConfig`, which can be created with `PingConfig::new`. -- Removed `Transport::nat_traversal` in favour of a stand-alone `address_translation` function in `libp2p-core`. -- Reworked the API of `Multiaddr`. -- Removed the `ToMultiaddr` trait in favour of `TryFrom`. -- Added `Swarm::ban_peer_id` and `Swarm::unban_peer_id`. -- The `TPeerId` generic parameter of `RawSwarm` is now `TConnInfo` and must now implement a `ConnectionInfo` trait. -- Reworked the `PingEvent`. -- Renamed `KeepAlive::Forever` to `Yes` and `KeepAlive::Now` to `No`. +- Fixed the inactive connections shutdown mechanism not working. +- `Transport::listen_on` must now return a `Stream` that produces + `ListenEvent`s. This makes it possible to notify about listened addresses at a + later point in time. +- `Transport::listen_on` no longer returns an address we're listening on. This + is done through `ListenEvent`s. All other `listen_on` methods have been + updated accordingly. +- Added `NetworkBehaviour::inject_new_listen_addr`, + `NetworkBehaviour::inject_expired_listen_addr` and + `NetworkBehaviour::inject_new_external_addr`. +- `ProtocolsHandler::listen_protocol` and + `ProtocolsHandlerEvent::OutboundSubstreamRequest` must now return a + `SubstreamProtocol` struct containing a timeout for the upgrade. +- `Ping::new` now requires a `PingConfig`, which can be created with + `PingConfig::new`. +- Removed `Transport::nat_traversal` in favour of a stand-alone + `address_translation` function in `libp2p-core`. +- Reworked the API of `Multiaddr`. +- Removed the `ToMultiaddr` trait in favour of `TryFrom`. +- Added `Swarm::ban_peer_id` and `Swarm::unban_peer_id`. +- The `TPeerId` generic parameter of `RawSwarm` is now `TConnInfo` and must now + implement a `ConnectionInfo` trait. +- Reworked the `PingEvent`. +- Renamed `KeepAlive::Forever` to `Yes` and `KeepAlive::Now` to `No`. ## Version 0.6.0 (2019-03-29) -- Replaced `NetworkBehaviour::inject_dial_failure` with `inject_dial_failure` and - `inject_addr_reach_failure`. The former is called when we have finished trying to dial a node - without success, while the latter is called when we have failed to reach a specific address. -- Fixed Kademlia storing a different hash than the reference implementation. -- Lots of bugfixes in Kademlia. -- Modified the `InboundUpgrade` and `OutboundUpgrade` trait to take a `Negotiated` instead - of `TSocket`. -- `PollParameters::external_addresses` now returns `Multiaddr`es as reference instead of by value. -- Added `Swarm::external_addresses`. -- Added a `core::swarm::toggle::Toggle` that allows having a disabled `NetworkBehaviour`. +- Replaced `NetworkBehaviour::inject_dial_failure` with `inject_dial_failure` + and `inject_addr_reach_failure`. The former is called when we have finished + trying to dial a node without success, while the latter is called when we have + failed to reach a specific address. +- Fixed Kademlia storing a different hash than the reference implementation. +- Lots of bugfixes in Kademlia. +- Modified the `InboundUpgrade` and `OutboundUpgrade` trait to take a + `Negotiated` instead of `TSocket`. +- `PollParameters::external_addresses` now returns `Multiaddr`es as reference + instead of by value. +- Added `Swarm::external_addresses`. +- Added a `core::swarm::toggle::Toggle` that allows having a disabled + `NetworkBehaviour`. ## Version 0.5.0 (2019-03-13) -- Moved the `SecioKeypair` struct in `core/identity` and renamed it to `Keypair`. -- mplex now supports half-closed substreams. -- Renamed `StreamMuxer::shutdown()` to `close()`. -- Closing a muxer with the `close()` method (formerly `shutdown`) now "destroys" all the existing substreams. After `close()` as been called, they all return either EOF or an error. -- The `shutdown_substream()` method now closes only the writing side of the substream, and you can continue reading from it until EOF or until you delete it. This was actually already more or less the case before, but it wasn't properly reflected in the API or the documentation. -- `poll_inbound()` and `poll_outbound()` no longer return an `Option`, as `None` was the same as returning an error. -- Removed the `NodeClosed` events and renamed `NodeError` to `NodeClosed`. From the API's point of view, a connection now always closes with an error. -- Added the `NodeHandlerWrapperError` enum that describes an error generated by the protocols handlers grouped together. It is either `UselessTimeout` or `Handler`. This allows properly reporting closing a connection because it is useless. -- Removed `NodeHandler::inject_inbound_closed`, `NodeHandler::inject_outbound_closed`, `NodeHandler::shutdown`, and `ProtocolsHandler::shutdown`. The handler is now dropped when a shutdown process starts. This should greatly simplify writing a handler. -- `StreamMuxer::close` now implies `flush_all`. -- Removed the `Shutdown` enum from `stream_muxer`. -- Removed `ProtocolsHandler::fuse()`. -- Reworked some API of `core/nodes/node.rs` and `core/nodes/handled_node.rs`. -- The core now works even outside of a tokio context. +- Moved the `SecioKeypair` struct in `core/identity` and renamed it to + `Keypair`. +- mplex now supports half-closed substreams. +- Renamed `StreamMuxer::shutdown()` to `close()`. +- Closing a muxer with the `close()` method (formerly `shutdown`) now "destroys" + all the existing substreams. After `close()` as been called, they all return + either EOF or an error. +- The `shutdown_substream()` method now closes only the writing side of the + substream, and you can continue reading from it until EOF or until you delete + it. This was actually already more or less the case before, but it wasn't + properly reflected in the API or the documentation. +- `poll_inbound()` and `poll_outbound()` no longer return an `Option`, as `None` + was the same as returning an error. +- Removed the `NodeClosed` events and renamed `NodeError` to `NodeClosed`. From + the API's point of view, a connection now always closes with an error. +- Added the `NodeHandlerWrapperError` enum that describes an error generated by + the protocols handlers grouped together. It is either `UselessTimeout` or + `Handler`. This allows properly reporting closing a connection because it is + useless. +- Removed `NodeHandler::inject_inbound_closed`, + `NodeHandler::inject_outbound_closed`, `NodeHandler::shutdown`, and + `ProtocolsHandler::shutdown`. The handler is now dropped when a shutdown + process starts. This should greatly simplify writing a handler. +- `StreamMuxer::close` now implies `flush_all`. +- Removed the `Shutdown` enum from `stream_muxer`. +- Removed `ProtocolsHandler::fuse()`. +- Reworked some API of `core/nodes/node.rs` and `core/nodes/handled_node.rs`. +- The core now works even outside of a tokio context. ## Version 0.4.2 (2019-02-27) -- Fixed periodic pinging not working. +- Fixed periodic pinging not working. ## Version 0.4.1 (2019-02-20) -- Fixed wrong version of libp2p-noise. +- Fixed wrong version of libp2p-noise. ## Version 0.4.0 (2019-02-20) -- The `multiaddr!` macro has been moved to the `multiaddr` crate and is now reexported under the name `build_multiaddr!`. -- Modified the functions in `upgrade::transfer` to be more convenient to use. -- Now properly sending external addresses in the identify protocol. -- Fixed duplicate addresses being reported in identify and Kademlia. -- Fixed infinite looping in the functions in `upgrade::transfer`. -- Fixed infinite loop on graceful node shutdown with the `ProtocolsHandlerSelect`. -- Fixed various issues with nodes dialing each other simultaneously. -- Added the `StreamMuxer::is_remote_acknowledged()` method. -- Added a `BandwidthLogging` transport wrapper that logs the bandwidth consumption. -- The addresses to try dialing when dialing a node is now refreshed by the `Swarm` when necessary. -- Lots of modifications to the semi-private structs in `core/nodes`. -- Added `IdentifyEvent::SendBack`, when we send back our information. -- Rewrote the `MemoryTransport` to be similar to the `TcpConfig`. +- The `multiaddr!` macro has been moved to the `multiaddr` crate and is now + reexported under the name `build_multiaddr!`. +- Modified the functions in `upgrade::transfer` to be more convenient to use. +- Now properly sending external addresses in the identify protocol. +- Fixed duplicate addresses being reported in identify and Kademlia. +- Fixed infinite looping in the functions in `upgrade::transfer`. +- Fixed infinite loop on graceful node shutdown with the + `ProtocolsHandlerSelect`. +- Fixed various issues with nodes dialing each other simultaneously. +- Added the `StreamMuxer::is_remote_acknowledged()` method. +- Added a `BandwidthLogging` transport wrapper that logs the bandwidth + consumption. +- The addresses to try dialing when dialing a node is now refreshed by the + `Swarm` when necessary. +- Lots of modifications to the semi-private structs in `core/nodes`. +- Added `IdentifyEvent::SendBack`, when we send back our information. +- Rewrote the `MemoryTransport` to be similar to the `TcpConfig`. ## Version 0.3.1 (2019-02-02) -- Added `NetworkBehaviour::inject_replaced` that is called whenever we replace a connection with a different connection to the same peer. -- Fixed various issues with Kademlia. +- Added `NetworkBehaviour::inject_replaced` that is called whenever we replace a + connection with a different connection to the same peer. +- Fixed various issues with Kademlia. ## Version 0.3.0 (2019-01-30) -- Removed the `topology` module and everything it contained, including the `Topology` trait. -- Added `libp2p-noise` that supports Noise handshakes, as an alternative to `libp2p-secio`. -- Updated `ring` to version 0.14. -- Creating a `Swarm` now expects the `PeerId` of the local node, instead of a `Topology`. -- Added `NetworkBehaviour::addresses_of_peer` that returns the addresses a `NetworkBehaviour` knows about a given peer. This exists as a replacement for the topology. -- The `Kademlia` and `Mdns` behaviours now report and store the list of addresses they discover. -- You must now call `Floodsub::add_node_to_partial_view()` and `Floodsub::remove_node_from_partial_view` to add/remove nodes from the list of nodes that floodsub must send messages to. -- Added `NetworkBehaviour::inject_dial_failure` that is called when we fail to dial an address. -- `ProtocolsHandler::connection_keep_alive()` now returns a `KeepAlive` enum that provides more fine grained control. -- The `NodeHandlerWrapper` no longer has a 5 seconds inactivity timeout. This is now handled entirely by `ProtocolsHandler::connection_keep_alive()`. -- Now properly denying connections incoming from the same `PeerId` as ours. -- Added a `SwarmBuilder`. The `incoming_limit` method lets you configure the number of simultaneous incoming connections. -- Removed `FloodsubHandler`, `PingListenHandler` and `PeriodicPingHandler`. -- The structs in `core::nodes` are now generic over the `PeerId`. -- Added `SecioKeypair::ed25519_raw_key()`. -- Fix improper connection shutdown in `ProtocolsHandler`. +- Removed the `topology` module and everything it contained, including the + `Topology` trait. +- Added `libp2p-noise` that supports Noise handshakes, as an alternative to + `libp2p-secio`. +- Updated `ring` to version 0.14. +- Creating a `Swarm` now expects the `PeerId` of the local node, instead of a + `Topology`. +- Added `NetworkBehaviour::addresses_of_peer` that returns the addresses a + `NetworkBehaviour` knows about a given peer. This exists as a replacement for + the topology. +- The `Kademlia` and `Mdns` behaviours now report and store the list of + addresses they discover. +- You must now call `Floodsub::add_node_to_partial_view()` and + `Floodsub::remove_node_from_partial_view` to add/remove nodes from the list of + nodes that floodsub must send messages to. +- Added `NetworkBehaviour::inject_dial_failure` that is called when we fail to + dial an address. +- `ProtocolsHandler::connection_keep_alive()` now returns a `KeepAlive` enum + that provides more fine grained control. +- The `NodeHandlerWrapper` no longer has a 5 seconds inactivity timeout. This is + now handled entirely by `ProtocolsHandler::connection_keep_alive()`. +- Now properly denying connections incoming from the same `PeerId` as ours. +- Added a `SwarmBuilder`. The `incoming_limit` method lets you configure the + number of simultaneous incoming connections. +- Removed `FloodsubHandler`, `PingListenHandler` and `PeriodicPingHandler`. +- The structs in `core::nodes` are now generic over the `PeerId`. +- Added `SecioKeypair::ed25519_raw_key()`. +- Fix improper connection shutdown in `ProtocolsHandler`. ## Version 0.2.2 (2019-01-14) -- Fixed improper dependencies versions causing deriving `NetworkBehaviour` to generate an error. +- Fixed improper dependencies versions causing deriving `NetworkBehaviour` to + generate an error. ## Version 0.2.1 (2019-01-14) -- Added the `IntoNodeHandler` and `IntoProtocolsHandler` traits, allowing node handlers and protocol handlers to know the `PeerId` of the node they are interacting with. +- Added the `IntoNodeHandler` and `IntoProtocolsHandler` traits, allowing node + handlers and protocol handlers to know the `PeerId` of the node they are + interacting with. ## Version 0.2 (2019-01-10) -- The `Transport` trait now has an `Error` associated type instead of always using `std::io::Error`. -- Merged `PeriodicPing` and `PingListen` into one `Ping` behaviour. -- `Floodsub` now generates `FloodsubEvent`s instead of direct floodsub messages. -- Added `ProtocolsHandler::connection_keep_alive`. If all the handlers return `false`, then the connection to the remote node will automatically be gracefully closed after a few seconds. -- The crate now successfully compiles for the `wasm32-unknown-unknown` target. -- Updated `ring` to version 0.13. -- Updated `secp256k1` to version 0.12. -- The enum returned by `RawSwarm::peer()` can now return `LocalNode`. This makes it impossible to accidentally attempt to dial the local node. -- Removed `Transport::map_err_dial`. -- Removed the `Result` from some connection-related methods in the `RawSwarm`, as they could never error. -- If a node doesn't respond to pings, we now generate an error on the connection instead of trying to gracefully close it. +- The `Transport` trait now has an `Error` associated type instead of always + using `std::io::Error`. +- Merged `PeriodicPing` and `PingListen` into one `Ping` behaviour. +- `Floodsub` now generates `FloodsubEvent`s instead of direct floodsub messages. +- Added `ProtocolsHandler::connection_keep_alive`. If all the handlers return + `false`, then the connection to the remote node will automatically be + gracefully closed after a few seconds. +- The crate now successfully compiles for the `wasm32-unknown-unknown` target. +- Updated `ring` to version 0.13. +- Updated `secp256k1` to version 0.12. +- The enum returned by `RawSwarm::peer()` can now return `LocalNode`. This makes + it impossible to accidentally attempt to dial the local node. +- Removed `Transport::map_err_dial`. +- Removed the `Result` from some connection-related methods in the `RawSwarm`, + as they could never error. +- If a node doesn't respond to pings, we now generate an error on the connection + instead of trying to gracefully close it. diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 7d0360646d6..1a6d8f2159b 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -68,7 +68,7 @@ pub mod dial_opts; pub mod dummy; pub mod handler; mod listen_opts; -pub mod translation; +mod translation; /// Bundles all symbols required for the [`libp2p_swarm_derive::NetworkBehaviour`] macro. #[doc(hidden)] From cba6fb9ffbfab93503fc5a86bd65d9b7597f610d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:52:59 +0900 Subject: [PATCH 158/179] Revert bad changes from #9d84b7c --- Cargo.lock | 10 +- examples/autonatv2/Cargo.toml | 38 + examples/autonatv2/Dockerfile | 20 + examples/autonatv2/docker-compose.yml | 16 + .../autonatv2/src/bin/autonatv2_client.rs | 111 +++ .../autonatv2/src/bin/autonatv2_server.rs | 87 +++ examples/autonatv2/src/lib.rs | 1 + protocols/autonat/CHANGELOG.md | 12 - protocols/autonat/Cargo.toml | 35 +- protocols/autonat/src/lib.rs | 46 +- protocols/autonat/src/v1.rs | 42 ++ protocols/autonat/src/v1/behaviour.rs | 689 ++++++++++++++++++ .../autonat/src/v1/behaviour/as_client.rs | 361 +++++++++ .../autonat/src/v1/behaviour/as_server.rs | 426 +++++++++++ protocols/autonat/src/v1/generated/mod.rs | 2 + .../autonat/src/v1/generated/structs.proto | 37 + protocols/autonat/src/v1/generated/structs.rs | 242 ++++++ protocols/autonat/src/v1/protocol.rs | 343 +++++++++ protocols/autonat/src/v2.rs | 17 + protocols/autonat/src/v2/client.rs | 5 + protocols/autonat/src/v2/client/behaviour.rs | 439 +++++++++++ protocols/autonat/src/v2/client/handler.rs | 2 + .../src/v2/client/handler/dial_back.rs | 141 ++++ .../src/v2/client/handler/dial_request.rs | 343 +++++++++ protocols/autonat/src/v2/generated/mod.rs | 2 + .../autonat/src/v2/generated/structs.proto | 54 ++ protocols/autonat/src/v2/generated/structs.rs | 403 ++++++++++ protocols/autonat/src/v2/protocol.rs | 361 +++++++++ protocols/autonat/src/v2/server.rs | 5 + protocols/autonat/src/v2/server/behaviour.rs | 155 ++++ protocols/autonat/src/v2/server/handler.rs | 8 + .../src/v2/server/handler/dial_back.rs | 140 ++++ .../src/v2/server/handler/dial_request.rs | 332 +++++++++ protocols/autonat/tests/autonatv2.rs | 568 +++++++++++++++ protocols/autonat/tests/test_client.rs | 1 + protocols/autonat/tests/test_server.rs | 1 + 36 files changed, 5433 insertions(+), 62 deletions(-) create mode 100644 examples/autonatv2/Cargo.toml create mode 100644 examples/autonatv2/Dockerfile create mode 100644 examples/autonatv2/docker-compose.yml create mode 100644 examples/autonatv2/src/bin/autonatv2_client.rs create mode 100644 examples/autonatv2/src/bin/autonatv2_server.rs create mode 100644 examples/autonatv2/src/lib.rs create mode 100644 protocols/autonat/src/v1.rs create mode 100644 protocols/autonat/src/v1/behaviour.rs create mode 100644 protocols/autonat/src/v1/behaviour/as_client.rs create mode 100644 protocols/autonat/src/v1/behaviour/as_server.rs create mode 100644 protocols/autonat/src/v1/generated/mod.rs create mode 100644 protocols/autonat/src/v1/generated/structs.proto create mode 100644 protocols/autonat/src/v1/generated/structs.rs create mode 100644 protocols/autonat/src/v1/protocol.rs create mode 100644 protocols/autonat/src/v2.rs create mode 100644 protocols/autonat/src/v2/client.rs create mode 100644 protocols/autonat/src/v2/client/behaviour.rs create mode 100644 protocols/autonat/src/v2/client/handler.rs create mode 100644 protocols/autonat/src/v2/client/handler/dial_back.rs create mode 100644 protocols/autonat/src/v2/client/handler/dial_request.rs create mode 100644 protocols/autonat/src/v2/generated/mod.rs create mode 100644 protocols/autonat/src/v2/generated/structs.proto create mode 100644 protocols/autonat/src/v2/generated/structs.rs create mode 100644 protocols/autonat/src/v2/protocol.rs create mode 100644 protocols/autonat/src/v2/server.rs create mode 100644 protocols/autonat/src/v2/server/behaviour.rs create mode 100644 protocols/autonat/src/v2/server/handler.rs create mode 100644 protocols/autonat/src/v2/server/handler/dial_back.rs create mode 100644 protocols/autonat/src/v2/server/handler/dial_request.rs create mode 100644 protocols/autonat/tests/autonatv2.rs diff --git a/Cargo.lock b/Cargo.lock index 03694d6c58b..45af0a19e41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2664,9 +2664,14 @@ dependencies = [ "async-std", "async-trait", "asynchronous-codec", + "bytes", + "either", "futures", + "futures-bounded", "futures-timer", + "instant", "libp2p-core", + "libp2p-identify", "libp2p-identity", "libp2p-request-response", "libp2p-swarm", @@ -2674,9 +2679,12 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", + "rand_core 0.6.4", + "thiserror", + "tokio", "tracing", "tracing-subscriber", - "web-time", + "void", ] [[package]] diff --git a/examples/autonatv2/Cargo.toml b/examples/autonatv2/Cargo.toml new file mode 100644 index 00000000000..6c862ee22e4 --- /dev/null +++ b/examples/autonatv2/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "autonatv2" +version = "0.1.0" +edition = "2021" +publish = false +license = "MIT or Apache-2.0" + +[package.metadata.release] +release = false + +[[bin]] +name = "autonatv2_client" + +[[bin]] +name = "autonatv2_server" + +[dependencies] +libp2p = { workspace = true, features = ["macros", "tokio", "tcp", "noise", "yamux", "autonat", "identify", "dns", "quic"] } +clap = { version = "4.4.18", features = ["derive"] } +tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +rand = "0.8.5" +opentelemetry = { version = "0.21.0", optional = true } +opentelemetry_sdk = { version = "0.21.1", optional = true, features = ["rt-tokio"] } +tracing-opentelemetry = { version = "0.22.0", optional = true } +opentelemetry-jaeger = { version = "0.20.0", optional = true, features = ["rt-tokio"] } +cfg-if = "1.0.0" + +[features] +jaeger = ["opentelemetry", "opentelemetry_sdk", "tracing-opentelemetry", "opentelemetry-jaeger"] +opentelemetry = ["dep:opentelemetry"] +opentelemetry_sdk = ["dep:opentelemetry_sdk"] +tracing-opentelemetry = ["dep:tracing-opentelemetry"] +opentelemetry-jaeger = ["dep:opentelemetry-jaeger"] + +[lints] +workspace = true diff --git a/examples/autonatv2/Dockerfile b/examples/autonatv2/Dockerfile new file mode 100644 index 00000000000..5a523649d80 --- /dev/null +++ b/examples/autonatv2/Dockerfile @@ -0,0 +1,20 @@ +FROM rust:1.75-alpine as builder + +RUN apk add musl-dev + +WORKDIR /workspace +COPY . . +RUN --mount=type=cache,target=./target \ + --mount=type=cache,target=/usr/local/cargo/registry \ + cargo build --release --package autonatv2 --bin autonatv2_server -F jaeger + +RUN --mount=type=cache,target=./target \ + mv ./target/release/autonatv2_server /usr/local/bin/autonatv2_server + +FROM alpine:latest + +COPY --from=builder /usr/local/bin/autonatv2_server /app/autonatv2_server + +EXPOSE 4884 + +ENTRYPOINT [ "/app/autonatv2_server", "-l", "4884" ] diff --git a/examples/autonatv2/docker-compose.yml b/examples/autonatv2/docker-compose.yml new file mode 100644 index 00000000000..75f44e7e6f9 --- /dev/null +++ b/examples/autonatv2/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + autonatv2: + build: + context: ../.. + dockerfile: examples/autonatv2/Dockerfile + ports: + - 4884:4884 + jaeger: + image: jaegertracing/all-in-one + ports: + - 6831:6831/udp + - 6832:6832/udp + - 16686:16686 + - 14268:14268 diff --git a/examples/autonatv2/src/bin/autonatv2_client.rs b/examples/autonatv2/src/bin/autonatv2_client.rs new file mode 100644 index 00000000000..de902514dd8 --- /dev/null +++ b/examples/autonatv2/src/bin/autonatv2_client.rs @@ -0,0 +1,111 @@ +use std::{error::Error, net::Ipv4Addr, time::Duration}; + +use clap::Parser; +use libp2p::{ + autonat, + futures::StreamExt, + identify, identity, + multiaddr::Protocol, + noise, + swarm::{dial_opts::DialOpts, NetworkBehaviour, SwarmEvent}, + tcp, yamux, Multiaddr, SwarmBuilder, +}; +use rand::rngs::OsRng; +use tracing_subscriber::EnvFilter; + +#[derive(Debug, Parser)] +#[clap(name = "libp2p autonatv2 client")] +struct Opt { + /// Port where the client will listen for incoming connections. + #[clap(short = 'p', long, default_value_t = 0)] + listen_port: u16, + + /// Address of the server where want to connect to. + #[clap(short = 'a', long)] + server_address: Multiaddr, + + /// Probe interval in seconds. + #[clap(short = 't', long, default_value = "2")] + probe_interval: u64, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let opt = Opt::parse(); + + let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_quic() + .with_dns()? + .with_behaviour(|key| Behaviour::new(key.public(), opt.probe_interval))? + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) + .build(); + + swarm.listen_on( + Multiaddr::empty() + .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) + .with(Protocol::Tcp(opt.listen_port)), + )?; + + swarm.dial( + DialOpts::unknown_peer_id() + .address(opt.server_address) + .build(), + )?; + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => { + println!("Listening on {address:?}"); + } + SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { + server, + tested_addr, + bytes_sent, + result: Ok(()), + })) => { + println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Everything Ok and verified."); + } + SwarmEvent::Behaviour(BehaviourEvent::Autonat(autonat::v2::client::Event { + server, + tested_addr, + bytes_sent, + result: Err(e), + })) => { + println!("Tested {tested_addr} with {server}. Sent {bytes_sent} bytes for verification. Failed with {e:?}."); + } + SwarmEvent::ExternalAddrConfirmed { address } => { + println!("External address confirmed: {address}"); + } + _ => {} + } + } +} + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + autonat: autonat::v2::client::Behaviour, + identify: identify::Behaviour, +} + +impl Behaviour { + pub fn new(key: identity::PublicKey, probe_interval: u64) -> Self { + Self { + autonat: autonat::v2::client::Behaviour::new( + OsRng, + autonat::v2::client::Config::default() + .with_probe_interval(Duration::from_secs(probe_interval)), + ), + identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), + } + } +} diff --git a/examples/autonatv2/src/bin/autonatv2_server.rs b/examples/autonatv2/src/bin/autonatv2_server.rs new file mode 100644 index 00000000000..849ed3b3b0a --- /dev/null +++ b/examples/autonatv2/src/bin/autonatv2_server.rs @@ -0,0 +1,87 @@ +use std::{error::Error, net::Ipv4Addr, time::Duration}; + +use cfg_if::cfg_if; +use clap::Parser; +use libp2p::{ + autonat, + futures::StreamExt, + identify, identity, + multiaddr::Protocol, + noise, + swarm::{NetworkBehaviour, SwarmEvent}, + tcp, yamux, Multiaddr, SwarmBuilder, +}; +use rand::rngs::OsRng; + +#[derive(Debug, Parser)] +#[clap(name = "libp2p autonatv2 server")] +struct Opt { + #[clap(short, long, default_value_t = 0)] + listen_port: u16, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + cfg_if! { + if #[cfg(feature = "jaeger")] { + use tracing_subscriber::layer::SubscriberExt; + use opentelemetry_sdk::runtime::Tokio; + let tracer = opentelemetry_jaeger::new_agent_pipeline() + .with_endpoint("jaeger:6831") + .with_service_name("autonatv2") + .install_batch(Tokio)?; + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + let subscriber = tracing_subscriber::Registry::default() + .with(telemetry); + } else { + let subscriber = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .finish(); + } + } + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + let opt = Opt::parse(); + + let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_quic() + .with_dns()? + .with_behaviour(|key| Behaviour::new(key.public()))? + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) + .build(); + + swarm.listen_on( + Multiaddr::empty() + .with(Protocol::Ip4(Ipv4Addr::UNSPECIFIED)) + .with(Protocol::Tcp(opt.listen_port)), + )?; + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), + SwarmEvent::Behaviour(event) => println!("{event:?}"), + e => println!("{e:?}"), + } + } +} + +#[derive(NetworkBehaviour)] +pub struct Behaviour { + autonat: autonat::v2::server::Behaviour, + identify: identify::Behaviour, +} + +impl Behaviour { + pub fn new(key: identity::PublicKey) -> Self { + Self { + autonat: autonat::v2::server::Behaviour::new(OsRng), + identify: identify::Behaviour::new(identify::Config::new("/ipfs/0.1.0".into(), key)), + } + } +} diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/examples/autonatv2/src/lib.rs @@ -0,0 +1 @@ + diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index 2a799221f7c..1259dd01fd4 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -1,15 +1,3 @@ -## 0.13.0 - -- Due to the refactor of `Transport` it's no longer required to create a seperate transport for -AutoNAT where port reuse is disabled. This information is now passed by the behaviour. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568). - - - -## 0.12.1 -- Use `web-time` instead of `instant`. - See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). - ## 0.12.0 - Remove `Clone`, `PartialEq` and `Eq` implementations on `Event` and its sub-structs. diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index f047cb7d5ba..5e975b33795 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -3,32 +3,50 @@ name = "libp2p-autonat" edition = "2021" rust-version = { workspace = true } description = "NAT and firewall detection for libp2p" -authors = ["David Craven ", "Elena Frank "] version = "0.13.0" +authors = ["David Craven ", "Elena Frank ", "Hannes Furmans "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] categories = ["network-programming", "asynchronous"] + [dependencies] async-trait = "0.1" +asynchronous-codec = { workspace = true } +bytes = { version = "1", optional = true } +either = { version = "1.9.0", optional = true } futures = { workspace = true } +futures-bounded = { workspace = true, optional = true } futures-timer = "3.0" -web-time = { workspace = true } +instant = "0.1" libp2p-core = { workspace = true } -libp2p-swarm = { workspace = true } -libp2p-request-response = { workspace = true } libp2p-identity = { workspace = true } +libp2p-request-response = { workspace = true, optional = true } +libp2p-swarm = { workspace = true } quick-protobuf = "0.8" -rand = "0.8" tracing = { workspace = true } quick-protobuf-codec = { workspace = true } -asynchronous-codec = { workspace = true } +rand = "0.8" +rand_core = { version = "0.6", optional = true } +thiserror = { version = "1.0.52", optional = true } +void = { version = "1", optional = true } [dev-dependencies] +tokio = { version = "1", features = ["macros", "rt", "sync"]} async-std = { version = "1.10", features = ["attributes"] } libp2p-swarm-test = { path = "../../swarm-test" } -tracing-subscriber = { workspace = true, features = ["env-filter"] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +libp2p-identify = { workspace = true } +libp2p-swarm = { workspace = true, features = ["macros"]} + +[features] +default = ["v1", "v2"] +v1 = ["dep:libp2p-request-response"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] + +[lints] +workspace = true # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling @@ -36,6 +54,3 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } all-features = true rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "docsrs"] - -[lints] -workspace = true diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 10c87b1e984..a6fc66b28d1 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1,41 +1,9 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +#[cfg(feature = "v1")] +pub mod v1; -//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. +#[cfg(feature = "v2")] +pub mod v2; -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - -mod behaviour; -mod protocol; - -pub use self::{ - behaviour::{ - Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, - OutboundProbeError, OutboundProbeEvent, ProbeId, - }, - protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, -}; -pub use libp2p_request_response::{InboundFailure, OutboundFailure}; - -mod proto { - #![allow(unreachable_pub)] - include!("generated/mod.rs"); - pub(crate) use self::structs::{mod_Message::*, Message}; -} +#[cfg(feature = "v1")] +#[allow(deprecated)] +pub use v1::*; diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs new file mode 100644 index 00000000000..07b08310871 --- /dev/null +++ b/protocols/autonat/src/v1.rs @@ -0,0 +1,42 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. + +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(test), deprecated(note = "Please use `v2` module instead."))] + +pub(crate) mod behaviour; +pub(crate) mod protocol; + +pub use self::{ + behaviour::{ + Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, NatStatus, + OutboundProbeError, OutboundProbeEvent, ProbeId, + }, + protocol::{ResponseError, DEFAULT_PROTOCOL_NAME}, +}; +pub use libp2p_request_response::{InboundFailure, OutboundFailure}; + +pub(crate) mod proto { + #![allow(unreachable_pub)] + include!("v1/generated/mod.rs"); + pub(crate) use self::structs::{mod_Message::*, Message}; +} diff --git a/protocols/autonat/src/v1/behaviour.rs b/protocols/autonat/src/v1/behaviour.rs new file mode 100644 index 00000000000..6e2c36a467f --- /dev/null +++ b/protocols/autonat/src/v1/behaviour.rs @@ -0,0 +1,689 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +mod as_client; +mod as_server; + +use crate::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; +use crate::DEFAULT_PROTOCOL_NAME; +use as_client::AsClient; +pub use as_client::{OutboundProbeError, OutboundProbeEvent}; +use as_server::AsServer; +pub use as_server::{InboundProbeError, InboundProbeEvent}; +use futures_timer::Delay; +use instant::Instant; +use libp2p_core::transport::PortUse; +use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_request_response::{ + self as request_response, InboundRequestId, OutboundRequestId, ProtocolSupport, ResponseChannel, +}; +use libp2p_swarm::{ + behaviour::{AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}, + ConnectionDenied, ConnectionId, ListenAddresses, NetworkBehaviour, THandler, THandlerInEvent, + THandlerOutEvent, ToSwarm, +}; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + iter, + task::{Context, Poll}, + time::Duration, +}; + +/// Config for the [`Behaviour`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config { + /// Timeout for requests. + pub timeout: Duration, + + // Client Config + /// Delay on init before starting the fist probe. + pub boot_delay: Duration, + /// Interval in which the NAT should be tested again if max confidence was reached in a status. + pub refresh_interval: Duration, + /// Interval in which the NAT status should be re-tried if it is currently unknown + /// or max confidence was not reached yet. + pub retry_interval: Duration, + /// Throttle period for re-using a peer as server for a dial-request. + pub throttle_server_period: Duration, + /// Use connected peers as servers for probes. + pub use_connected: bool, + /// Max confidence that can be reached in a public / private NAT status. + /// Note: for [`NatStatus::Unknown`] the confidence is always 0. + pub confidence_max: usize, + + // Server Config + /// Max addresses that are tried per peer. + pub max_peer_addresses: usize, + /// Max total dial requests done in `[Config::throttle_clients_period`]. + pub throttle_clients_global_max: usize, + /// Max dial requests done in `[Config::throttle_clients_period`] for a peer. + pub throttle_clients_peer_max: usize, + /// Period for throttling clients requests. + pub throttle_clients_period: Duration, + /// As a server reject probes for clients that are observed at a non-global ip address. + /// Correspondingly as a client only pick peers as server that are not observed at a + /// private ip address. Note that this does not apply for servers that are added via + /// [`Behaviour::add_server`]. + pub only_global_ips: bool, +} + +impl Default for Config { + fn default() -> Self { + Config { + timeout: Duration::from_secs(30), + boot_delay: Duration::from_secs(15), + retry_interval: Duration::from_secs(90), + refresh_interval: Duration::from_secs(15 * 60), + throttle_server_period: Duration::from_secs(90), + use_connected: true, + confidence_max: 3, + max_peer_addresses: 16, + throttle_clients_global_max: 30, + throttle_clients_peer_max: 3, + throttle_clients_period: Duration::from_secs(1), + only_global_ips: true, + } + } +} + +/// Assumed NAT status. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NatStatus { + Public(Multiaddr), + Private, + Unknown, +} + +impl NatStatus { + pub fn is_public(&self) -> bool { + matches!(self, NatStatus::Public(..)) + } +} + +/// Unique identifier for a probe. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ProbeId(usize); + +impl ProbeId { + fn next(&mut self) -> ProbeId { + let current = *self; + self.0 += 1; + current + } +} + +/// Event produced by [`Behaviour`]. +#[derive(Debug)] +pub enum Event { + /// Event on an inbound probe. + InboundProbe(InboundProbeEvent), + /// Event on an outbound probe. + OutboundProbe(OutboundProbeEvent), + /// The assumed NAT changed. + StatusChanged { + /// Former status. + old: NatStatus, + /// New status. + new: NatStatus, + }, +} + +/// [`NetworkBehaviour`] for AutoNAT. +/// +/// The behaviour frequently runs probes to determine whether the local peer is behind NAT and/ or a firewall, or +/// publicly reachable. +/// In a probe, a dial-back request is sent to a peer that is randomly selected from the list of fixed servers and +/// connected peers. Upon receiving a dial-back request, the remote tries to dial the included addresses. When a +/// first address was successfully dialed, a status Ok will be send back together with the dialed address. If no address +/// can be reached a dial-error is send back. +/// Based on the received response, the sender assumes themselves to be public or private. +/// The status is retried in a frequency of [`Config::retry_interval`] or [`Config::retry_interval`], depending on whether +/// enough confidence in the assumed NAT status was reached or not. +/// The confidence increases each time a probe confirms the assumed status, and decreases if a different status is reported. +/// If the confidence is 0, the status is flipped and the Behaviour will report the new status in an `OutEvent`. +pub struct Behaviour { + // Local peer id + local_peer_id: PeerId, + + // Inner behaviour for sending requests and receiving the response. + inner: request_response::Behaviour, + + config: Config, + + // Additional peers apart from the currently connected ones, that may be used for probes. + servers: HashSet, + + // Assumed NAT status. + nat_status: NatStatus, + + // Confidence in the assumed NAT status. + confidence: usize, + + // Timer for the next probe. + schedule_probe: Delay, + + // Ongoing inbound requests, where no response has been sent back to the remote yet. + ongoing_inbound: HashMap< + PeerId, + ( + ProbeId, + InboundRequestId, + Vec, + ResponseChannel, + ), + >, + + // Ongoing outbound probes and mapped to the inner request id. + ongoing_outbound: HashMap, + + // Connected peers with the observed address of each connection. + // If the endpoint of a connection is relayed or not global (in case of Config::only_global_ips), + // the observed address is `None`. + connected: HashMap>>, + + // Used servers in recent outbound probes that are throttled through Config::throttle_server_period. + throttled_servers: Vec<(PeerId, Instant)>, + + // Recent probes done for clients + throttled_clients: Vec<(PeerId, Instant)>, + + last_probe: Option, + + pending_actions: VecDeque::ToSwarm, THandlerInEvent>>, + + probe_id: ProbeId, + + listen_addresses: ListenAddresses, + other_candidates: HashSet, +} + +impl Behaviour { + pub fn new(local_peer_id: PeerId, config: Config) -> Self { + let protocols = iter::once((DEFAULT_PROTOCOL_NAME, ProtocolSupport::Full)); + let inner = request_response::Behaviour::with_codec( + AutoNatCodec, + protocols, + request_response::Config::default().with_request_timeout(config.timeout), + ); + Self { + local_peer_id, + inner, + schedule_probe: Delay::new(config.boot_delay), + config, + servers: HashSet::new(), + ongoing_inbound: HashMap::default(), + ongoing_outbound: HashMap::default(), + connected: HashMap::default(), + nat_status: NatStatus::Unknown, + confidence: 0, + throttled_servers: Vec::new(), + throttled_clients: Vec::new(), + last_probe: None, + pending_actions: VecDeque::new(), + probe_id: ProbeId(0), + listen_addresses: Default::default(), + other_candidates: Default::default(), + } + } + + /// Assumed public address of the local peer. + /// Returns `None` in case of status [`NatStatus::Private`] or [`NatStatus::Unknown`]. + pub fn public_address(&self) -> Option<&Multiaddr> { + match &self.nat_status { + NatStatus::Public(address) => Some(address), + _ => None, + } + } + + /// Assumed NAT status. + pub fn nat_status(&self) -> NatStatus { + self.nat_status.clone() + } + + /// Confidence in the assumed NAT status. + pub fn confidence(&self) -> usize { + self.confidence + } + + /// Add a peer to the list over servers that may be used for probes. + /// These peers are used for dial-request even if they are currently not connection, in which case a connection will be + /// establish before sending the dial-request. + pub fn add_server(&mut self, peer: PeerId, address: Option) { + self.servers.insert(peer); + if let Some(addr) = address { + #[allow(deprecated)] + self.inner.add_address(&peer, addr); + } + } + + /// Remove a peer from the list of servers. + /// See [`Behaviour::add_server`] for more info. + pub fn remove_server(&mut self, peer: &PeerId) { + self.servers.retain(|p| p != peer); + } + + /// Explicitly probe the provided address for external reachability. + pub fn probe_address(&mut self, candidate: Multiaddr) { + self.other_candidates.insert(candidate); + self.as_client().on_new_address(); + } + + fn as_client(&mut self) -> AsClient { + AsClient { + inner: &mut self.inner, + local_peer_id: self.local_peer_id, + config: &self.config, + connected: &self.connected, + probe_id: &mut self.probe_id, + servers: &self.servers, + throttled_servers: &mut self.throttled_servers, + nat_status: &mut self.nat_status, + confidence: &mut self.confidence, + ongoing_outbound: &mut self.ongoing_outbound, + last_probe: &mut self.last_probe, + schedule_probe: &mut self.schedule_probe, + listen_addresses: &self.listen_addresses, + other_candidates: &self.other_candidates, + } + } + + fn as_server(&mut self) -> AsServer { + AsServer { + inner: &mut self.inner, + config: &self.config, + connected: &self.connected, + probe_id: &mut self.probe_id, + throttled_clients: &mut self.throttled_clients, + ongoing_inbound: &mut self.ongoing_inbound, + } + } + + fn on_connection_established( + &mut self, + ConnectionEstablished { + peer_id: peer, + connection_id: conn, + endpoint, + .. + }: ConnectionEstablished, + ) { + let connections = self.connected.entry(peer).or_default(); + let addr = endpoint.get_remote_address(); + let observed_addr = + if !endpoint.is_relayed() && (!self.config.only_global_ips || addr.is_global_ip()) { + Some(addr.clone()) + } else { + None + }; + connections.insert(conn, observed_addr); + + match endpoint { + ConnectedPoint::Dialer { + address, + role_override: Endpoint::Dialer, + port_use: _, + } => { + if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { + self.pending_actions + .push_back(ToSwarm::GenerateEvent(Event::InboundProbe(event))); + } + } + ConnectedPoint::Dialer { + address: _, + role_override: Endpoint::Listener, + port_use: _, + } => { + // Outgoing connection was dialed as a listener. In other words outgoing connection + // was dialed as part of a hole punch. `libp2p-autonat` never attempts to hole + // punch, thus this connection has not been requested by this [`NetworkBehaviour`]. + } + ConnectedPoint::Listener { .. } => self.as_client().on_inbound_connection(), + } + } + + fn on_connection_closed( + &mut self, + ConnectionClosed { + peer_id, + connection_id, + remaining_established, + .. + }: ConnectionClosed, + ) { + if remaining_established == 0 { + self.connected.remove(&peer_id); + } else { + let connections = self + .connected + .get_mut(&peer_id) + .expect("Peer is connected."); + connections.remove(&connection_id); + } + } + + fn on_dial_failure(&mut self, DialFailure { peer_id, error, .. }: DialFailure) { + if let Some(event) = self.as_server().on_outbound_dial_error(peer_id, error) { + self.pending_actions + .push_back(ToSwarm::GenerateEvent(Event::InboundProbe(event))); + } + } + + fn on_address_change( + &mut self, + AddressChange { + peer_id: peer, + connection_id: conn, + old, + new, + }: AddressChange, + ) { + if old.is_relayed() && new.is_relayed() { + return; + } + let connections = self.connected.get_mut(&peer).expect("Peer is connected."); + let addr = new.get_remote_address(); + let observed_addr = + if !new.is_relayed() && (!self.config.only_global_ips || addr.is_global_ip()) { + Some(addr.clone()) + } else { + None + }; + connections.insert(conn, observed_addr); + } +} + +impl NetworkBehaviour for Behaviour { + type ConnectionHandler = + as NetworkBehaviour>::ConnectionHandler; + type ToSwarm = Event; + + #[tracing::instrument(level = "trace", name = "NetworkBehaviour::poll", skip(self, cx))] + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll>> { + loop { + if let Some(event) = self.pending_actions.pop_front() { + return Poll::Ready(event); + } + + match self.inner.poll(cx) { + Poll::Ready(ToSwarm::GenerateEvent(event)) => { + let actions = match event { + request_response::Event::Message { + message: request_response::Message::Response { .. }, + .. + } + | request_response::Event::OutboundFailure { .. } => { + self.as_client().handle_event(event) + } + request_response::Event::Message { + message: request_response::Message::Request { .. }, + .. + } + | request_response::Event::InboundFailure { .. } => { + self.as_server().handle_event(event) + } + request_response::Event::ResponseSent { .. } => VecDeque::new(), + }; + + self.pending_actions.extend(actions); + continue; + } + Poll::Ready(action) => { + self.pending_actions + .push_back(action.map_out(|_| unreachable!())); + continue; + } + Poll::Pending => {} + } + + match self.as_client().poll_auto_probe(cx) { + Poll::Ready(event) => { + self.pending_actions + .push_back(ToSwarm::GenerateEvent(Event::OutboundProbe(event))); + continue; + } + Poll::Pending => {} + } + + return Poll::Pending; + } + } + + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.inner + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) + } + + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + self.inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + ) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result, ConnectionDenied> { + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + self.listen_addresses.on_swarm_event(&event); + self.inner.on_swarm_event(event); + + match event { + FromSwarm::ConnectionEstablished(e) => self.on_connection_established(e), + FromSwarm::ConnectionClosed(e) => self.on_connection_closed(e), + FromSwarm::DialFailure(e) => self.on_dial_failure(e), + FromSwarm::AddressChange(e) => self.on_address_change(e), + FromSwarm::NewListenAddr(_) => { + self.as_client().on_new_address(); + } + FromSwarm::ExpiredListenAddr(e) => { + self.as_client().on_expired_address(e.addr); + } + FromSwarm::ExternalAddrExpired(e) => { + self.as_client().on_expired_address(e.addr); + } + FromSwarm::NewExternalAddrCandidate(e) => { + self.probe_address(e.addr.to_owned()); + } + _ => {} + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + connection_id: ConnectionId, + event: THandlerOutEvent, + ) { + self.inner + .on_connection_handler_event(peer_id, connection_id, event) + } +} + +type Action = ToSwarm<::ToSwarm, THandlerInEvent>; + +// Trait implemented for `AsClient` and `AsServer` to handle events from the inner [`request_response::Behaviour`] Protocol. +trait HandleInnerEvent { + fn handle_event( + &mut self, + event: request_response::Event, + ) -> VecDeque; +} + +trait GlobalIp { + fn is_global_ip(&self) -> bool; +} + +impl GlobalIp for Multiaddr { + fn is_global_ip(&self) -> bool { + match self.iter().next() { + Some(Protocol::Ip4(a)) => a.is_global_ip(), + Some(Protocol::Ip6(a)) => a.is_global_ip(), + _ => false, + } + } +} + +impl GlobalIp for std::net::Ipv4Addr { + // NOTE: The below logic is copied from `std::net::Ipv4Addr::is_global`, which is at the time of + // writing behind the unstable `ip` feature. + // See https://github.com/rust-lang/rust/issues/27709 for more info. + fn is_global_ip(&self) -> bool { + // Check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two + // globally routable addresses in the 192.0.0.0/24 range. + if u32::from_be_bytes(self.octets()) == 0xc0000009 + || u32::from_be_bytes(self.octets()) == 0xc000000a + { + return true; + } + + // Copied from the unstable method `std::net::Ipv4Addr::is_shared`. + fn is_shared(addr: &std::net::Ipv4Addr) -> bool { + addr.octets()[0] == 100 && (addr.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + + // Copied from the unstable method `std::net::Ipv4Addr::is_reserved`. + // + // **Warning**: As IANA assigns new addresses, this logic will be + // updated. This may result in non-reserved addresses being + // treated as reserved in code that relies on an outdated version + // of this method. + fn is_reserved(addr: &std::net::Ipv4Addr) -> bool { + addr.octets()[0] & 240 == 240 && !addr.is_broadcast() + } + + // Copied from the unstable method `std::net::Ipv4Addr::is_benchmarking`. + fn is_benchmarking(addr: &std::net::Ipv4Addr) -> bool { + addr.octets()[0] == 198 && (addr.octets()[1] & 0xfe) == 18 + } + + !self.is_private() + && !self.is_loopback() + && !self.is_link_local() + && !self.is_broadcast() + && !self.is_documentation() + && !is_shared(self) + // addresses reserved for future protocols (`192.0.0.0/24`) + && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + && !is_reserved(self) + && !is_benchmarking(self) + // Make sure the address is not in 0.0.0.0/8 + && self.octets()[0] != 0 + } +} + +impl GlobalIp for std::net::Ipv6Addr { + // NOTE: The below logic is copied from `std::net::Ipv6Addr::is_global`, which is at the time of + // writing behind the unstable `ip` feature. + // See https://github.com/rust-lang/rust/issues/27709 for more info. + // + // Note that contrary to `Ipv4Addr::is_global_ip` this currently checks for global scope + // rather than global reachability. + fn is_global_ip(&self) -> bool { + // Copied from the unstable method `std::net::Ipv6Addr::is_unicast`. + fn is_unicast(addr: &std::net::Ipv6Addr) -> bool { + !addr.is_multicast() + } + // Copied from the unstable method `std::net::Ipv6Addr::is_unicast_link_local`. + fn is_unicast_link_local(addr: &std::net::Ipv6Addr) -> bool { + (addr.segments()[0] & 0xffc0) == 0xfe80 + } + // Copied from the unstable method `std::net::Ipv6Addr::is_unique_local`. + fn is_unique_local(addr: &std::net::Ipv6Addr) -> bool { + (addr.segments()[0] & 0xfe00) == 0xfc00 + } + // Copied from the unstable method `std::net::Ipv6Addr::is_documentation`. + fn is_documentation(addr: &std::net::Ipv6Addr) -> bool { + (addr.segments()[0] == 0x2001) && (addr.segments()[1] == 0xdb8) + } + + // Copied from the unstable method `std::net::Ipv6Addr::is_unicast_global`. + fn is_unicast_global(addr: &std::net::Ipv6Addr) -> bool { + is_unicast(addr) + && !addr.is_loopback() + && !is_unicast_link_local(addr) + && !is_unique_local(addr) + && !addr.is_unspecified() + && !is_documentation(addr) + } + + // Variation of unstable method [`std::net::Ipv6Addr::multicast_scope`] that instead of the + // `Ipv6MulticastScope` just returns if the scope is global or not. + // Equivalent to `Ipv6Addr::multicast_scope(..).map(|scope| matches!(scope, Ipv6MulticastScope::Global))`. + fn is_multicast_scope_global(addr: &std::net::Ipv6Addr) -> Option { + match addr.segments()[0] & 0x000f { + 14 => Some(true), // Global multicast scope. + 1..=5 | 8 => Some(false), // Local multicast scope. + _ => None, // Unknown multicast scope. + } + } + + match is_multicast_scope_global(self) { + Some(true) => true, + None => is_unicast_global(self), + _ => false, + } + } +} diff --git a/protocols/autonat/src/v1/behaviour/as_client.rs b/protocols/autonat/src/v1/behaviour/as_client.rs new file mode 100644 index 00000000000..668f3b93719 --- /dev/null +++ b/protocols/autonat/src/v1/behaviour/as_client.rs @@ -0,0 +1,361 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::ResponseError; + +use super::{ + Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, NatStatus, + ProbeId, +}; +use futures::FutureExt; +use futures_timer::Delay; +use instant::Instant; +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; +use libp2p_request_response::{self as request_response, OutboundFailure, OutboundRequestId}; +use libp2p_swarm::{ConnectionId, ListenAddresses, ToSwarm}; +use rand::{seq::SliceRandom, thread_rng}; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + task::{Context, Poll}, + time::Duration, +}; + +/// Outbound probe failed or was aborted. +#[derive(Debug)] +pub enum OutboundProbeError { + /// Probe was aborted because no server is known, or all servers + /// are throttled through [`Config::throttle_server_period`]. + NoServer, + /// Probe was aborted because the local peer has no listening or + /// external addresses. + NoAddresses, + /// Sending the dial-back request or receiving a response failed. + OutboundRequest(OutboundFailure), + /// The server refused or failed to dial us. + Response(ResponseError), +} + +#[derive(Debug)] +pub enum OutboundProbeEvent { + /// A dial-back request was sent to a remote peer. + Request { + probe_id: ProbeId, + /// Peer to which the request is sent. + peer: PeerId, + }, + /// The remote successfully dialed one of our addresses. + Response { + probe_id: ProbeId, + /// Id of the peer that sent the response. + peer: PeerId, + /// The address at which the remote succeeded to dial us. + address: Multiaddr, + }, + /// The outbound request failed, was rejected, or the remote could dial + /// none of our addresses. + Error { + probe_id: ProbeId, + /// Id of the peer used for the probe. + /// `None` if the probe was aborted due to no addresses or no qualified server. + peer: Option, + error: OutboundProbeError, + }, +} + +/// View over [`super::Behaviour`] in a client role. +pub(crate) struct AsClient<'a> { + pub(crate) inner: &'a mut request_response::Behaviour, + pub(crate) local_peer_id: PeerId, + pub(crate) config: &'a Config, + pub(crate) connected: &'a HashMap>>, + pub(crate) probe_id: &'a mut ProbeId, + pub(crate) servers: &'a HashSet, + pub(crate) throttled_servers: &'a mut Vec<(PeerId, Instant)>, + pub(crate) nat_status: &'a mut NatStatus, + pub(crate) confidence: &'a mut usize, + pub(crate) ongoing_outbound: &'a mut HashMap, + pub(crate) last_probe: &'a mut Option, + pub(crate) schedule_probe: &'a mut Delay, + pub(crate) listen_addresses: &'a ListenAddresses, + pub(crate) other_candidates: &'a HashSet, +} + +impl<'a> HandleInnerEvent for AsClient<'a> { + fn handle_event( + &mut self, + event: request_response::Event, + ) -> VecDeque { + match event { + request_response::Event::Message { + peer, + message: + request_response::Message::Response { + request_id, + response, + }, + } => { + tracing::debug!(?response, "Outbound dial-back request returned response"); + + let probe_id = self + .ongoing_outbound + .remove(&request_id) + .expect("OutboundRequestId exists."); + + let event = match response.result.clone() { + Ok(address) => OutboundProbeEvent::Response { + probe_id, + peer, + address, + }, + Err(e) => OutboundProbeEvent::Error { + probe_id, + peer: Some(peer), + error: OutboundProbeError::Response(e), + }, + }; + + let mut actions = VecDeque::with_capacity(3); + + actions.push_back(ToSwarm::GenerateEvent(Event::OutboundProbe(event))); + + if let Some(old) = self.handle_reported_status(response.result.clone().into()) { + actions.push_back(ToSwarm::GenerateEvent(Event::StatusChanged { + old, + new: self.nat_status.clone(), + })); + } + + if let Ok(address) = response.result { + actions.push_back(ToSwarm::ExternalAddrConfirmed(address)); + } + + actions + } + request_response::Event::OutboundFailure { + peer, + error, + request_id, + } => { + tracing::debug!( + %peer, + "Outbound Failure {} when on dial-back request to peer.", + error, + ); + let probe_id = self + .ongoing_outbound + .remove(&request_id) + .unwrap_or_else(|| self.probe_id.next()); + + self.schedule_probe.reset(Duration::ZERO); + + VecDeque::from([ToSwarm::GenerateEvent(Event::OutboundProbe( + OutboundProbeEvent::Error { + probe_id, + peer: Some(peer), + error: OutboundProbeError::OutboundRequest(error), + }, + ))]) + } + _ => VecDeque::default(), + } + } +} + +impl<'a> AsClient<'a> { + pub(crate) fn poll_auto_probe(&mut self, cx: &mut Context<'_>) -> Poll { + match self.schedule_probe.poll_unpin(cx) { + Poll::Ready(()) => { + self.schedule_probe.reset(self.config.retry_interval); + + let addresses = self + .other_candidates + .iter() + .chain(self.listen_addresses.iter()) + .cloned() + .collect(); + + let probe_id = self.probe_id.next(); + let event = match self.do_probe(probe_id, addresses) { + Ok(peer) => OutboundProbeEvent::Request { probe_id, peer }, + Err(error) => { + self.handle_reported_status(NatStatus::Unknown); + OutboundProbeEvent::Error { + probe_id, + peer: None, + error, + } + } + }; + Poll::Ready(event) + } + Poll::Pending => Poll::Pending, + } + } + + // An inbound connection can indicate that we are public; adjust the delay to the next probe. + pub(crate) fn on_inbound_connection(&mut self) { + if *self.confidence == self.config.confidence_max { + if self.nat_status.is_public() { + self.schedule_next_probe(self.config.refresh_interval * 2); + } else { + self.schedule_next_probe(self.config.refresh_interval / 5); + } + } + } + + pub(crate) fn on_new_address(&mut self) { + if !self.nat_status.is_public() { + // New address could be publicly reachable, trigger retry. + if *self.confidence > 0 { + *self.confidence -= 1; + } + self.schedule_next_probe(self.config.retry_interval); + } + } + + pub(crate) fn on_expired_address(&mut self, addr: &Multiaddr) { + if let NatStatus::Public(public_address) = self.nat_status { + if public_address == addr { + *self.confidence = 0; + *self.nat_status = NatStatus::Unknown; + self.schedule_next_probe(Duration::ZERO); + } + } + } + + // Select a random server for the probe. + fn random_server(&mut self) -> Option { + // Update list of throttled servers. + let i = self.throttled_servers.partition_point(|(_, time)| { + *time + self.config.throttle_server_period < Instant::now() + }); + self.throttled_servers.drain(..i); + + let mut servers: Vec<&PeerId> = self.servers.iter().collect(); + + if self.config.use_connected { + servers.extend(self.connected.iter().filter_map(|(id, addrs)| { + // Filter servers for which no qualified address is known. + // This is the case if the connection is relayed or the address is + // not global (in case of Config::only_global_ips). + addrs.values().any(|a| a.is_some()).then_some(id) + })); + } + + servers.retain(|s| !self.throttled_servers.iter().any(|(id, _)| s == &id)); + + servers.choose(&mut thread_rng()).map(|&&p| p) + } + + // Send a dial-request to a randomly selected server. + // Returns the server that is used in this probe. + // `Err` if there are no qualified servers or no addresses. + fn do_probe( + &mut self, + probe_id: ProbeId, + addresses: Vec, + ) -> Result { + let _ = self.last_probe.insert(Instant::now()); + if addresses.is_empty() { + tracing::debug!("Outbound dial-back request aborted: No dial-back addresses"); + return Err(OutboundProbeError::NoAddresses); + } + + let server = self.random_server().ok_or(OutboundProbeError::NoServer)?; + + let request_id = self.inner.send_request( + &server, + DialRequest { + peer_id: self.local_peer_id, + addresses, + }, + ); + self.throttled_servers.push((server, Instant::now())); + tracing::debug!(peer=%server, "Send dial-back request to peer"); + self.ongoing_outbound.insert(request_id, probe_id); + Ok(server) + } + + // Set the delay to the next probe based on the time of our last probe + // and the specified delay. + fn schedule_next_probe(&mut self, delay: Duration) { + let Some(last_probe_instant) = self.last_probe else { + return; + }; + let schedule_next = *last_probe_instant + delay; + self.schedule_probe + .reset(schedule_next.saturating_duration_since(Instant::now())); + } + + // Adapt current confidence and NAT status to the status reported by the latest probe. + // Return the old status if it flipped. + fn handle_reported_status(&mut self, reported_status: NatStatus) -> Option { + self.schedule_next_probe(self.config.retry_interval); + + if matches!(reported_status, NatStatus::Unknown) { + return None; + } + + if reported_status == *self.nat_status { + if *self.confidence < self.config.confidence_max { + *self.confidence += 1; + } + // Delay with (usually longer) refresh-interval. + if *self.confidence >= self.config.confidence_max { + self.schedule_next_probe(self.config.refresh_interval); + } + return None; + } + + if reported_status.is_public() && self.nat_status.is_public() { + // Different address than the currently assumed public address was reported. + // Switch address, but don't report as flipped. + *self.nat_status = reported_status; + return None; + } + if *self.confidence > 0 { + // Reduce confidence but keep old status. + *self.confidence -= 1; + return None; + } + + tracing::debug!( + old_status=?self.nat_status, + new_status=?reported_status, + "Flipped assumed NAT status" + ); + + let old_status = self.nat_status.clone(); + *self.nat_status = reported_status; + + Some(old_status) + } +} + +impl From> for NatStatus { + fn from(result: Result) -> Self { + match result { + Ok(addr) => NatStatus::Public(addr), + Err(ResponseError::DialError) => NatStatus::Private, + _ => NatStatus::Unknown, + } + } +} diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs new file mode 100644 index 00000000000..e309023bc75 --- /dev/null +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -0,0 +1,426 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +use super::{ + Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, + ResponseError, +}; +use instant::Instant; +use libp2p_core::{multiaddr::Protocol, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_request_response::{ + self as request_response, InboundFailure, InboundRequestId, ResponseChannel, +}; +use libp2p_swarm::{ + dial_opts::{DialOpts, PeerCondition}, + ConnectionId, DialError, ToSwarm, +}; +use std::{ + collections::{HashMap, HashSet, VecDeque}, + num::NonZeroU8, +}; + +/// Inbound probe failed. +#[derive(Debug)] +pub enum InboundProbeError { + /// Receiving the dial-back request or sending a response failed. + InboundRequest(InboundFailure), + /// We refused or failed to dial the client. + Response(ResponseError), +} + +#[derive(Debug)] +pub enum InboundProbeEvent { + /// A dial-back request was received from a remote peer. + Request { + probe_id: ProbeId, + /// Peer that sent the request. + peer: PeerId, + /// The addresses that will be attempted to dial. + addresses: Vec, + }, + /// A dial request to the remote was successful. + Response { + probe_id: ProbeId, + /// Peer to which the response is sent. + peer: PeerId, + address: Multiaddr, + }, + /// The inbound request failed, was rejected, or none of the remote's + /// addresses could be dialed. + Error { + probe_id: ProbeId, + /// Peer that sent the dial-back request. + peer: PeerId, + error: InboundProbeError, + }, +} + +/// View over [`super::Behaviour`] in a server role. +pub(crate) struct AsServer<'a> { + pub(crate) inner: &'a mut request_response::Behaviour, + pub(crate) config: &'a Config, + pub(crate) connected: &'a HashMap>>, + pub(crate) probe_id: &'a mut ProbeId, + pub(crate) throttled_clients: &'a mut Vec<(PeerId, Instant)>, + #[allow(clippy::type_complexity)] + pub(crate) ongoing_inbound: &'a mut HashMap< + PeerId, + ( + ProbeId, + InboundRequestId, + Vec, + ResponseChannel, + ), + >, +} + +impl<'a> HandleInnerEvent for AsServer<'a> { + fn handle_event( + &mut self, + event: request_response::Event, + ) -> VecDeque { + match event { + request_response::Event::Message { + peer, + message: + request_response::Message::Request { + request_id, + request, + channel, + }, + } => { + let probe_id = self.probe_id.next(); + match self.resolve_inbound_request(peer, request) { + Ok(addrs) => { + tracing::debug!( + %peer, + "Inbound dial request from peer with dial-back addresses {:?}", + addrs + ); + + self.ongoing_inbound + .insert(peer, (probe_id, request_id, addrs.clone(), channel)); + self.throttled_clients.push((peer, Instant::now())); + + VecDeque::from([ + ToSwarm::GenerateEvent(Event::InboundProbe( + InboundProbeEvent::Request { + probe_id, + peer, + addresses: addrs.clone(), + }, + )), + ToSwarm::Dial { + opts: DialOpts::peer_id(peer) + .condition(PeerCondition::Always) + .override_dial_concurrency_factor( + NonZeroU8::new(1).expect("1 > 0"), + ) + .addresses(addrs) + .allocate_new_port() + .build(), + }, + ]) + } + Err((status_text, error)) => { + tracing::debug!( + %peer, + status=%status_text, + "Reject inbound dial request from peer" + ); + + let response = DialResponse { + result: Err(error.clone()), + status_text: Some(status_text), + }; + let _ = self.inner.send_response(channel, response); + + VecDeque::from([ToSwarm::GenerateEvent(Event::InboundProbe( + InboundProbeEvent::Error { + probe_id, + peer, + error: InboundProbeError::Response(error), + }, + ))]) + } + } + } + request_response::Event::InboundFailure { + peer, + error, + request_id, + } => { + tracing::debug!( + %peer, + "Inbound Failure {} when on dial-back request from peer", + error + ); + + let probe_id = match self.ongoing_inbound.get(&peer) { + Some((_, rq_id, _, _)) if *rq_id == request_id => { + self.ongoing_inbound.remove(&peer).unwrap().0 + } + _ => self.probe_id.next(), + }; + + VecDeque::from([ToSwarm::GenerateEvent(Event::InboundProbe( + InboundProbeEvent::Error { + probe_id, + peer, + error: InboundProbeError::InboundRequest(error), + }, + ))]) + } + _ => VecDeque::new(), + } + } +} + +impl<'a> AsServer<'a> { + pub(crate) fn on_outbound_connection( + &mut self, + peer: &PeerId, + address: &Multiaddr, + ) -> Option { + let (_, _, addrs, _) = self.ongoing_inbound.get(peer)?; + + // Check if the dialed address was among the requested addresses. + if !addrs.contains(address) { + return None; + } + + tracing::debug!( + %peer, + %address, + "Dial-back to peer succeeded" + ); + + let (probe_id, _, _, channel) = self.ongoing_inbound.remove(peer).unwrap(); + let response = DialResponse { + result: Ok(address.clone()), + status_text: None, + }; + let _ = self.inner.send_response(channel, response); + + Some(InboundProbeEvent::Response { + probe_id, + peer: *peer, + address: address.clone(), + }) + } + + pub(crate) fn on_outbound_dial_error( + &mut self, + peer: Option, + error: &DialError, + ) -> Option { + let (probe_id, _, _, channel) = peer.and_then(|p| self.ongoing_inbound.remove(&p))?; + + match peer { + Some(p) => tracing::debug!( + peer=%p, + "Dial-back to peer failed with error {:?}", + error + ), + None => tracing::debug!( + "Dial-back to non existent peer failed with error {:?}", + error + ), + }; + + let response_error = ResponseError::DialError; + let response = DialResponse { + result: Err(response_error.clone()), + status_text: Some("dial failed".to_string()), + }; + let _ = self.inner.send_response(channel, response); + + Some(InboundProbeEvent::Error { + probe_id, + peer: peer.expect("PeerId is present."), + error: InboundProbeError::Response(response_error), + }) + } + + // Validate the inbound request and collect the addresses to be dialed. + fn resolve_inbound_request( + &mut self, + sender: PeerId, + request: DialRequest, + ) -> Result, (String, ResponseError)> { + // Update list of throttled clients. + let i = self.throttled_clients.partition_point(|(_, time)| { + *time + self.config.throttle_clients_period < Instant::now() + }); + self.throttled_clients.drain(..i); + + if request.peer_id != sender { + let status_text = "peer id mismatch".to_string(); + return Err((status_text, ResponseError::BadRequest)); + } + + if self.ongoing_inbound.contains_key(&sender) { + let status_text = "dial-back already ongoing".to_string(); + return Err((status_text, ResponseError::DialRefused)); + } + + if self.throttled_clients.len() >= self.config.throttle_clients_global_max { + let status_text = "too many total dials".to_string(); + return Err((status_text, ResponseError::DialRefused)); + } + + let throttled_for_client = self + .throttled_clients + .iter() + .filter(|(p, _)| p == &sender) + .count(); + + if throttled_for_client >= self.config.throttle_clients_peer_max { + let status_text = "too many dials for peer".to_string(); + return Err((status_text, ResponseError::DialRefused)); + } + + // Obtain an observed address from non-relayed connections. + let observed_addr = self + .connected + .get(&sender) + .expect("Peer is connected.") + .values() + .find_map(|a| a.as_ref()) + .ok_or_else(|| { + let status_text = "refusing to dial peer with blocked observed address".to_string(); + (status_text, ResponseError::DialRefused) + })?; + + let mut addrs = Self::filter_valid_addrs(sender, request.addresses, observed_addr); + addrs.truncate(self.config.max_peer_addresses); + + if addrs.is_empty() { + let status_text = "no dialable addresses".to_string(); + return Err((status_text, ResponseError::DialRefused)); + } + + Ok(addrs) + } + + // Filter dial addresses and replace demanded ip with the observed one. + fn filter_valid_addrs( + peer: PeerId, + demanded: Vec, + observed_remote_at: &Multiaddr, + ) -> Vec { + let Some(observed_ip) = observed_remote_at + .into_iter() + .find(|p| matches!(p, Protocol::Ip4(_) | Protocol::Ip6(_))) + else { + return Vec::new(); + }; + + let mut distinct = HashSet::new(); + demanded + .into_iter() + .filter_map(|addr| { + // Replace the demanded ip with the observed one. + let i = addr + .iter() + .position(|p| matches!(p, Protocol::Ip4(_) | Protocol::Ip6(_)))?; + let mut addr = addr.replace(i, |_| Some(observed_ip.clone()))?; + + let is_valid = addr.iter().all(|proto| match proto { + Protocol::P2pCircuit => false, + Protocol::P2p(peer_id) => peer_id == peer, + _ => true, + }); + + if !is_valid { + return None; + } + if !addr.iter().any(|p| matches!(p, Protocol::P2p(_))) { + addr.push(Protocol::P2p(peer)) + } + // Only collect distinct addresses. + distinct.insert(addr.clone()).then_some(addr) + }) + .collect() + } +} + +#[cfg(test)] +mod test { + use super::*; + + use std::net::Ipv4Addr; + + fn random_ip<'a>() -> Protocol<'a> { + Protocol::Ip4(Ipv4Addr::new( + rand::random(), + rand::random(), + rand::random(), + rand::random(), + )) + } + fn random_port<'a>() -> Protocol<'a> { + Protocol::Tcp(rand::random()) + } + + #[test] + fn filter_addresses() { + let peer_id = PeerId::random(); + let observed_ip = random_ip(); + let observed_addr = Multiaddr::empty() + .with(observed_ip.clone()) + .with(random_port()) + .with(Protocol::P2p(peer_id)); + // Valid address with matching peer-id + let demanded_1 = Multiaddr::empty() + .with(random_ip()) + .with(random_port()) + .with(Protocol::P2p(peer_id)); + // Invalid because peer_id does not match + let demanded_2 = Multiaddr::empty() + .with(random_ip()) + .with(random_port()) + .with(Protocol::P2p(PeerId::random())); + // Valid address without peer-id + let demanded_3 = Multiaddr::empty().with(random_ip()).with(random_port()); + // Invalid because relayed + let demanded_4 = Multiaddr::empty() + .with(random_ip()) + .with(random_port()) + .with(Protocol::P2p(PeerId::random())) + .with(Protocol::P2pCircuit) + .with(Protocol::P2p(peer_id)); + let demanded = vec![ + demanded_1.clone(), + demanded_2, + demanded_3.clone(), + demanded_4, + ]; + let filtered = AsServer::filter_valid_addrs(peer_id, demanded, &observed_addr); + let expected_1 = demanded_1 + .replace(0, |_| Some(observed_ip.clone())) + .unwrap(); + let expected_2 = demanded_3 + .replace(0, |_| Some(observed_ip)) + .unwrap() + .with(Protocol::P2p(peer_id)); + assert_eq!(filtered, vec![expected_1, expected_2]); + } +} diff --git a/protocols/autonat/src/v1/generated/mod.rs b/protocols/autonat/src/v1/generated/mod.rs new file mode 100644 index 00000000000..e52c5a80bc0 --- /dev/null +++ b/protocols/autonat/src/v1/generated/mod.rs @@ -0,0 +1,2 @@ +// Automatically generated mod.rs +pub mod structs; diff --git a/protocols/autonat/src/v1/generated/structs.proto b/protocols/autonat/src/v1/generated/structs.proto new file mode 100644 index 00000000000..19e27abd36a --- /dev/null +++ b/protocols/autonat/src/v1/generated/structs.proto @@ -0,0 +1,37 @@ +syntax = "proto2"; + +package structs; + +message Message { + enum MessageType { + DIAL = 0; + DIAL_RESPONSE = 1; + } + + enum ResponseStatus { + OK = 0; + E_DIAL_ERROR = 100; + E_DIAL_REFUSED = 101; + E_BAD_REQUEST = 200; + E_INTERNAL_ERROR = 300; + } + + message PeerInfo { + optional bytes id = 1; + repeated bytes addrs = 2; + } + + message Dial { + optional PeerInfo peer = 1; + } + + message DialResponse { + optional ResponseStatus status = 1; + optional string statusText = 2; + optional bytes addr = 3; + } + + optional MessageType type = 1; + optional Dial dial = 2; + optional DialResponse dialResponse = 3; +} diff --git a/protocols/autonat/src/v1/generated/structs.rs b/protocols/autonat/src/v1/generated/structs.rs new file mode 100644 index 00000000000..3a6d416b2b1 --- /dev/null +++ b/protocols/autonat/src/v1/generated/structs.rs @@ -0,0 +1,242 @@ +// Automatically generated rust module for 'structs.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::*; + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct Message { + pub type_pb: Option, + pub dial: Option, + pub dialResponse: Option, +} + +impl<'a> MessageRead<'a> for Message { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.type_pb = Some(r.read_enum(bytes)?), + Ok(18) => msg.dial = Some(r.read_message::(bytes)?), + Ok(26) => msg.dialResponse = Some(r.read_message::(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for Message { + fn get_size(&self) -> usize { + 0 + + self.type_pb.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.dial.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + + self.dialResponse.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.type_pb { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + if let Some(ref s) = self.dial { w.write_with_tag(18, |w| w.write_message(s))?; } + if let Some(ref s) = self.dialResponse { w.write_with_tag(26, |w| w.write_message(s))?; } + Ok(()) + } +} + +pub mod mod_Message { + +use super::*; + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct PeerInfo { + pub id: Option>, + pub addrs: Vec>, +} + +impl<'a> MessageRead<'a> for PeerInfo { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.id = Some(r.read_bytes(bytes)?.to_owned()), + Ok(18) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for PeerInfo { + fn get_size(&self) -> usize { + 0 + + self.id.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.id { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + for s in &self.addrs { w.write_with_tag(18, |w| w.write_bytes(&**s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct Dial { + pub peer: Option, +} + +impl<'a> MessageRead<'a> for Dial { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.peer = Some(r.read_message::(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for Dial { + fn get_size(&self) -> usize { + 0 + + self.peer.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.peer { w.write_with_tag(10, |w| w.write_message(s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialResponse { + pub status: Option, + pub statusText: Option, + pub addr: Option>, +} + +impl<'a> MessageRead<'a> for DialResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(18) => msg.statusText = Some(r.read_string(bytes)?.to_owned()), + Ok(26) => msg.addr = Some(r.read_bytes(bytes)?.to_owned()), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.statusText.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + + self.addr.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + if let Some(ref s) = self.statusText { w.write_with_tag(18, |w| w.write_string(&**s))?; } + if let Some(ref s) = self.addr { w.write_with_tag(26, |w| w.write_bytes(&**s))?; } + Ok(()) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum MessageType { + DIAL = 0, + DIAL_RESPONSE = 1, +} + +impl Default for MessageType { + fn default() -> Self { + MessageType::DIAL + } +} + +impl From for MessageType { + fn from(i: i32) -> Self { + match i { + 0 => MessageType::DIAL, + 1 => MessageType::DIAL_RESPONSE, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for MessageType { + fn from(s: &'a str) -> Self { + match s { + "DIAL" => MessageType::DIAL, + "DIAL_RESPONSE" => MessageType::DIAL_RESPONSE, + _ => Self::default(), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ResponseStatus { + OK = 0, + E_DIAL_ERROR = 100, + E_DIAL_REFUSED = 101, + E_BAD_REQUEST = 200, + E_INTERNAL_ERROR = 300, +} + +impl Default for ResponseStatus { + fn default() -> Self { + ResponseStatus::OK + } +} + +impl From for ResponseStatus { + fn from(i: i32) -> Self { + match i { + 0 => ResponseStatus::OK, + 100 => ResponseStatus::E_DIAL_ERROR, + 101 => ResponseStatus::E_DIAL_REFUSED, + 200 => ResponseStatus::E_BAD_REQUEST, + 300 => ResponseStatus::E_INTERNAL_ERROR, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for ResponseStatus { + fn from(s: &'a str) -> Self { + match s { + "OK" => ResponseStatus::OK, + "E_DIAL_ERROR" => ResponseStatus::E_DIAL_ERROR, + "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, + "E_BAD_REQUEST" => ResponseStatus::E_BAD_REQUEST, + "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, + _ => Self::default(), + } + } +} + +} + diff --git a/protocols/autonat/src/v1/protocol.rs b/protocols/autonat/src/v1/protocol.rs new file mode 100644 index 00000000000..2ce538fddf4 --- /dev/null +++ b/protocols/autonat/src/v1/protocol.rs @@ -0,0 +1,343 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::proto; +use async_trait::async_trait; +use asynchronous_codec::{FramedRead, FramedWrite}; +use futures::io::{AsyncRead, AsyncWrite}; +use futures::{SinkExt, StreamExt}; +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; +use libp2p_request_response::{self as request_response}; +use libp2p_swarm::StreamProtocol; +use std::io; + +/// The protocol name used for negotiating with multistream-select. +pub const DEFAULT_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/1.0.0"); + +#[derive(Clone)] +pub struct AutoNatCodec; + +#[async_trait] +impl request_response::Codec for AutoNatCodec { + type Protocol = StreamProtocol; + type Request = DialRequest; + type Response = DialResponse; + + async fn read_request(&mut self, _: &StreamProtocol, io: &mut T) -> io::Result + where + T: AsyncRead + Send + Unpin, + { + let message = FramedRead::new(io, codec()) + .next() + .await + .ok_or(io::ErrorKind::UnexpectedEof)??; + let request = DialRequest::from_proto(message)?; + + Ok(request) + } + + async fn read_response( + &mut self, + _: &StreamProtocol, + io: &mut T, + ) -> io::Result + where + T: AsyncRead + Send + Unpin, + { + let message = FramedRead::new(io, codec()) + .next() + .await + .ok_or(io::ErrorKind::UnexpectedEof)??; + let response = DialResponse::from_proto(message)?; + + Ok(response) + } + + async fn write_request( + &mut self, + _: &StreamProtocol, + io: &mut T, + data: Self::Request, + ) -> io::Result<()> + where + T: AsyncWrite + Send + Unpin, + { + let mut framed = FramedWrite::new(io, codec()); + framed.send(data.into_proto()).await?; + framed.close().await?; + + Ok(()) + } + + async fn write_response( + &mut self, + _: &StreamProtocol, + io: &mut T, + data: Self::Response, + ) -> io::Result<()> + where + T: AsyncWrite + Send + Unpin, + { + let mut framed = FramedWrite::new(io, codec()); + framed.send(data.into_proto()).await?; + framed.close().await?; + + Ok(()) + } +} + +fn codec() -> quick_protobuf_codec::Codec { + quick_protobuf_codec::Codec::::new(1024) +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DialRequest { + pub peer_id: PeerId, + pub addresses: Vec, +} + +impl DialRequest { + pub fn from_proto(msg: proto::Message) -> Result { + if msg.type_pb != Some(proto::MessageType::DIAL) { + return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid type")); + } + + let peer_id_result = msg.dial.and_then(|dial| { + dial.peer + .and_then(|peer_info| peer_info.id.map(|peer_id| (peer_id, peer_info.addrs))) + }); + + let (peer_id, addrs) = peer_id_result + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid dial message"))?; + + let peer_id = { + PeerId::try_from(peer_id.to_vec()) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid peer id"))? + }; + + let addrs = addrs + .into_iter() + .filter_map(|a| match Multiaddr::try_from(a.to_vec()) { + Ok(a) => Some(a), + Err(e) => { + tracing::debug!("Unable to parse multiaddr: {e}"); + None + } + }) + .collect(); + Ok(Self { + peer_id, + addresses: addrs, + }) + } + + pub fn into_proto(self) -> proto::Message { + let peer_id = self.peer_id.to_bytes(); + let addrs = self + .addresses + .into_iter() + .map(|addr| addr.to_vec()) + .collect(); + + proto::Message { + type_pb: Some(proto::MessageType::DIAL), + dial: Some(proto::Dial { + peer: Some(proto::PeerInfo { + id: Some(peer_id.to_vec()), + addrs, + }), + }), + dialResponse: None, + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ResponseError { + DialError, + DialRefused, + BadRequest, + InternalError, +} + +impl From for proto::ResponseStatus { + fn from(t: ResponseError) -> Self { + match t { + ResponseError::DialError => proto::ResponseStatus::E_DIAL_ERROR, + ResponseError::DialRefused => proto::ResponseStatus::E_DIAL_REFUSED, + ResponseError::BadRequest => proto::ResponseStatus::E_BAD_REQUEST, + ResponseError::InternalError => proto::ResponseStatus::E_INTERNAL_ERROR, + } + } +} + +impl TryFrom for ResponseError { + type Error = io::Error; + + fn try_from(value: proto::ResponseStatus) -> Result { + match value { + proto::ResponseStatus::E_DIAL_ERROR => Ok(ResponseError::DialError), + proto::ResponseStatus::E_DIAL_REFUSED => Ok(ResponseError::DialRefused), + proto::ResponseStatus::E_BAD_REQUEST => Ok(ResponseError::BadRequest), + proto::ResponseStatus::E_INTERNAL_ERROR => Ok(ResponseError::InternalError), + proto::ResponseStatus::OK => { + tracing::debug!("Received response with status code OK but expected error"); + Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid response error type", + )) + } + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DialResponse { + pub status_text: Option, + pub result: Result, +} + +impl DialResponse { + pub fn from_proto(msg: proto::Message) -> Result { + if msg.type_pb != Some(proto::MessageType::DIAL_RESPONSE) { + return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid type")); + } + + Ok(match msg.dialResponse { + Some(proto::DialResponse { + status: Some(proto::ResponseStatus::OK), + statusText, + addr: Some(addr), + }) => { + let addr = Multiaddr::try_from(addr.to_vec()) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + Self { + status_text: statusText, + result: Ok(addr), + } + } + Some(proto::DialResponse { + status: Some(status), + statusText, + addr: None, + }) => Self { + status_text: statusText, + result: Err(ResponseError::try_from(status)?), + }, + _ => { + tracing::debug!("Received malformed response message"); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid dial response message", + )); + } + }) + } + + pub fn into_proto(self) -> proto::Message { + let dial_response = match self.result { + Ok(addr) => proto::DialResponse { + status: Some(proto::ResponseStatus::OK), + statusText: self.status_text, + addr: Some(addr.to_vec()), + }, + Err(error) => proto::DialResponse { + status: Some(error.into()), + statusText: self.status_text, + addr: None, + }, + }; + + proto::Message { + type_pb: Some(proto::MessageType::DIAL_RESPONSE), + dial: None, + dialResponse: Some(dial_response), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_request_encode_decode() { + let request = DialRequest { + peer_id: PeerId::random(), + addresses: vec![ + "/ip4/8.8.8.8/tcp/30333".parse().unwrap(), + "/ip4/192.168.1.42/tcp/30333".parse().unwrap(), + ], + }; + let proto = request.clone().into_proto(); + let request2 = DialRequest::from_proto(proto).unwrap(); + assert_eq!(request, request2); + } + + #[test] + fn test_response_ok_encode_decode() { + let response = DialResponse { + result: Ok("/ip4/8.8.8.8/tcp/30333".parse().unwrap()), + status_text: None, + }; + let proto = response.clone().into_proto(); + let response2 = DialResponse::from_proto(proto).unwrap(); + assert_eq!(response, response2); + } + + #[test] + fn test_response_err_encode_decode() { + let response = DialResponse { + result: Err(ResponseError::DialError), + status_text: Some("dial failed".to_string()), + }; + let proto = response.clone().into_proto(); + let response2 = DialResponse::from_proto(proto).unwrap(); + assert_eq!(response, response2); + } + + #[test] + fn test_skip_unparsable_multiaddr() { + let valid_multiaddr: Multiaddr = "/ip6/2001:db8::/tcp/1234".parse().unwrap(); + let valid_multiaddr_bytes = valid_multiaddr.to_vec(); + + let invalid_multiaddr = { + let a = vec![255; 8]; + assert!(Multiaddr::try_from(a.clone()).is_err()); + a + }; + + let msg = proto::Message { + type_pb: Some(proto::MessageType::DIAL), + dial: Some(proto::Dial { + peer: Some(proto::PeerInfo { + id: Some(PeerId::random().to_bytes()), + addrs: vec![valid_multiaddr_bytes, invalid_multiaddr], + }), + }), + dialResponse: None, + }; + + let request = DialRequest::from_proto(msg).expect("not to fail"); + + assert_eq!(request.addresses, vec![valid_multiaddr]) + } +} diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs new file mode 100644 index 00000000000..8cab10a1d14 --- /dev/null +++ b/protocols/autonat/src/v2.rs @@ -0,0 +1,17 @@ +use libp2p_swarm::StreamProtocol; + +pub mod client; +pub(crate) mod protocol; +pub mod server; + +pub(crate) mod generated { + #![allow(unreachable_pub)] + include!("v2/generated/mod.rs"); +} + +pub(crate) const DIAL_REQUEST_PROTOCOL: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-request"); +pub(crate) const DIAL_BACK_PROTOCOL: StreamProtocol = + StreamProtocol::new("/libp2p/autonat/2/dial-back"); + +type Nonce = u64; diff --git a/protocols/autonat/src/v2/client.rs b/protocols/autonat/src/v2/client.rs new file mode 100644 index 00000000000..d3272512f35 --- /dev/null +++ b/protocols/autonat/src/v2/client.rs @@ -0,0 +1,5 @@ +mod behaviour; +mod handler; + +pub use behaviour::Event; +pub use behaviour::{Behaviour, Config}; diff --git a/protocols/autonat/src/v2/client/behaviour.rs b/protocols/autonat/src/v2/client/behaviour.rs new file mode 100644 index 00000000000..97509c05443 --- /dev/null +++ b/protocols/autonat/src/v2/client/behaviour.rs @@ -0,0 +1,439 @@ +use std::{ + collections::{HashMap, VecDeque}, + task::{Context, Poll}, + time::Duration, +}; + +use either::Either; +use futures::FutureExt; +use futures_timer::Delay; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + behaviour::ConnectionEstablished, ConnectionClosed, ConnectionDenied, ConnectionHandler, + ConnectionId, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, NotifyHandler, ToSwarm, +}; +use rand::prelude::*; +use rand_core::OsRng; +use std::fmt::{Debug, Display, Formatter}; + +use crate::v2::{protocol::DialRequest, Nonce}; + +use super::handler::{ + dial_back::{self, IncomingNonce}, + dial_request, +}; + +#[derive(Debug, Clone, Copy)] +pub struct Config { + /// How many candidates we will test at most. + pub(crate) max_candidates: usize, + + /// The interval at which we will attempt to confirm candidates as external addresses. + pub(crate) probe_interval: Duration, +} + +impl Config { + pub fn with_max_candidates(self, max_candidates: usize) -> Self { + Self { + max_candidates, + ..self + } + } + + pub fn with_probe_interval(self, probe_interval: Duration) -> Self { + Self { + probe_interval, + ..self + } + } +} + +impl Default for Config { + fn default() -> Self { + Self { + max_candidates: 10, + probe_interval: Duration::from_secs(5), + } + } +} + +pub struct Behaviour +where + R: RngCore + 'static, +{ + rng: R, + config: Config, + pending_events: VecDeque< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + >, + address_candidates: HashMap, + next_tick: Delay, + peer_info: HashMap, +} + +impl NetworkBehaviour for Behaviour +where + R: RngCore + 'static, +{ + type ConnectionHandler = Either; + + type ToSwarm = Event; + + fn handle_established_inbound_connection( + &mut self, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: &Multiaddr, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + Ok(Either::Right(dial_back::Handler::new())) + } + + fn handle_established_outbound_connection( + &mut self, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: Endpoint, + _: PortUse, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + Ok(Either::Left(dial_request::Handler::new())) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { + self.address_candidates + .entry(addr.clone()) + .or_default() + .score += 1; + } + FromSwarm::ConnectionEstablished(ConnectionEstablished { + peer_id, + connection_id, + endpoint: _, + .. + }) => { + self.peer_info.insert( + connection_id, + ConnectionInfo { + peer_id, + supports_autonat: false, + }, + ); + } + FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + .. + }) => { + let info = self + .peer_info + .remove(&connection_id) + .expect("inconsistent state"); + + if info.supports_autonat { + tracing::debug!(%peer_id, "Disconnected from AutoNAT server"); + } + } + _ => {} + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + connection_id: ConnectionId, + event: ::ToBehaviour, + ) { + let (nonce, outcome) = match event { + Either::Right(IncomingNonce { nonce, sender }) => { + let Some((_, info)) = self + .address_candidates + .iter_mut() + .find(|(_, info)| info.is_pending_with_nonce(nonce)) + else { + let _ = sender.send(Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Received unexpected nonce: {nonce} from {peer_id}"), + ))); + return; + }; + + info.status = TestStatus::Received(nonce); + tracing::debug!(%peer_id, %nonce, "Successful dial-back"); + + let _ = sender.send(Ok(())); + + return; + } + Either::Left(dial_request::ToBehaviour::PeerHasServerSupport) => { + self.peer_info + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = true; + return; + } + Either::Left(dial_request::ToBehaviour::TestOutcome { nonce, outcome }) => { + (nonce, outcome) + } + }; + + let ((tested_addr, bytes_sent), result) = match outcome { + Ok(address) => { + let received_dial_back = self + .address_candidates + .iter_mut() + .any(|(_, info)| info.is_received_with_nonce(nonce)); + + if !received_dial_back { + tracing::warn!( + %peer_id, + %nonce, + "Server reported reachbility but we never received a dial-back" + ); + return; + } + + self.pending_events + .push_back(ToSwarm::ExternalAddrConfirmed(address.0.clone())); + + (address, Ok(())) + } + Err(dial_request::Error::UnsupportedProtocol) => { + self.peer_info + .get_mut(&connection_id) + .expect("inconsistent state") + .supports_autonat = false; + + self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. + + return; + } + Err(dial_request::Error::Io(e)) => { + tracing::debug!( + %peer_id, + %nonce, + "Failed to complete AutoNAT probe: {e}" + ); + + self.reset_status_to(nonce, TestStatus::Untested); // Reset so it will be tried again. + + return; + } + Err(dial_request::Error::AddressNotReachable { + address, + bytes_sent, + error, + }) => { + self.reset_status_to(nonce, TestStatus::Failed); + + ((address, bytes_sent), Err(error)) + } + }; + + self.pending_events.push_back(ToSwarm::GenerateEvent(Event { + tested_addr, + bytes_sent, + server: peer_id, + result: result.map_err(|e| Error { inner: e }), + })); + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll::FromBehaviour>> + { + loop { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + + if self.next_tick.poll_unpin(cx).is_ready() { + self.next_tick.reset(self.config.probe_interval); + + self.issue_dial_requests_for_untested_candidates(); + continue; + } + + return Poll::Pending; + } + } +} + +impl Behaviour +where + R: RngCore + 'static, +{ + pub fn new(rng: R, config: Config) -> Self { + Self { + rng, + next_tick: Delay::new(config.probe_interval), + config, + pending_events: VecDeque::new(), + address_candidates: HashMap::new(), + peer_info: HashMap::new(), + } + } + + /// Issues dial requests to random AutoNAT servers for the most frequently reported, untested candidates. + /// + /// In the current implementation, we only send a single address to each AutoNAT server. + /// This spreads our candidates out across all servers we are connected to which should give us pretty fast feedback on all of them. + fn issue_dial_requests_for_untested_candidates(&mut self) { + for addr in self.untested_candidates() { + let Some((conn_id, peer_id)) = self.random_autonat_server() else { + tracing::debug!("Not connected to any AutoNAT servers"); + return; + }; + + let nonce = self.rng.gen(); + self.address_candidates + .get_mut(&addr) + .expect("only emit candidates") + .status = TestStatus::Pending(nonce); + + self.pending_events.push_back(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(conn_id), + event: Either::Left(DialRequest { + nonce, + addrs: vec![addr], + }), + }); + } + } + + /// Returns all untested candidates, sorted by the frequency they were reported at. + /// + /// More frequently reported candidates are considered to more likely be external addresses and thus tested first. + fn untested_candidates(&self) -> impl Iterator { + let mut entries = self + .address_candidates + .iter() + .filter(|(_, info)| info.status == TestStatus::Untested) + .map(|(addr, count)| (addr.clone(), *count)) + .collect::>(); + + entries.sort_unstable_by_key(|(_, info)| info.score); + + if entries.is_empty() { + tracing::debug!("No untested address candidates"); + } + + entries + .into_iter() + .rev() // `sort_unstable` is ascending + .take(self.config.max_candidates) + .map(|(addr, _)| addr) + } + + /// Chooses an active connection to one of our peers that reported support for the [`DIAL_REQUEST_PROTOCOL`](crate::v2::DIAL_REQUEST_PROTOCOL) protocol. + fn random_autonat_server(&mut self) -> Option<(ConnectionId, PeerId)> { + let (conn_id, info) = self + .peer_info + .iter() + .filter(|(_, info)| info.supports_autonat) + .choose(&mut self.rng)?; + + Some((*conn_id, info.peer_id)) + } + + fn reset_status_to(&mut self, nonce: Nonce, new_status: TestStatus) { + let Some((_, info)) = self + .address_candidates + .iter_mut() + .find(|(_, i)| i.is_pending_with_nonce(nonce) || i.is_received_with_nonce(nonce)) + else { + return; + }; + + info.status = new_status; + } + + // FIXME: We don't want test-only APIs in our public API. + #[doc(hidden)] + pub fn validate_addr(&mut self, addr: &Multiaddr) { + if let Some(info) = self.address_candidates.get_mut(addr) { + info.status = TestStatus::Received(self.rng.next_u64()); + } + } +} + +impl Default for Behaviour { + fn default() -> Self { + Self::new(OsRng, Config::default()) + } +} + +pub struct Error { + pub(crate) inner: dial_request::DialBackError, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +#[derive(Debug)] +pub struct Event { + /// The address that was selected for testing. + pub tested_addr: Multiaddr, + /// The amount of data that was sent to the server. + /// Is 0 if it wasn't necessary to send any data. + /// Otherwise it's a number between 30.000 and 100.000. + pub bytes_sent: usize, + /// The peer id of the server that was selected for testing. + pub server: PeerId, + /// The result of the test. If the test was successful, this is `Ok(())`. + /// Otherwise it's an error. + pub result: Result<(), Error>, +} + +struct ConnectionInfo { + peer_id: PeerId, + supports_autonat: bool, +} + +#[derive(Copy, Clone, Default)] +struct AddressInfo { + score: usize, + status: TestStatus, +} + +impl AddressInfo { + fn is_pending_with_nonce(&self, nonce: Nonce) -> bool { + match self.status { + TestStatus::Pending(c) => c == nonce, + _ => false, + } + } + + fn is_received_with_nonce(&self, nonce: Nonce) -> bool { + match self.status { + TestStatus::Received(c) => c == nonce, + _ => false, + } + } +} + +#[derive(Clone, Copy, Default, PartialEq)] +enum TestStatus { + #[default] + Untested, + Pending(Nonce), + Failed, + Received(Nonce), +} diff --git a/protocols/autonat/src/v2/client/handler.rs b/protocols/autonat/src/v2/client/handler.rs new file mode 100644 index 00000000000..e526c2fb44c --- /dev/null +++ b/protocols/autonat/src/v2/client/handler.rs @@ -0,0 +1,2 @@ +pub(crate) mod dial_back; +pub(crate) mod dial_request; diff --git a/protocols/autonat/src/v2/client/handler/dial_back.rs b/protocols/autonat/src/v2/client/handler/dial_back.rs new file mode 100644 index 00000000000..b94580e69ba --- /dev/null +++ b/protocols/autonat/src/v2/client/handler/dial_back.rs @@ -0,0 +1,141 @@ +use std::{ + io, + task::{Context, Poll}, + time::Duration, +}; + +use futures::channel::oneshot; +use futures_bounded::StreamSet; +use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use void::Void; + +use crate::v2::{protocol, Nonce, DIAL_BACK_PROTOCOL}; + +pub struct Handler { + inbound: StreamSet>, +} + +impl Handler { + pub(crate) fn new() -> Self { + Self { + inbound: StreamSet::new(Duration::from_secs(5), 2), + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = Void; + type ToBehaviour = IncomingNonce; + type InboundProtocol = ReadyUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + loop { + match self.inbound.poll_next_unpin(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => continue, + Poll::Ready(Some(Err(err))) => { + tracing::debug!("Stream timed out: {err}"); + continue; + } + Poll::Ready(Some(Ok(Err(err)))) => { + tracing::debug!("Dial back handler failed with: {err:?}"); + continue; + } + Poll::Ready(Some(Ok(Ok(incoming_nonce)))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(incoming_nonce)); + } + } + } + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + if self.inbound.try_push(perform_dial_back(protocol)).is_err() { + tracing::warn!("Dial back request dropped, too many requests in flight"); + } + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { + void::unreachable(error); + } + _ => {} + } + } +} + +struct State { + stream: libp2p_swarm::Stream, + oneshot: Option>>, +} + +#[derive(Debug)] +pub struct IncomingNonce { + pub nonce: Nonce, + pub sender: oneshot::Sender>, +} + +fn perform_dial_back( + stream: libp2p_swarm::Stream, +) -> impl futures::Stream> { + let state = State { + stream, + oneshot: None, + }; + futures::stream::unfold(state, |mut state| async move { + if let Some(ref mut receiver) = state.oneshot { + match receiver.await { + Ok(Ok(())) => {} + Ok(Err(e)) => return Some((Err(e), state)), + Err(_) => { + return Some(( + Err(io::Error::new(io::ErrorKind::Other, "Sender got cancelled")), + state, + )); + } + } + if let Err(e) = protocol::dial_back_response(&mut state.stream).await { + return Some((Err(e), state)); + } + return None; + } + + let nonce = match protocol::recv_dial_back(&mut state.stream).await { + Ok(nonce) => nonce, + Err(err) => { + return Some((Err(err), state)); + } + }; + + let (sender, receiver) = oneshot::channel(); + state.oneshot = Some(receiver); + Some((Ok(IncomingNonce { nonce, sender }), state)) + }) +} diff --git a/protocols/autonat/src/v2/client/handler/dial_request.rs b/protocols/autonat/src/v2/client/handler/dial_request.rs new file mode 100644 index 00000000000..9d2df8ee6b4 --- /dev/null +++ b/protocols/autonat/src/v2/client/handler/dial_request.rs @@ -0,0 +1,343 @@ +use futures::{channel::oneshot, AsyncWrite}; +use futures_bounded::FuturesMap; +use libp2p_core::{ + upgrade::{DeniedUpgrade, ReadyUpgrade}, + Multiaddr, +}; + +use libp2p_swarm::{ + handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound, OutboundUpgradeSend, + ProtocolsChange, + }, + ConnectionHandler, ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, + SubstreamProtocol, +}; +use std::{ + collections::VecDeque, + io, + iter::{once, repeat}, + task::{Context, Poll}, + time::Duration, +}; + +use crate::v2::{ + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + protocol::{ + Coder, DialDataRequest, DialDataResponse, DialRequest, Response, + DATA_FIELD_LEN_UPPER_BOUND, DATA_LEN_LOWER_BOUND, DATA_LEN_UPPER_BOUND, + }, + Nonce, DIAL_REQUEST_PROTOCOL, +}; + +#[derive(Debug)] +pub enum ToBehaviour { + TestOutcome { + nonce: Nonce, + outcome: Result<(Multiaddr, usize), Error>, + }, + PeerHasServerSupport, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Address is not reachable: {error}")] + AddressNotReachable { + address: Multiaddr, + bytes_sent: usize, + error: DialBackError, + }, + #[error("Peer does not support AutoNAT dial-request protocol")] + UnsupportedProtocol, + #[error("IO error: {0}")] + Io(io::Error), +} + +impl From for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum DialBackError { + #[error("server failed to establish a connection")] + NoConnection, + #[error("dial back stream failed")] + StreamFailed, +} + +pub struct Handler { + queued_events: VecDeque< + ConnectionHandlerEvent< + ::OutboundProtocol, + ::OutboundOpenInfo, + ::ToBehaviour, + >, + >, + outbound: FuturesMap>, + queued_streams: VecDeque< + oneshot::Sender< + Result< + Stream, + StreamUpgradeError< as OutboundUpgradeSend>::Error>, + >, + >, + >, +} + +impl Handler { + pub(crate) fn new() -> Self { + Self { + queued_events: VecDeque::new(), + outbound: FuturesMap::new(Duration::from_secs(10), 10), + queued_streams: VecDeque::default(), + } + } + + fn perform_request(&mut self, req: DialRequest) { + let (tx, rx) = oneshot::channel(); + self.queued_streams.push_back(tx); + self.queued_events + .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()), + }); + if self + .outbound + .try_push(req.nonce, start_stream_handle(req, rx)) + .is_err() + { + tracing::debug!("Dial request dropped, too many requests in flight"); + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = DialRequest; + type ToBehaviour = ToBehaviour; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = ReadyUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Some(event) = self.queued_events.pop_front() { + return Poll::Ready(event); + } + + match self.outbound.poll_unpin(cx) { + Poll::Ready((nonce, Ok(outcome))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestOutcome { nonce, outcome }, + )) + } + Poll::Ready((nonce, Err(_))) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::TestOutcome { + nonce, + outcome: Err(Error::Io(io::ErrorKind::TimedOut.into())), + }, + )); + } + Poll::Pending => {} + } + + Poll::Pending + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + self.perform_request(event); + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + tracing::debug!("Dial request failed: {}", error); + match self.queued_streams.pop_front() { + Some(stream_tx) => { + let _ = stream_tx.send(Err(error)); + } + None => { + tracing::warn!( + "Opened unexpected substream without a pending dial request" + ); + } + } + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => match self.queued_streams.pop_front() { + Some(stream_tx) => { + if stream_tx.send(Ok(protocol)).is_err() { + tracing::debug!("Failed to send stream to dead handler"); + } + } + None => { + tracing::warn!("Opened unexpected substream without a pending dial request"); + } + }, + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(mut added)) => { + if added.any(|p| p.as_ref() == DIAL_REQUEST_PROTOCOL) { + self.queued_events + .push_back(ConnectionHandlerEvent::NotifyBehaviour( + ToBehaviour::PeerHasServerSupport, + )); + } + } + _ => {} + } + } +} + +async fn start_stream_handle( + req: DialRequest, + stream_recv: oneshot::Receiver>>, +) -> Result<(Multiaddr, usize), Error> { + let stream = stream_recv + .await + .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))? + .map_err(|e| match e { + StreamUpgradeError::NegotiationFailed => Error::UnsupportedProtocol, + StreamUpgradeError::Timeout => Error::Io(io::ErrorKind::TimedOut.into()), + StreamUpgradeError::Apply(v) => void::unreachable(v), + StreamUpgradeError::Io(e) => Error::Io(e), + })?; + + let mut coder = Coder::new(stream); + coder.send(req.clone()).await?; + + let (res, bytes_sent) = match coder.next().await? { + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => { + if addr_idx >= req.addrs.len() { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "address index out of bounds", + ))); + } + if !(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND).contains(&num_bytes) { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "requested bytes out of bounds", + ))); + } + + send_aap_data(&mut coder, num_bytes).await?; + + let Response::Dial(dial_response) = coder.next().await? else { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "expected message", + ))); + }; + + (dial_response, num_bytes) + } + Response::Dial(dial_response) => (dial_response, 0), + }; + match coder.close().await { + Ok(_) => {} + Err(err) => { + if err.kind() == io::ErrorKind::ConnectionReset { + // The AutoNAT server may have already closed the stream (this is normal because the probe is finished), in this case we have this error: + // Err(Custom { kind: ConnectionReset, error: Stopped(0) }) + // so we silently ignore this error + } else { + return Err(err.into()); + } + } + } + + match res.status { + ResponseStatus::E_REQUEST_REJECTED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server rejected request", + ))) + } + ResponseStatus::E_DIAL_REFUSED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server refused dial", + ))) + } + ResponseStatus::E_INTERNAL_ERROR => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::Other, + "server encountered internal error", + ))) + } + ResponseStatus::OK => {} + } + + let tested_address = req + .addrs + .get(res.addr_idx) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "address index out of bounds"))? + .clone(); + + match res.dial_status { + DialStatus::UNUSED => { + return Err(Error::Io(io::Error::new( + io::ErrorKind::InvalidInput, + "unexpected message", + ))) + } + DialStatus::E_DIAL_ERROR => { + return Err(Error::AddressNotReachable { + address: tested_address, + bytes_sent, + error: DialBackError::NoConnection, + }) + } + DialStatus::E_DIAL_BACK_ERROR => { + return Err(Error::AddressNotReachable { + address: tested_address, + bytes_sent, + error: DialBackError::StreamFailed, + }) + } + DialStatus::OK => {} + } + + Ok((tested_address, bytes_sent)) +} + +async fn send_aap_data(stream: &mut Coder, num_bytes: usize) -> io::Result<()> +where + I: AsyncWrite + Unpin, +{ + let count_full = num_bytes / DATA_FIELD_LEN_UPPER_BOUND; + let partial_len = num_bytes % DATA_FIELD_LEN_UPPER_BOUND; + for req in repeat(DATA_FIELD_LEN_UPPER_BOUND) + .take(count_full) + .chain(once(partial_len)) + .filter(|e| *e > 0) + .map(|data_count| { + DialDataResponse::new(data_count).expect("data count is unexpectedly too big") + }) + { + stream.send(req).await?; + } + + Ok(()) +} diff --git a/protocols/autonat/src/v2/generated/mod.rs b/protocols/autonat/src/v2/generated/mod.rs new file mode 100644 index 00000000000..e52c5a80bc0 --- /dev/null +++ b/protocols/autonat/src/v2/generated/mod.rs @@ -0,0 +1,2 @@ +// Automatically generated mod.rs +pub mod structs; diff --git a/protocols/autonat/src/v2/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto new file mode 100644 index 00000000000..31791463956 --- /dev/null +++ b/protocols/autonat/src/v2/generated/structs.proto @@ -0,0 +1,54 @@ +syntax = "proto2"; + +package structs; + +message Message { + oneof msg { + DialRequest dialRequest = 1; + DialResponse dialResponse = 2; + DialDataRequest dialDataRequest = 3; + DialDataResponse dialDataResponse = 4; + } +} + +message DialRequest { + repeated bytes addrs = 1; + fixed64 nonce = 2; +} + +message DialDataRequest { + uint32 addrIdx = 1; + uint64 numBytes = 2; +} + +enum DialStatus { + UNUSED = 0; + E_DIAL_ERROR = 100; + E_DIAL_BACK_ERROR = 101; + OK = 200; +} + +message DialResponse { + enum ResponseStatus { + E_INTERNAL_ERROR = 0; + E_REQUEST_REJECTED = 100; + E_DIAL_REFUSED = 101; + OK = 200; + } + + ResponseStatus status = 1; + uint32 addrIdx = 2; + DialStatus dialStatus = 3; +} + +message DialDataResponse { bytes data = 1; } + +message DialBack { fixed64 nonce = 1; } + +message DialBackResponse { + enum DialBackStatus { + OK = 0; + } + + DialBackStatus status = 1; +} diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs new file mode 100644 index 00000000000..12568dd0364 --- /dev/null +++ b/protocols/autonat/src/v2/generated/structs.rs @@ -0,0 +1,403 @@ +// Automatically generated rust module for 'structs.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::*; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DialStatus { + UNUSED = 0, + E_DIAL_ERROR = 100, + E_DIAL_BACK_ERROR = 101, + OK = 200, +} + +impl Default for DialStatus { + fn default() -> Self { + DialStatus::UNUSED + } +} + +impl From for DialStatus { + fn from(i: i32) -> Self { + match i { + 0 => DialStatus::UNUSED, + 100 => DialStatus::E_DIAL_ERROR, + 101 => DialStatus::E_DIAL_BACK_ERROR, + 200 => DialStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for DialStatus { + fn from(s: &'a str) -> Self { + match s { + "UNUSED" => DialStatus::UNUSED, + "E_DIAL_ERROR" => DialStatus::E_DIAL_ERROR, + "E_DIAL_BACK_ERROR" => DialStatus::E_DIAL_BACK_ERROR, + "OK" => DialStatus::OK, + _ => Self::default(), + } + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct Message { + pub msg: structs::mod_Message::OneOfmsg, +} + +impl<'a> MessageRead<'a> for Message { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.msg = structs::mod_Message::OneOfmsg::dialRequest(r.read_message::(bytes)?), + Ok(18) => msg.msg = structs::mod_Message::OneOfmsg::dialResponse(r.read_message::(bytes)?), + Ok(26) => msg.msg = structs::mod_Message::OneOfmsg::dialDataRequest(r.read_message::(bytes)?), + Ok(34) => msg.msg = structs::mod_Message::OneOfmsg::dialDataResponse(r.read_message::(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for Message { + fn get_size(&self) -> usize { + 0 + + match self.msg { + structs::mod_Message::OneOfmsg::dialRequest(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialResponse(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => 1 + sizeof_len((m).get_size()), + structs::mod_Message::OneOfmsg::None => 0, + } } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + match self.msg { structs::mod_Message::OneOfmsg::dialRequest(ref m) => { w.write_with_tag(10, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialResponse(ref m) => { w.write_with_tag(18, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialDataRequest(ref m) => { w.write_with_tag(26, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::dialDataResponse(ref m) => { w.write_with_tag(34, |w| w.write_message(m))? }, + structs::mod_Message::OneOfmsg::None => {}, + } Ok(()) + } +} + +pub mod mod_Message { + +use super::*; + +#[derive(Debug, PartialEq, Clone)] +pub enum OneOfmsg { + dialRequest(structs::DialRequest), + dialResponse(structs::DialResponse), + dialDataRequest(structs::DialDataRequest), + dialDataResponse(structs::DialDataResponse), + None, +} + +impl Default for OneOfmsg { + fn default() -> Self { + OneOfmsg::None + } +} + +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialRequest { + pub addrs: Vec>, + pub nonce: Option, +} + +impl<'a> MessageRead<'a> for DialRequest { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), + Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialRequest { + fn get_size(&self) -> usize { + 0 + + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() + + self.nonce.as_ref().map_or(0, |_| 1 + 8) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + for s in &self.addrs { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + if let Some(ref s) = self.nonce { w.write_with_tag(17, |w| w.write_fixed64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialDataRequest { + pub addrIdx: Option, + pub numBytes: Option, +} + +impl<'a> MessageRead<'a> for DialDataRequest { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.addrIdx = Some(r.read_uint32(bytes)?), + Ok(16) => msg.numBytes = Some(r.read_uint64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialDataRequest { + fn get_size(&self) -> usize { + 0 + + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.numBytes.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.addrIdx { w.write_with_tag(8, |w| w.write_uint32(*s))?; } + if let Some(ref s) = self.numBytes { w.write_with_tag(16, |w| w.write_uint64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialResponse { + pub status: Option, + pub addrIdx: Option, + pub dialStatus: Option, +} + +impl<'a> MessageRead<'a> for DialResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(16) => msg.addrIdx = Some(r.read_uint32(bytes)?), + Ok(24) => msg.dialStatus = Some(r.read_enum(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + self.dialStatus.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + if let Some(ref s) = self.addrIdx { w.write_with_tag(16, |w| w.write_uint32(*s))?; } + if let Some(ref s) = self.dialStatus { w.write_with_tag(24, |w| w.write_enum(*s as i32))?; } + Ok(()) + } +} + +pub mod mod_DialResponse { + + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ResponseStatus { + E_INTERNAL_ERROR = 0, + E_REQUEST_REJECTED = 100, + E_DIAL_REFUSED = 101, + OK = 200, +} + +impl Default for ResponseStatus { + fn default() -> Self { + ResponseStatus::E_INTERNAL_ERROR + } +} + +impl From for ResponseStatus { + fn from(i: i32) -> Self { + match i { + 0 => ResponseStatus::E_INTERNAL_ERROR, + 100 => ResponseStatus::E_REQUEST_REJECTED, + 101 => ResponseStatus::E_DIAL_REFUSED, + 200 => ResponseStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for ResponseStatus { + fn from(s: &'a str) -> Self { + match s { + "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, + "E_REQUEST_REJECTED" => ResponseStatus::E_REQUEST_REJECTED, + "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, + "OK" => ResponseStatus::OK, + _ => Self::default(), + } + } +} + +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialDataResponse { + pub data: Option>, +} + +impl<'a> MessageRead<'a> for DialDataResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned()), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialDataResponse { + fn get_size(&self) -> usize { + 0 + + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.data { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialBack { + pub nonce: Option, +} + +impl<'a> MessageRead<'a> for DialBack { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(9) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialBack { + fn get_size(&self) -> usize { + 0 + + self.nonce.as_ref().map_or(0, |_| 1 + 8) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.nonce { w.write_with_tag(9, |w| w.write_fixed64(*s))?; } + Ok(()) + } +} + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct DialBackResponse { + pub status: Option, +} + +impl<'a> MessageRead<'a> for DialBackResponse { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for DialBackResponse { + fn get_size(&self) -> usize { + 0 + + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + Ok(()) + } +} + +pub mod mod_DialBackResponse { + + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DialBackStatus { + OK = 0, +} + +impl Default for DialBackStatus { + fn default() -> Self { + DialBackStatus::OK + } +} + +impl From for DialBackStatus { + fn from(i: i32) -> Self { + match i { + 0 => DialBackStatus::OK, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for DialBackStatus { + fn from(s: &'a str) -> Self { + match s { + "OK" => DialBackStatus::OK, + _ => Self::default(), + } + } +} + +} + diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs new file mode 100644 index 00000000000..126077fe936 --- /dev/null +++ b/protocols/autonat/src/v2/protocol.rs @@ -0,0 +1,361 @@ +// change to quick-protobuf-codec + +use std::io; +use std::io::ErrorKind; + +use asynchronous_codec::{Framed, FramedRead, FramedWrite}; + +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use libp2p_core::Multiaddr; + +use quick_protobuf_codec::Codec; +use rand::Rng; + +use crate::v2::{generated::structs as proto, Nonce}; + +const REQUEST_MAX_SIZE: usize = 4104; +pub(super) const DATA_LEN_LOWER_BOUND: usize = 30_000u32 as usize; +pub(super) const DATA_LEN_UPPER_BOUND: usize = 100_000u32 as usize; +pub(super) const DATA_FIELD_LEN_UPPER_BOUND: usize = 4096; + +fn new_io_invalid_data_err(msg: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, msg.into()) +} + +macro_rules! ok_or_invalid_data { + ($field:ident) => { + $field.ok_or_else(|| new_io_invalid_data_err(concat!(stringify!($field), " is missing"))) + }; +} + +pub(crate) struct Coder { + inner: Framed>, +} + +impl Coder +where + I: AsyncWrite + AsyncRead + Unpin, +{ + pub(crate) fn new(io: I) -> Self { + Self { + inner: Framed::new(io, Codec::new(REQUEST_MAX_SIZE)), + } + } + pub(crate) async fn close(mut self) -> io::Result<()> { + self.inner.close().await?; + Ok(()) + } +} + +impl Coder +where + I: AsyncRead + Unpin, +{ + pub(crate) async fn next(&mut self) -> io::Result + where + proto::Message: TryInto, + io::Error: From, + { + Ok(self.next_msg().await?.try_into()?) + } + + async fn next_msg(&mut self) -> io::Result { + self.inner + .next() + .await + .ok_or(io::Error::new( + ErrorKind::UnexpectedEof, + "no request to read", + ))? + .map_err(|e| io::Error::new(ErrorKind::InvalidData, e)) + } +} + +impl Coder +where + I: AsyncWrite + Unpin, +{ + pub(crate) async fn send(&mut self, msg: M) -> io::Result<()> + where + M: Into, + { + self.inner.send(msg.into()).await?; + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum Request { + Dial(DialRequest), + Data(DialDataResponse), +} + +impl From for proto::Message { + fn from(val: DialRequest) -> Self { + let addrs = val.addrs.iter().map(|e| e.to_vec()).collect(); + let nonce = Some(val.nonce); + + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }), + } + } +} + +impl From for proto::Message { + fn from(val: DialDataResponse) -> Self { + debug_assert!( + val.data_count <= DATA_FIELD_LEN_UPPER_BOUND, + "data_count too large" + ); + proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { + data: Some(vec![0; val.data_count]), // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI + }), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct DialRequest { + pub(crate) addrs: Vec, + pub(crate) nonce: u64, +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct DialDataResponse { + data_count: usize, +} + +impl DialDataResponse { + pub(crate) fn new(data_count: usize) -> Option { + if data_count <= DATA_FIELD_LEN_UPPER_BOUND { + Some(Self { data_count }) + } else { + None + } + } + + pub(crate) fn get_data_count(&self) -> usize { + self.data_count + } +} + +impl TryFrom for Request { + type Error = io::Error; + + fn try_from(msg: proto::Message) -> Result { + match msg.msg { + proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }) => { + let addrs = addrs + .into_iter() + .map(|e| e.to_vec()) + .map(|e| { + Multiaddr::try_from(e).map_err(|err| { + new_io_invalid_data_err(format!("invalid multiaddr: {}", err)) + }) + }) + .collect::, io::Error>>()?; + let nonce = ok_or_invalid_data!(nonce)?; + Ok(Self::Dial(DialRequest { addrs, nonce })) + } + proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { + let data_count = ok_or_invalid_data!(data)?.len(); + Ok(Self::Data(DialDataResponse { data_count })) + } + _ => Err(new_io_invalid_data_err( + "expected dialResponse or dialDataRequest", + )), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum Response { + Dial(DialResponse), + Data(DialDataRequest), +} + +#[derive(Debug, Clone)] +pub(crate) struct DialDataRequest { + pub(crate) addr_idx: usize, + pub(crate) num_bytes: usize, +} + +#[derive(Debug, Clone)] +pub(crate) struct DialResponse { + pub(crate) status: proto::mod_DialResponse::ResponseStatus, + pub(crate) addr_idx: usize, + pub(crate) dial_status: proto::DialStatus, +} + +impl TryFrom for Response { + type Error = io::Error; + + fn try_from(msg: proto::Message) -> Result { + match msg.msg { + proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { + status, + addrIdx, + dialStatus, + }) => { + let status = ok_or_invalid_data!(status)?; + let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; + let dial_status = ok_or_invalid_data!(dialStatus)?; + Ok(Response::Dial(DialResponse { + status, + addr_idx, + dial_status, + })) + } + proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx, + numBytes, + }) => { + let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; + let num_bytes = ok_or_invalid_data!(numBytes)? as usize; + Ok(Self::Data(DialDataRequest { + addr_idx, + num_bytes, + })) + } + _ => Err(new_io_invalid_data_err( + "invalid message type, expected dialResponse or dialDataRequest", + )), + } + } +} + +impl From for proto::Message { + fn from(val: Response) -> Self { + match val { + Response::Dial(DialResponse { + status, + addr_idx, + dial_status, + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { + status: Some(status), + addrIdx: Some(addr_idx as u32), + dialStatus: Some(dial_status), + }), + }, + Response::Data(DialDataRequest { + addr_idx, + num_bytes, + }) => proto::Message { + msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { + addrIdx: Some(addr_idx as u32), + numBytes: Some(num_bytes as u64), + }), + }, + } + } +} + +impl DialDataRequest { + pub(crate) fn from_rng(addr_idx: usize, mut rng: R) -> Self { + let num_bytes = rng.gen_range(DATA_LEN_LOWER_BOUND..=DATA_LEN_UPPER_BOUND); + Self { + addr_idx, + num_bytes, + } + } +} + +const DIAL_BACK_MAX_SIZE: usize = 10; + +pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { + let msg = proto::DialBack { nonce: Some(nonce) }; + let mut framed = FramedWrite::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); + + framed + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + Ok(()) +} + +pub(crate) async fn recv_dial_back(stream: impl AsyncRead + Unpin) -> io::Result { + let framed = &mut FramedRead::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); + let proto::DialBack { nonce } = framed + .next() + .await + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; + let nonce = ok_or_invalid_data!(nonce)?; + + Ok(nonce) +} + +pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::Result<()> { + let msg = proto::DialBackResponse { + status: Some(proto::mod_DialBackResponse::DialBackStatus::OK), + }; + let mut framed = FramedWrite::new( + stream, + Codec::::new(DIAL_BACK_MAX_SIZE), + ); + framed + .send(msg) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + Ok(()) +} + +pub(crate) async fn recv_dial_back_response( + stream: impl AsyncRead + AsyncWrite + Unpin, +) -> io::Result<()> { + let framed = &mut FramedRead::new( + stream, + Codec::::new(DIAL_BACK_MAX_SIZE), + ); + let proto::DialBackResponse { status } = framed + .next() + .await + .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; + + if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid dial back response", + )) + } +} + +#[cfg(test)] +mod tests { + use crate::v2::generated::structs::{ + mod_Message::OneOfmsg, DialDataResponse as GenDialDataResponse, Message, + }; + + #[test] + fn message_correct_max_size() { + let message_bytes = quick_protobuf::serialize_into_vec(&Message { + msg: OneOfmsg::dialDataResponse(GenDialDataResponse { + data: Some(vec![0; 4096]), + }), + }) + .unwrap(); + assert_eq!(message_bytes.len(), super::REQUEST_MAX_SIZE); + } + + #[test] + fn dial_back_correct_size() { + let dial_back = super::proto::DialBack { nonce: Some(0) }; + let buf = quick_protobuf::serialize_into_vec(&dial_back).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + + let dial_back_none = super::proto::DialBack { nonce: None }; + let buf = quick_protobuf::serialize_into_vec(&dial_back_none).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + + let dial_back_max_nonce = super::proto::DialBack { + nonce: Some(u64::MAX), + }; + let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); + assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); + } +} diff --git a/protocols/autonat/src/v2/server.rs b/protocols/autonat/src/v2/server.rs new file mode 100644 index 00000000000..25819307784 --- /dev/null +++ b/protocols/autonat/src/v2/server.rs @@ -0,0 +1,5 @@ +mod behaviour; +mod handler; + +pub use behaviour::Behaviour; +pub use behaviour::Event; diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs new file mode 100644 index 00000000000..97446c6263f --- /dev/null +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -0,0 +1,155 @@ +use std::{ + collections::{HashMap, VecDeque}, + io, + task::{Context, Poll}, +}; + +use crate::v2::server::handler::dial_request::DialBackStatus; +use either::Either; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use libp2p_swarm::dial_opts::PeerCondition; +use libp2p_swarm::{ + dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, + FromSwarm, NetworkBehaviour, ToSwarm, +}; +use rand_core::{OsRng, RngCore}; + +use crate::v2::server::handler::{ + dial_back, + dial_request::{self, DialBackCommand}, + Handler, +}; + +pub struct Behaviour +where + R: Clone + Send + RngCore + 'static, +{ + dialing_dial_back: HashMap, + pending_events: VecDeque< + ToSwarm< + ::ToSwarm, + <::ConnectionHandler as ConnectionHandler>::FromBehaviour, + >, + >, + rng: R, +} + +impl Default for Behaviour { + fn default() -> Self { + Self::new(OsRng) + } +} + +impl Behaviour +where + R: RngCore + Send + Clone + 'static, +{ + pub fn new(rng: R) -> Self { + Self { + dialing_dial_back: HashMap::new(), + pending_events: VecDeque::new(), + rng, + } + } +} + +impl NetworkBehaviour for Behaviour +where + R: RngCore + Send + Clone + 'static, +{ + type ConnectionHandler = Handler; + + type ToSwarm = Event; + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + _local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + Ok(Either::Right(dial_request::Handler::new( + peer, + remote_addr.clone(), + self.rng.clone(), + ))) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + _peer: PeerId, + _addr: &Multiaddr, + _role_override: Endpoint, + _port_use: PortUse, + ) -> Result<::ConnectionHandler, ConnectionDenied> { + Ok(match self.dialing_dial_back.remove(&connection_id) { + Some(cmd) => Either::Left(Either::Left(dial_back::Handler::new(cmd))), + None => Either::Left(Either::Right(dummy::ConnectionHandler)), + }) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { + if let Some(DialBackCommand { back_channel, .. }) = + self.dialing_dial_back.remove(&connection_id) + { + let _ = back_channel.send(Err(DialBackStatus::DialErr)); + } + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + _connection_id: ConnectionId, + event: as ConnectionHandler>::ToBehaviour, + ) { + match event { + Either::Left(Either::Left(Ok(_))) => {} + Either::Left(Either::Left(Err(e))) => { + tracing::debug!("dial back error: {e:?}"); + } + Either::Left(Either::Right(v)) => void::unreachable(v), + Either::Right(Either::Left(cmd)) => { + let addr = cmd.addr.clone(); + let opts = DialOpts::peer_id(peer_id) + .addresses(Vec::from([addr])) + .condition(PeerCondition::Always) + .allocate_new_port() + .build(); + let conn_id = opts.connection_id(); + self.dialing_dial_back.insert(conn_id, cmd); + self.pending_events.push_back(ToSwarm::Dial { opts }); + } + Either::Right(Either::Right(status_update)) => self + .pending_events + .push_back(ToSwarm::GenerateEvent(status_update)), + } + } + + fn poll( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll as ConnectionHandler>::FromBehaviour>> { + if let Some(event) = self.pending_events.pop_front() { + return Poll::Ready(event); + } + Poll::Pending + } +} + +#[derive(Debug)] +pub struct Event { + /// All address that were submitted for testing. + pub all_addrs: Vec, + /// The address that was eventually tested. + pub tested_addr: Multiaddr, + /// The peer id of the client that submitted addresses for testing. + pub client: PeerId, + /// The amount of data that was requested by the server and was transmitted. + pub data_amount: usize, + /// The result of the test. + pub result: Result<(), io::Error>, +} diff --git a/protocols/autonat/src/v2/server/handler.rs b/protocols/autonat/src/v2/server/handler.rs new file mode 100644 index 00000000000..ffdad69c86f --- /dev/null +++ b/protocols/autonat/src/v2/server/handler.rs @@ -0,0 +1,8 @@ +use either::Either; +use libp2p_swarm::dummy; + +pub(crate) mod dial_back; +pub(crate) mod dial_request; + +pub(crate) type Handler = + Either, dial_request::Handler>; diff --git a/protocols/autonat/src/v2/server/handler/dial_back.rs b/protocols/autonat/src/v2/server/handler/dial_back.rs new file mode 100644 index 00000000000..3cacd4ff32b --- /dev/null +++ b/protocols/autonat/src/v2/server/handler/dial_back.rs @@ -0,0 +1,140 @@ +use std::{ + convert::identity, + io, + task::{Context, Poll}, + time::Duration, +}; + +use futures::{AsyncRead, AsyncWrite}; +use futures_bounded::FuturesSet; +use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; +use libp2p_swarm::{ + handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedOutbound}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, + SubstreamProtocol, +}; + +use crate::v2::{ + protocol::{dial_back, recv_dial_back_response}, + DIAL_BACK_PROTOCOL, +}; + +use super::dial_request::{DialBackCommand, DialBackStatus as DialBackRes}; + +pub(crate) type ToBehaviour = io::Result<()>; + +pub struct Handler { + pending_nonce: Option, + requested_substream_nonce: Option, + outbound: FuturesSet, +} + +impl Handler { + pub(crate) fn new(cmd: DialBackCommand) -> Self { + Self { + pending_nonce: Some(cmd), + requested_substream_nonce: None, + outbound: FuturesSet::new(Duration::from_secs(10), 5), + } + } +} + +impl ConnectionHandler for Handler { + type FromBehaviour = (); + type ToBehaviour = ToBehaviour; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = ReadyUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Poll::Ready(result) = self.outbound.poll_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + result + .map_err(|timeout| io::Error::new(io::ErrorKind::TimedOut, timeout)) + .and_then(identity), + )); + } + if let Some(cmd) = self.pending_nonce.take() { + self.requested_substream_nonce = Some(cmd); + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(ReadyUpgrade::new(DIAL_BACK_PROTOCOL), ()), + }); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => { + if let Some(cmd) = self.requested_substream_nonce.take() { + if self + .outbound + .try_push(perform_dial_back(protocol, cmd)) + .is_err() + { + tracing::warn!("Dial back dropped, too many requests in flight"); + } + } else { + tracing::warn!("received dial back substream without nonce"); + } + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { + error: StreamUpgradeError::NegotiationFailed | StreamUpgradeError::Timeout, + .. + }) => { + if let Some(cmd) = self.requested_substream_nonce.take() { + let _ = cmd.back_channel.send(Err(DialBackRes::DialBackErr)); + } + } + _ => {} + } + } +} + +async fn perform_dial_back( + mut stream: impl AsyncRead + AsyncWrite + Unpin, + DialBackCommand { + nonce, + back_channel, + .. + }: DialBackCommand, +) -> io::Result<()> { + let res = dial_back(&mut stream, nonce) + .await + .map_err(|_| DialBackRes::DialBackErr) + .map(|_| ()); + + let res = match res { + Ok(()) => recv_dial_back_response(stream) + .await + .map_err(|_| DialBackRes::DialBackErr) + .map(|_| ()), + Err(e) => Err(e), + }; + back_channel + .send(res) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "send error"))?; + Ok(()) +} diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs new file mode 100644 index 00000000000..f8c3f6b654d --- /dev/null +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -0,0 +1,332 @@ +use std::{ + io, + task::{Context, Poll}, + time::Duration, +}; + +use either::Either; +use futures::{ + channel::{mpsc, oneshot}, + AsyncRead, AsyncWrite, SinkExt, StreamExt, +}; +use futures_bounded::FuturesSet; +use libp2p_core::{ + upgrade::{DeniedUpgrade, ReadyUpgrade}, + Multiaddr, +}; +use libp2p_identity::PeerId; +use libp2p_swarm::{ + handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, + ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, +}; +use rand_core::RngCore; + +use crate::v2::{ + generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, + server::behaviour::Event, + Nonce, DIAL_REQUEST_PROTOCOL, +}; + +#[derive(Debug, PartialEq)] +pub(crate) enum DialBackStatus { + /// Failure during dial + DialErr, + /// Failure during dial back + DialBackErr, +} + +#[derive(Debug)] +pub struct DialBackCommand { + pub(crate) addr: Multiaddr, + pub(crate) nonce: Nonce, + pub(crate) back_channel: oneshot::Sender>, +} + +pub struct Handler { + client_id: PeerId, + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + dial_back_cmd_receiver: mpsc::Receiver, + inbound: FuturesSet, + rng: R, +} + +impl Handler +where + R: RngCore, +{ + pub(crate) fn new(client_id: PeerId, observed_multiaddr: Multiaddr, rng: R) -> Self { + let (dial_back_cmd_sender, dial_back_cmd_receiver) = mpsc::channel(10); + Self { + client_id, + observed_multiaddr, + dial_back_cmd_sender, + dial_back_cmd_receiver, + inbound: FuturesSet::new(Duration::from_secs(10), 10), + rng, + } + } +} + +impl ConnectionHandler for Handler +where + R: RngCore + Send + Clone + 'static, +{ + type FromBehaviour = void::Void; + type ToBehaviour = Either; + type InboundProtocol = ReadyUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(ReadyUpgrade::new(DIAL_REQUEST_PROTOCOL), ()) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + loop { + match self.inbound.poll_unpin(cx) { + Poll::Ready(Ok(event)) => { + if let Err(e) = &event.result { + tracing::warn!("inbound request handle failed: {:?}", e); + } + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + event, + ))); + } + Poll::Ready(Err(e)) => { + tracing::warn!("inbound request handle timed out {e:?}"); + } + Poll::Pending => break, + } + } + if let Poll::Ready(Some(cmd)) = self.dial_back_cmd_receiver.poll_next_unpin(cx) { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(cmd))); + } + Poll::Pending + } + + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour) {} + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => { + if self + .inbound + .try_push(handle_request( + protocol, + self.observed_multiaddr.clone(), + self.client_id, + self.dial_back_cmd_sender.clone(), + self.rng.clone(), + )) + .is_err() + { + tracing::warn!( + "failed to push inbound request handler, too many requests in flight" + ); + } + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { error, .. }) => { + tracing::debug!("inbound request failed: {:?}", error); + } + _ => {} + } + } +} + +enum HandleFail { + InternalError(usize), + RequestRejected, + DialRefused, + DialBack { + idx: usize, + result: Result<(), DialBackStatus>, + }, +} + +impl From for DialResponse { + fn from(value: HandleFail) -> Self { + match value { + HandleFail::InternalError(addr_idx) => Self { + status: ResponseStatus::E_INTERNAL_ERROR, + addr_idx, + dial_status: DialStatus::UNUSED, + }, + HandleFail::RequestRejected => Self { + status: ResponseStatus::E_REQUEST_REJECTED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + }, + HandleFail::DialRefused => Self { + status: ResponseStatus::E_DIAL_REFUSED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + }, + HandleFail::DialBack { idx, result } => Self { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: match result { + Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, + Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, + Ok(()) => DialStatus::OK, + }, + }, + } + } +} + +async fn handle_request( + stream: impl AsyncRead + AsyncWrite + Unpin, + observed_multiaddr: Multiaddr, + client: PeerId, + dial_back_cmd_sender: mpsc::Sender, + rng: impl RngCore, +) -> Event { + let mut coder = Coder::new(stream); + let mut all_addrs = Vec::new(); + let mut tested_addr_opt = None; + let mut data_amount = 0; + let response = handle_request_internal( + &mut coder, + observed_multiaddr.clone(), + dial_back_cmd_sender, + rng, + &mut all_addrs, + &mut tested_addr_opt, + &mut data_amount, + ) + .await + .unwrap_or_else(|e| e.into()); + let Some(tested_addr) = tested_addr_opt else { + return Event { + all_addrs, + tested_addr: observed_multiaddr, + client, + data_amount, + result: Err(io::Error::new( + io::ErrorKind::Other, + "client is not conformint to protocol. the tested address is not the observed address", + )), + }; + }; + if let Err(e) = coder.send(Response::Dial(response)).await { + return Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Err(e), + }; + } + if let Err(e) = coder.close().await { + return Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Err(e), + }; + } + Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Ok(()), + } +} + +async fn handle_request_internal( + coder: &mut Coder, + observed_multiaddr: Multiaddr, + dial_back_cmd_sender: mpsc::Sender, + mut rng: impl RngCore, + all_addrs: &mut Vec, + tested_addrs: &mut Option, + data_amount: &mut usize, +) -> Result +where + I: AsyncRead + AsyncWrite + Unpin, +{ + let DialRequest { mut addrs, nonce } = match coder + .next() + .await + .map_err(|_| HandleFail::InternalError(0))? + { + Request::Dial(dial_request) => dial_request, + Request::Data(_) => { + return Err(HandleFail::RequestRejected); + } + }; + *all_addrs = addrs.clone(); + let idx = 0; + let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; + *tested_addrs = Some(addr.clone()); + *data_amount = 0; + if addr != observed_multiaddr { + let dial_data_request = DialDataRequest::from_rng(idx, &mut rng); + let mut rem_data = dial_data_request.num_bytes; + coder + .send(Response::Data(dial_data_request)) + .await + .map_err(|_| HandleFail::InternalError(idx))?; + while rem_data > 0 { + let data_count = match coder + .next() + .await + .map_err(|_e| HandleFail::InternalError(idx))? + { + Request::Dial(_) => { + return Err(HandleFail::RequestRejected); + } + Request::Data(dial_data_response) => dial_data_response.get_data_count(), + }; + rem_data = rem_data.saturating_sub(data_count); + *data_amount += data_count; + } + } + let (back_channel, rx) = oneshot::channel(); + let dial_back_cmd = DialBackCommand { + addr, + nonce, + back_channel, + }; + dial_back_cmd_sender + .clone() + .send(dial_back_cmd) + .await + .map_err(|_| HandleFail::DialBack { + idx, + result: Err(DialBackStatus::DialErr), + })?; + + let dial_back = rx.await.map_err(|_e| HandleFail::InternalError(idx))?; + if let Err(err) = dial_back { + return Err(HandleFail::DialBack { + idx, + result: Err(err), + }); + } + Ok(DialResponse { + status: ResponseStatus::OK, + addr_idx: idx, + dial_status: DialStatus::OK, + }) +} diff --git a/protocols/autonat/tests/autonatv2.rs b/protocols/autonat/tests/autonatv2.rs new file mode 100644 index 00000000000..abd0c4bd8eb --- /dev/null +++ b/protocols/autonat/tests/autonatv2.rs @@ -0,0 +1,568 @@ +use libp2p_autonat::v2::client::{self, Config}; +use libp2p_autonat::v2::server; +use libp2p_core::transport::TransportError; +use libp2p_core::Multiaddr; +use libp2p_swarm::{ + DialError, FromSwarm, NetworkBehaviour, NewExternalAddrCandidate, Swarm, SwarmEvent, +}; +use libp2p_swarm_test::SwarmExt; +use rand_core::OsRng; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::oneshot; +use tracing_subscriber::EnvFilter; + +#[tokio::test] +async fn confirm_successful() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let (mut alice, mut bob) = start_and_connect().await; + + let cor_server_peer = *alice.local_peer_id(); + let cor_client_peer = *bob.local_peer_id(); + let bob_external_addrs = Arc::new(bob.external_addresses().cloned().collect::>()); + let alice_bob_external_addrs = bob_external_addrs.clone(); + + let alice_task = async { + let _ = alice + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), + _ => None, + }) + .await; + + let (dialed_peer_id, dialed_connection_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + .. + } => peer_id.map(|peer_id| (peer_id, connection_id)), + _ => None, + }) + .await; + + assert_eq!(dialed_peer_id, cor_client_peer); + + let _ = alice + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + peer_id, + connection_id, + .. + } if peer_id == dialed_peer_id + && peer_id == cor_client_peer + && connection_id == dialed_connection_id => + { + Some(()) + } + _ => None, + }) + .await; + + let server::Event { + all_addrs, + tested_addr, + client, + data_amount, + result, + } = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(status_update)) => { + Some(status_update) + } + _ => None, + }) + .await; + + assert_eq!(tested_addr, bob_external_addrs.first().cloned().unwrap()); + assert_eq!(data_amount, 0); + assert_eq!(client, cor_client_peer); + assert_eq!(&all_addrs[..], &bob_external_addrs[..]); + assert!(result.is_ok(), "Result: {result:?}"); + }; + + let bob_task = async { + bob.wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; + let incoming_conn_id = bob + .wait(|event| match event { + SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), + _ => None, + }) + .await; + + let _ = bob + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }) + .await; + + let client::Event { + tested_addr, + bytes_sent, + server, + result, + } = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(status_update)) => { + Some(status_update) + } + _ => None, + }) + .await; + assert_eq!( + tested_addr, + alice_bob_external_addrs.first().cloned().unwrap() + ); + assert_eq!(bytes_sent, 0); + assert_eq!(server, cor_server_peer); + assert!(result.is_ok(), "Result is {result:?}"); + }; + + tokio::join!(alice_task, bob_task); +} + +#[tokio::test] +async fn dial_back_to_unsupported_protocol() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let (mut alice, mut bob) = bootstrap().await; + + let alice_peer_id = *alice.local_peer_id(); + + let test_addr: Multiaddr = "/ip4/127.0.0.1/udp/1234/quic/webtransport".parse().unwrap(); + let bob_test_addr = test_addr.clone(); + bob.behaviour_mut() + .autonat + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &test_addr }, + )); + + let (bob_done_tx, bob_done_rx) = oneshot::channel(); + + let alice_task = async { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|e| (e, connection_id)), + _ => None, + }) + .await; + let mut outgoing_conn_error = alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::Transport(transport_errs), + } if connection_id == alice_conn_id && alice_dialing_peer == peer_id => { + Some(transport_errs) + } + _ => None, + }) + .await; + if let Some((multiaddr, TransportError::MultiaddrNotSupported(not_supported_addr))) = + outgoing_conn_error.pop() + { + assert_eq!( + multiaddr, + test_addr.clone().with_p2p(alice_dialing_peer).unwrap() + ); + assert_eq!(not_supported_addr, multiaddr,); + } else { + panic!("Peers are empty"); + } + assert_eq!(outgoing_conn_error.len(), 0); + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![test_addr.clone()] + && tested_addr == test_addr.clone() + && client == alice_dialing_peer => + { + Some(data_amount) + } + _ => None, + }) + .await; + + let handler = tokio::spawn(async move { + alice.loop_on_next().await; + }); + let _ = bob_done_rx.await; + handler.abort(); + data_amount + }; + + let bob_task = async { + let data_amount = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { + tested_addr, + bytes_sent, + server, + result: Err(_), + })) if server == alice_peer_id && tested_addr == bob_test_addr => Some(bytes_sent), + _ => None, + }) + .await; + bob_done_tx.send(()).unwrap(); + data_amount + }; + let (alice_amount, bob_amount) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_amount, bob_amount); +} + +#[tokio::test] +async fn dial_back_to_non_libp2p() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + let (mut alice, mut bob) = bootstrap().await; + let alice_peer_id = *alice.local_peer_id(); + + for addr_str in ["/ip4/169.150.247.38/tcp/32", "/ip6/::1/tcp/1000"] { + let addr: Multiaddr = addr_str.parse().unwrap(); + let bob_addr = addr.clone(); + bob.behaviour_mut() + .autonat + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + + let alice_task = async { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|p| (p, connection_id)), + _ => None, + }) + .await; + let mut outgoing_conn_error = alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::Transport(peers), + } if connection_id == alice_conn_id && peer_id == alice_dialing_peer => { + Some(peers) + } + _ => None, + }) + .await; + + if let Some((multiaddr, TransportError::Other(o))) = outgoing_conn_error.pop() { + assert_eq!( + multiaddr, + addr.clone().with_p2p(alice_dialing_peer).unwrap() + ); + let error_string = o.to_string(); + assert!( + error_string.contains("Connection refused"), + "Correct error string: {error_string} for {addr_str}" + ); + } else { + panic!("No outgoing connection errors"); + } + + alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![addr.clone()] + && tested_addr == addr + && alice_dialing_peer == client => + { + Some(data_amount) + } + _ => None, + }) + .await + }; + let bob_task = async { + bob.wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { + tested_addr, + bytes_sent, + server, + result: Err(_), + })) if tested_addr == bob_addr && server == alice_peer_id => Some(bytes_sent), + _ => None, + }) + .await + }; + + let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_bytes_sent, bob_bytes_sent); + bob.behaviour_mut().autonat.validate_addr(&addr); + } +} + +#[tokio::test] +async fn dial_back_to_not_supporting() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let (mut alice, mut bob) = bootstrap().await; + let alice_peer_id = *alice.local_peer_id(); + + let (bob_done_tx, bob_done_rx) = oneshot::channel(); + + let hannes = new_dummy().await; + let hannes_peer_id = *hannes.local_peer_id(); + let unreachable_address = hannes.external_addresses().next().unwrap().clone(); + let bob_unreachable_address = unreachable_address.clone(); + bob.behaviour_mut() + .autonat + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { + addr: &unreachable_address, + }, + )); + + let handler = tokio::spawn(async { hannes.loop_on_next().await }); + + let alice_task = async { + let (alice_dialing_peer, alice_conn_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + } => peer_id.map(|p| (p, connection_id)), + _ => None, + }) + .await; + alice + .wait(|event| match event { + SwarmEvent::OutgoingConnectionError { + connection_id, + peer_id: Some(peer_id), + error: DialError::WrongPeerId { obtained, .. }, + } if connection_id == alice_conn_id + && peer_id == alice_dialing_peer + && obtained == hannes_peer_id => + { + Some(()) + } + _ => None, + }) + .await; + + let data_amount = alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(server::Event { + all_addrs, + tested_addr, + client, + data_amount, + result: Ok(()), + })) if all_addrs == vec![unreachable_address.clone()] + && tested_addr == unreachable_address + && alice_dialing_peer == client => + { + Some(data_amount) + } + _ => None, + }) + .await; + tokio::select! { + _ = bob_done_rx => { + data_amount + } + _ = alice.loop_on_next() => { + unreachable!(); + } + } + }; + + let bob_task = async { + let bytes_sent = bob + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(client::Event { + tested_addr, + bytes_sent, + server, + result: Err(_), + })) if tested_addr == bob_unreachable_address && server == alice_peer_id => { + Some(bytes_sent) + } + _ => None, + }) + .await; + bob_done_tx.send(()).unwrap(); + bytes_sent + }; + + let (alice_bytes_sent, bob_bytes_sent) = tokio::join!(alice_task, bob_task); + assert_eq!(alice_bytes_sent, bob_bytes_sent); + handler.abort(); +} + +async fn new_server() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| CombinedServer { + autonat: libp2p_autonat::v2::server::Behaviour::default(), + identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )), + }); + node.listen().with_tcp_addr_external().await; + + node +} + +async fn new_client() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| CombinedClient { + autonat: libp2p_autonat::v2::client::Behaviour::new( + OsRng, + Config::default().with_probe_interval(Duration::from_millis(100)), + ), + identify: libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )), + }); + node.listen().with_tcp_addr_external().await; + node +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct CombinedServer { + autonat: libp2p_autonat::v2::server::Behaviour, + identify: libp2p_identify::Behaviour, +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct CombinedClient { + autonat: libp2p_autonat::v2::client::Behaviour, + identify: libp2p_identify::Behaviour, +} + +async fn new_dummy() -> Swarm { + let mut node = Swarm::new_ephemeral(|identity| { + libp2p_identify::Behaviour::new(libp2p_identify::Config::new( + "/libp2p-test/1.0.0".into(), + identity.public().clone(), + )) + }); + node.listen().with_tcp_addr_external().await; + node +} + +async fn start_and_connect() -> (Swarm, Swarm) { + let mut alice = new_server().await; + let mut bob = new_client().await; + + bob.connect(&mut alice).await; + (alice, bob) +} + +async fn bootstrap() -> (Swarm, Swarm) { + let (mut alice, mut bob) = start_and_connect().await; + + let cor_server_peer = *alice.local_peer_id(); + let cor_client_peer = *bob.local_peer_id(); + + let alice_task = async { + let _ = alice + .wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { .. } => Some(()), + _ => None, + }) + .await; + + let (dialed_peer_id, dialed_connection_id) = alice + .wait(|event| match event { + SwarmEvent::Dialing { + peer_id, + connection_id, + .. + } => peer_id.map(|peer_id| (peer_id, connection_id)), + _ => None, + }) + .await; + + let _ = alice + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + peer_id, + connection_id, + .. + } if peer_id == dialed_peer_id + && peer_id == cor_client_peer + && connection_id == dialed_connection_id => + { + Some(()) + } + _ => None, + }) + .await; + + alice + .wait(|event| match event { + SwarmEvent::Behaviour(CombinedServerEvent::Autonat(_)) => Some(()), + _ => None, + }) + .await; + }; + + let bob_task = async { + bob.wait(|event| match event { + SwarmEvent::NewExternalAddrCandidate { address } => Some(address), + _ => None, + }) + .await; + let incoming_conn_id = bob + .wait(|event| match event { + SwarmEvent::IncomingConnection { connection_id, .. } => Some(connection_id), + _ => None, + }) + .await; + + let _ = bob + .wait(|event| match event { + SwarmEvent::ConnectionEstablished { + connection_id, + peer_id, + .. + } if incoming_conn_id == connection_id && peer_id == cor_server_peer => Some(()), + _ => None, + }) + .await; + + bob.wait(|event| match event { + SwarmEvent::Behaviour(CombinedClientEvent::Autonat(_)) => Some(()), + _ => None, + }) + .await; + }; + + tokio::join!(alice_task, bob_task); + (alice, bob) +} diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 7509d3ef425..2b1460cac85 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -17,6 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#![allow(deprecated)] use async_std::task::JoinHandle; use libp2p_autonat::{ diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index fd97b1a9132..a01c5901b8e 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -17,6 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#![allow(deprecated)] use libp2p_autonat::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, From cba4113c2ecb38bf9a17233b38ab8b53609b38c5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:02:33 +0900 Subject: [PATCH 159/179] Fix clippy issues --- Cargo.lock | 210 +++++++++++++++--- Cargo.toml | 1 + .../src/v2/server/handler/dial_request.rs | 2 +- 3 files changed, 187 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5d45569a57..1ccddbfb3d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -556,6 +556,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "autonatv2" +version = "0.1.0" +dependencies = [ + "cfg-if", + "clap", + "libp2p", + "opentelemetry 0.21.0", + "opentelemetry-jaeger", + "opentelemetry_sdk 0.21.2", + "rand 0.8.5", + "tokio", + "tracing", + "tracing-opentelemetry 0.22.0", + "tracing-subscriber", +] + [[package]] name = "axum" version = "0.6.20" @@ -1141,6 +1158,15 @@ dependencies = [ "itertools 0.10.5", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -2558,6 +2584,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + [[package]] name = "interceptor" version = "0.10.0" @@ -2608,7 +2640,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "wasm-logger", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -2901,7 +2933,7 @@ dependencies = [ "tracing", "unsigned-varint 0.8.0", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -2935,7 +2967,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3010,7 +3042,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3098,7 +3130,7 @@ dependencies = [ "tracing-subscriber", "uint", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3161,7 +3193,7 @@ dependencies = [ "libp2p-swarm", "pin-project", "prometheus-client", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3253,7 +3285,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3273,7 +3305,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3370,7 +3402,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3400,7 +3432,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3429,7 +3461,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3499,7 +3531,7 @@ dependencies = [ "trybuild", "void", "wasm-bindgen-futures", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -3924,13 +3956,13 @@ dependencies = [ "axum 0.7.5", "futures", "libp2p", - "opentelemetry", + "opentelemetry 0.23.0", "opentelemetry-otlp", - "opentelemetry_sdk", + "opentelemetry_sdk 0.23.0", "prometheus-client", "tokio", "tracing", - "tracing-opentelemetry", + "tracing-opentelemetry 0.24.0", "tracing-subscriber", ] @@ -4314,6 +4346,22 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.2.6", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + [[package]] name = "opentelemetry" version = "0.23.0" @@ -4328,6 +4376,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "opentelemetry-jaeger" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1" +dependencies = [ + "async-trait", + "futures-core", + "futures-util", + "opentelemetry 0.21.0", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk 0.21.2", + "thrift", + "tokio", +] + [[package]] name = "opentelemetry-otlp" version = "0.16.0" @@ -4337,9 +4401,9 @@ dependencies = [ "async-trait", "futures-core", "http 0.2.12", - "opentelemetry", + "opentelemetry 0.23.0", "opentelemetry-proto", - "opentelemetry_sdk", + "opentelemetry_sdk 0.23.0", "prost", "thiserror", "tokio", @@ -4352,12 +4416,43 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "984806e6cf27f2b49282e2a05e288f30594f3dbc74eb7a6e99422bc48ed78162" dependencies = [ - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.23.0", + "opentelemetry_sdk 0.23.0", "prost", "tonic", ] +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" +dependencies = [ + "opentelemetry 0.21.0", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry 0.21.0", + "ordered-float 4.2.0", + "percent-encoding", + "rand 0.8.5", + "thiserror", + "tokio", + "tokio-stream", +] + [[package]] name = "opentelemetry_sdk" version = "0.23.0" @@ -4371,8 +4466,8 @@ dependencies = [ "glob", "lazy_static", "once_cell", - "opentelemetry", - "ordered-float", + "opentelemetry 0.23.0", + "ordered-float 4.2.0", "percent-encoding", "rand 0.8.5", "thiserror", @@ -4386,6 +4481,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-float" version = "4.2.0" @@ -6105,6 +6209,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float 2.10.1", + "threadpool", +] + [[package]] name = "time" version = "0.3.36" @@ -6419,6 +6545,24 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry 0.21.0", + "opentelemetry_sdk 0.21.2", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time 0.2.4", +] + [[package]] name = "tracing-opentelemetry" version = "0.24.0" @@ -6427,14 +6571,14 @@ checksum = "f68803492bf28ab40aeccaecc7021096bd256baf7ca77c3d425d89b35a7be4e4" dependencies = [ "js-sys", "once_cell", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.23.0", + "opentelemetry_sdk 0.23.0", "smallvec", "tracing", "tracing-core", "tracing-log", "tracing-subscriber", - "web-time", + "web-time 1.1.0", ] [[package]] @@ -6619,6 +6763,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -6834,6 +6984,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web-time" version = "1.1.0" @@ -7408,7 +7568,7 @@ dependencies = [ "pin-project", "rand 0.8.5", "static_assertions", - "web-time", + "web-time 1.1.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 510309b7850..90c1e7f1c80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "core", "examples/autonat", + "examples/autonatv2", "examples/browser-webrtc", "examples/chat", "examples/dcutr", diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index f8c3f6b654d..9a3729d4ccf 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -275,7 +275,7 @@ where return Err(HandleFail::RequestRejected); } }; - *all_addrs = addrs.clone(); + all_addrs.clone_from(&addrs); let idx = 0; let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; *tested_addrs = Some(addr.clone()); From 6a632b53022156dffb8b52605259f2073b070a53 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:34:43 +0900 Subject: [PATCH 160/179] Don't format --- libp2p/CHANGELOG.md | 1845 +++++++++++++++++++------------------------ 1 file changed, 819 insertions(+), 1026 deletions(-) diff --git a/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md index 22d2bce439a..95ede75c1eb 100644 --- a/libp2p/CHANGELOG.md +++ b/libp2p/CHANGELOG.md @@ -1,69 +1,67 @@ ## 0.54.0 -- Update individual crates. +- Update individual crates. + - Update to [`libp2p-kad` `v0.46.0`](protocols/kad/CHANGELOG.md#0460). - - Update to [`libp2p-kad` `v0.46.0`](protocols/kad/CHANGELOG.md#0460). +- Raise MSRV to 1.73. + See [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). -- Raise MSRV to 1.73. See - [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). +- Implement refactored `Transport`. + See [PR 4568]. -- Implement refactored `Transport`. See [PR 4568]. +- Move `address_translation` from `libp2p-core` to `libp2p-swarm` and `libp2p-identify`. To now get + address translation behaviour, i.e. discovery of extern address (candidates) based on connecting + to other peers, one needs to use `libp2p-identify` now. This pertains to you if your nodes can be + behind NATs and they aren't aware of their true external address. + See [PR 4568]. -- Move `address_translation` from `libp2p-core` to `libp2p-swarm` and - `libp2p-identify`. To now get address translation behaviour, i.e. discovery of - extern address (candidates) based on connecting to other peers, one needs to - use `libp2p-identify` now. This pertains to you if your nodes can be behind - NATs and they aren't aware of their true external address. See [PR 4568]. - -- Use `web-time` instead of `instant`. See - [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). +- Use `web-time` instead of `instant`. + See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). [PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 ## 0.53.2 -- Allow `SwarmBuilder::with_bandwidth_metrics` after - `SwarmBuilder::with_websocket`. See - [PR 4937](https://github.com/libp2p/rust-libp2p/pull/4937). +- Allow `SwarmBuilder::with_bandwidth_metrics` after `SwarmBuilder::with_websocket`. + See [PR 4937](https://github.com/libp2p/rust-libp2p/pull/4937). ## 0.53.1 -- Allow `SwarmBuilder::with_quic_config` to be called without `with_tcp` first. - See [PR 4821](https://github.com/libp2p/rust-libp2p/pull/4821). -- Introduce `SwarmBuilder::with_dns_config`. See - [PR 4808](https://github.com/libp2p/rust-libp2p/pull/4808). +- Allow `SwarmBuilder::with_quic_config` to be called without `with_tcp` first. + See [PR 4821](https://github.com/libp2p/rust-libp2p/pull/4821). +- Introduce `SwarmBuilder::with_dns_config`. + See [PR 4808](https://github.com/libp2p/rust-libp2p/pull/4808). ## 0.53.0 -- Raise MSRV to 1.73. See - [PR 4692](https://github.com/libp2p/rust-libp2p/pull/4692). -- Remove deprecated `libp2p-wasm-ext`. Users should use - `libp2p-websocket-websys` instead. See - [PR 4694](https://github.com/libp2p/rust-libp2p/pull/4694). -- Remove deprecated `libp2p-deflate`. See - [issue 4522](https://github.com/libp2p/rust-libp2p/issues/4522) for details. - See [PR 4729](https://github.com/libp2p/rust-libp2p/pull/4729). -- Remove deprecated `development_transport`. Use `libp2p::SwarmBuilder` instead. - See [PR 4732](https://github.com/libp2p/rust-libp2p/pull/4732). -- Introduce `SwarmBuilder::with_bandwidth_metrics` exposing Prometheus bandwidth - metrics per transport protocol stack and direction (in-/ outbound). Deprecate - `Transport::with_bandwidth_logging` and `SwarmBuilder::with_bandwidth_logging` - in favor of the new `SwarmBuilder::with_bandwidth_metrics`. See - [PR 4727](https://github.com/libp2p/rust-libp2p/pull/4727). +- Raise MSRV to 1.73. + See [PR 4692](https://github.com/libp2p/rust-libp2p/pull/4692). +- Remove deprecated `libp2p-wasm-ext`. + Users should use `libp2p-websocket-websys` instead. + See [PR 4694](https://github.com/libp2p/rust-libp2p/pull/4694). +- Remove deprecated `libp2p-deflate`. + See [issue 4522](https://github.com/libp2p/rust-libp2p/issues/4522) for details. + See [PR 4729](https://github.com/libp2p/rust-libp2p/pull/4729). +- Remove deprecated `development_transport`. + Use `libp2p::SwarmBuilder` instead. + See [PR 4732](https://github.com/libp2p/rust-libp2p/pull/4732). +- Introduce `SwarmBuilder::with_bandwidth_metrics` exposing Prometheus bandwidth metrics per transport protocol stack and direction (in-/ outbound). + Deprecate `Transport::with_bandwidth_logging` and `SwarmBuilder::with_bandwidth_logging` in favor of the new `SwarmBuilder::with_bandwidth_metrics`. + See [PR 4727](https://github.com/libp2p/rust-libp2p/pull/4727). ## 0.52.4 -- Introduce `libp2p::websocket_websys` module behind `websocket-websys` feature - flag. This supersedes the existing `libp2p::wasm_ext` module which is now - deprecated. See [PR 3679]. +- Introduce `libp2p::websocket_websys` module behind `websocket-websys` feature flag. + This supersedes the existing `libp2p::wasm_ext` module which is now deprecated. + See [PR 3679]. -- Introduce a new `libp2p::SwarmBuilder` in favor of the now deprecated - `libp2p::swarm::SwarmBuilder`. See `libp2p::SwarmBuilder` docs on how to use - the new builder. Also see [PR 4120]. +- Introduce a new `libp2p::SwarmBuilder` in favor of the now deprecated `libp2p::swarm::SwarmBuilder`. + See `libp2p::SwarmBuilder` docs on how to use the new builder. + Also see [PR 4120]. -- Update `libp2p-identity` version to 0.2.6. Under the hood, we feature-flagged - `libp2p-identity`'s `rand` dependency but it is enabled by default when using - `libp2p`. See [PR 4349]. +- Update `libp2p-identity` version to 0.2.6. + Under the hood, we feature-flagged `libp2p-identity`'s `rand` dependency but it is enabled by default when using `libp2p`. + See [PR 4349]. [PR 3679]: https://github.com/libp2p/rust-libp2p/pull/3679 [PR 4120]: https://github.com/libp2p/rust-libp2p/pull/4120 @@ -71,18 +69,21 @@ ## 0.52.3 -- Add `libp2p-quic` stable release. +- Add `libp2p-quic` stable release. ## 0.52.2 -- Include gossipsub when compiling for wasm. See [PR 4217]. +- Include gossipsub when compiling for wasm. + See [PR 4217]. -- Add `json` feature which exposes `request_response::json`. See [PR 4188]. +- Add `json` feature which exposes `request_response::json`. + See [PR 4188]. -- Add support for UPnP via the IGD protocol. See [PR 4156]. +- Add support for UPnP via the IGD protocol. + See [PR 4156]. -- Add `libp2p-memory-connection-limits` providing memory usage based connection - limit configurations. See [PR 4281]. +- Add `libp2p-memory-connection-limits` providing memory usage based connection limit configurations. + See [PR 4281]. [PR 4188]: https://github.com/libp2p/rust-libp2p/pull/4188 [PR 4156]: https://github.com/libp2p/rust-libp2p/pull/4156 @@ -91,37 +92,36 @@ ## 0.52.1 -- Add `libp2p-webtransport-websys` providing WebTransport for WASM environments. - See [PR 4015]. +- Add `libp2p-webtransport-websys` providing WebTransport for WASM environments. + See [PR 4015]. [PR 4015]: https://github.com/libp2p/rust-libp2p/pull/4015 ## 0.52.0 -- Raise MSRV to 1.65. See [PR 3715]. +- Raise MSRV to 1.65. + See [PR 3715]. -- Protocol names are now required to be valid UTF8 strings. We delete the - `ProtocolName` trait from `libp2p::core` and replace it with a requirement for - `AsRef`. At the same time, we introduce `StreamProtocol`, a newtype in - `libp2p::swarm`. This newtype enforces additional variants like a leading - forward-slash. We encourage users to use `StreamProtocol` when implementing - `UpgradeInfo`. See [PR 3746]. +- Protocol names are now required to be valid UTF8 strings. + We delete the `ProtocolName` trait from `libp2p::core` and replace it with a requirement for `AsRef`. + At the same time, we introduce `StreamProtocol`, a newtype in `libp2p::swarm`. + This newtype enforces additional variants like a leading forward-slash. + We encourage users to use `StreamProtocol` when implementing `UpgradeInfo`. + See [PR 3746]. -- Rename `NetworkBehaviour::OutEvent` to `NetworkBehaviour::ToSwarm`, - `ConnectionHandler::InEvent` to `ConnectionHandler::FromBehaviour`, - `ConnectionHandler::OutEvent` to `ConnectionHandler::ToBehaviour`. See - [PR 3848]. +- Rename `NetworkBehaviour::OutEvent` to `NetworkBehaviour::ToSwarm`, `ConnectionHandler::InEvent` to `ConnectionHandler::FromBehaviour`, `ConnectionHandler::OutEvent` to `ConnectionHandler::ToBehaviour`. See [PR 3848]. -- Remove deprecated `mplex` module. You can still depend on `libp2p-mplex` - directly but we strongly encourage to migrate to `yamux`. This also removes - `mplex` from the `development_transport` and `tokio_development_transport` - functions. See [PR 3920]. +- Remove deprecated `mplex` module. + You can still depend on `libp2p-mplex` directly but we strongly encourage to migrate to `yamux`. + This also removes `mplex` from the `development_transport` and `tokio_development_transport` functions. + See [PR 3920]. -- Remove `libp2p-perf` protocol. To use `libp2p-perf` one needs to import it - directly. See [PR 3990]. +- Remove `libp2p-perf` protocol. To use `libp2p-perf` one needs to import it directly. + See [PR 3990]. -- Remove `libp2p-quic` and `libp2p-webrtc` protocols. These are in alpha status - and should be depended on directly. See [PR 4041]. +- Remove `libp2p-quic` and `libp2p-webrtc` protocols. + These are in alpha status and should be depended on directly. + See [PR 4041]. [PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715 [PR 3746]: https://github.com/libp2p/rust-libp2p/pull/3746 @@ -132,23 +132,27 @@ ## 0.51.3 -- Deprecate the `mplex` feature. The recommended baseline stream multiplexer is - `yamux`. See [PR 3689]. +- Deprecate the `mplex` feature. + The recommended baseline stream multiplexer is `yamux`. + See [PR 3689]. [PR 3689]: https://github.com/libp2p/rust-libp2p/pull/3689 ## 0.51.2 -- Introduce `libp2p::connection_limits` module. See [PR 3386]. +- Introduce `libp2p::connection_limits` module. + See [PR 3386]. -- Deprecate the `quic` and `webrtc` feature. These two crates are only in alpha - state. To properly communicate this to users, we want them to add the - dependency directly which makes the `alpha` version visible. See [PR 3580]. +- Deprecate the `quic` and `webrtc` feature. + These two crates are only in alpha state. + To properly communicate this to users, we want them to add the dependency directly which makes the `alpha` version visible. + See [PR 3580]. -- Introduce `libp2p::allow_block_list` module and deprecate - `libp2p::Swarm::ban_peer_id`. See [PR 3590]. +- Introduce `libp2p::allow_block_list` module and deprecate `libp2p::Swarm::ban_peer_id`. + See [PR 3590]. -- Introduce `libp2p::perf` module. See [PR 3693]. +- Introduce `libp2p::perf` module. + See [PR 3693]. [PR 3386]: https://github.com/libp2p/rust-libp2p/pull/3386 [PR 3580]: https://github.com/libp2p/rust-libp2p/pull/3580 @@ -157,147 +161,114 @@ ## 0.51.1 -- Depend on `libp2p-tls` `v0.1.0`. +- Depend on `libp2p-tls` `v0.1.0`. -- Introduce `ed25519` feature. For backwards-compatibility, the `ed25519` - identity keys are still available without activating this feature. However, - going forward, you should explicitly activate it to avoid compile errors going - forward. See [PR 3350]. +- Introduce `ed25519` feature. + For backwards-compatibility, the `ed25519` identity keys are still available without activating this feature. + However, going forward, you should explicitly activate it to avoid compile errors going forward. + See [PR 3350]. [PR 3350]: https://github.com/libp2p/rust-libp2p/pull/3350 ## 0.51.0 -- Enable `NetworkBehaviour`s to manage connections. This deprecates - `NetworkBehaviour::new_handler` and `NetworkBehaviour::addresses_of_peer`. Due - to limitations in the Rust compiler, these deprecations may not show up for - you, nevertheless they will be removed in a future release. See - [`libp2p-swarm`'s CHANGELOG](swarm/CHANGELOG.md#0420) for details. - -- Count bandwidth at the application level. Previously `BandwidthLogging` would - implement `Transport` and now implements `StreamMuxer` - ([PR 3180](https://github.com/libp2p/rust-libp2p/pull/3180)). - - `BandwidthLogging::new` now requires a 2nd argument: `Arc` - - Remove `BandwidthFuture` - - Rename `BandwidthConnecLogging` to `InstrumentedStream` -- Remove `SimpleProtocol` due to being unused. See - [`libp2p::core::upgrade`](https://docs.rs/libp2p/0.50.0/libp2p/core/upgrade/index.html) - for alternatives. See [PR 3191]. +- Enable `NetworkBehaviour`s to manage connections. + This deprecates `NetworkBehaviour::new_handler` and `NetworkBehaviour::addresses_of_peer`. + Due to limitations in the Rust compiler, these deprecations may not show up for you, nevertheless they will be removed in a future release. + See [`libp2p-swarm`'s CHANGELOG](swarm/CHANGELOG.md#0420) for details. -- Bump MSRV to 1.65.0. +- Count bandwidth at the application level. Previously `BandwidthLogging` would implement `Transport` and now implements `StreamMuxer` ([PR 3180](https://github.com/libp2p/rust-libp2p/pull/3180)). + - `BandwidthLogging::new` now requires a 2nd argument: `Arc` + - Remove `BandwidthFuture` + - Rename `BandwidthConnecLogging` to `InstrumentedStream` +- Remove `SimpleProtocol` due to being unused. See [`libp2p::core::upgrade`](https://docs.rs/libp2p/0.50.0/libp2p/core/upgrade/index.html) for alternatives. See [PR 3191]. -- Update individual crates. +- Bump MSRV to 1.65.0. - - Update to [`libp2p-dcutr` `v0.9.0`](protocols/dcutr/CHANGELOG.md#090). +- Update individual crates. + - Update to [`libp2p-dcutr` `v0.9.0`](protocols/dcutr/CHANGELOG.md#090). - - Update to [`libp2p-ping` `v0.42.0`](protocols/ping/CHANGELOG.md#0420). + - Update to [`libp2p-ping` `v0.42.0`](protocols/ping/CHANGELOG.md#0420). - - Update to - [`libp2p-request-response` `v0.24.0`](protocols/request-response/CHANGELOG.md#0240). + - Update to [`libp2p-request-response` `v0.24.0`](protocols/request-response/CHANGELOG.md#0240). - - Update to [`libp2p-kad` `v0.43.0`](protocols/kad/CHANGELOG.md#0430). + - Update to [`libp2p-kad` `v0.43.0`](protocols/kad/CHANGELOG.md#0430). - - Update to - [`libp2p-floodsub` `v0.42.0`](protocols/floodsub/CHANGELOG.md#0420). + - Update to [`libp2p-floodsub` `v0.42.0`](protocols/floodsub/CHANGELOG.md#0420). - - Update to [`libp2p-autonat` `v0.10.0`](protocols/autonat/CHANGELOG.md#0100). + - Update to [`libp2p-autonat` `v0.10.0`](protocols/autonat/CHANGELOG.md#0100). - - Update to [`libp2p-relay` `v0.15.0`](protocols/relay/CHANGELOG.md#0150). + - Update to [`libp2p-relay` `v0.15.0`](protocols/relay/CHANGELOG.md#0150). - - Update to - [`libp2p-identify` `v0.42.0`](protocols/identify/CHANGELOG.md#0420). + - Update to [`libp2p-identify` `v0.42.0`](protocols/identify/CHANGELOG.md#0420). - - Update to - [`libp2p-rendezvous` `v0.12.0`](protocols/rendezvous/CHANGELOG.md#0120). + - Update to [`libp2p-rendezvous` `v0.12.0`](protocols/rendezvous/CHANGELOG.md#0120). - - Update to [`libp2p-metrics` `v0.12.0`](misc/metrics/CHANGELOG.md#0120). + - Update to [`libp2p-metrics` `v0.12.0`](misc/metrics/CHANGELOG.md#0120). - - Update to [`libp2p-swarm` `v0.42.0`](swarm/CHANGELOG.md#0420). + - Update to [`libp2p-swarm` `v0.42.0`](swarm/CHANGELOG.md#0420). - - Update to [`libp2p-mdns` `v0.43.0`](protocols/mdns/CHANGELOG.md#0430). + - Update to [`libp2p-mdns` `v0.43.0`](protocols/mdns/CHANGELOG.md#0430). - - Update to - [`libp2p-gossipsub` `v0.44.0`](protocols/gossipsub/CHANGELOG.md#0440). + - Update to [`libp2p-gossipsub` `v0.44.0`](protocols/gossipsub/CHANGELOG.md#0440). - - Update to [`libp2p-yamux` `v0.43.0`](muxers/yamux/CHANGELOG.md#0430). + - Update to [`libp2p-yamux` `v0.43.0`](muxers/yamux/CHANGELOG.md#0430). - - Update to [`libp2p-mplex` `v0.39.0`](muxers/mplex/CHANGELOG.md#0390). + - Update to [`libp2p-mplex` `v0.39.0`](muxers/mplex/CHANGELOG.md#0390). - - Update to - [`libp2p-wasm-ext` `v0.39.0`](transports/wasm-ext/CHANGELOG.md#0390). + - Update to [`libp2p-wasm-ext` `v0.39.0`](transports/wasm-ext/CHANGELOG.md#0390). - - Update to - [`libp2p-plaintext` `v0.39.0`](transports/plaintext/CHANGELOG.md#0390). + - Update to [`libp2p-plaintext` `v0.39.0`](transports/plaintext/CHANGELOG.md#0390). - - Update to [`libp2p-noise` `v0.42.0`](transports/noise/CHANGELOG.md#0420). + - Update to [`libp2p-noise` `v0.42.0`](transports/noise/CHANGELOG.md#0420). - - Update to [`libp2p-core` `v0.39.0`](core/CHANGELOG.md#0390). + - Update to [`libp2p-core` `v0.39.0`](core/CHANGELOG.md#0390). [PR 3191]: https://github.com/libp2p/rust-libp2p/pull/3191 ## 0.50.0 -This is a large release. After > 4 years, rust-libp2p ships with an -[(alpha) QUIC implementation](transports/quic/CHANGELOG.md#070-alpha). The -[necessary TLS logic is extracted into -its own crate](transports/tls/CHANGELOG.md#010-alpha), and can thus be used -detached from QUIC, e.g. on top of TCP as an alternative to Noise. In addition -to these two transports, this release adds a third, namely -[WebRTC (browser-to-server)](transports/webrtc/CHANGELOG.md#040-alpha). But that -is definitely not it. See below for the many other changes packed into this -release. - -- Introduce - [`libp2p-tls` `v0.1.0-alpha`](transports/tls/CHANGELOG.md#010-alpha). See - [PR 2945]. -- Introduce - [`libp2p-quic` `v0.7.0-alpha`](transports/quic/CHANGELOG.md#070-alpha). See - [PR 2289]. -- Introduce - [`libp2p-webrtc` `v0.4.0-alpha`](transports/webrtc/CHANGELOG.md#040-alpha). - See [PR 2289]. -- Remove deprecated features: `tcp-tokio`, `mdns-tokio`, `dns-tokio`, - `tcp-async-io`, `mdns-async-io`, `dns-async-std`. See [PR 3001]. -- Remove `NetworkBehaviour` macro export from root crate in favor of re-exported - macro from `libp2p::swarm`. Change your import from `libp2p::NetworkBehaviour` - to `libp2p::swarm::NetworkBehaviour`. See [PR 3055]. -- Feature-gate `NetworkBehaviour` macro behind `macros` feature flag. See - [PR 3055]. -- Update individual crates. - - Update to [`libp2p-autonat` `v0.89.0`](protocols/autonat/CHANGELOG.md#090). - - Update to [`libp2p-core` `v0.38.0`](core/CHANGELOG.md#0380). - - Update to [`libp2p-dcutr` `v0.8.0`](protocols/dcutr/CHANGELOG.md#080). - - Update to - [`libp2p-deflate` `v0.38.0`](transports/deflate/CHANGELOG.md#0380). - - Update to [`libp2p-dns` `v0.38.0`](transports/dns/CHANGELOG.md#0380). - - Update to - [`libp2p-floodsub` `v0.41.0`](protocols/floodsub/CHANGELOG.md#0410). - - Update to - [`libp2p-gossipsub` `v0.43.0`](protocols/gossipsub/CHANGELOG.md#0430). - - Update to - [`libp2p-identify` `v0.41.0`](protocols/identify/CHANGELOG.md#0410). - - Update to [`libp2p-kad` `v0.42.0`](protocols/kad/CHANGELOG.md#0420). - - Update to [`libp2p-mdns` `v0.42.0`](protocols/mdns/CHANGELOG.md#0420). - - Update to [`libp2p-metrics` `v0.11.0`](misc/metrics/CHANGELOG.md#0110). - - Update to [`libp2p-mplex` `v0.38.0`](muxers/mplex/CHANGELOG.md#0380). - - Update to [`libp2p-noise` `v0.41.0`](transports/noise/CHANGELOG.md#0410). - - Update to [`libp2p-ping` `v0.41.0`](protocols/ping/CHANGELOG.md#0410). - - Update to - [`libp2p-plaintext` `v0.38.0`](transports/plaintext/CHANGELOG.md#0380). - - Update to [`libp2p-pnet` `v0.22.2`](transports/pnet/CHANGELOG.md#0222). - - Update to [`libp2p-relay` `v0.14.0`](protocols/relay/CHANGELOG.md#0140). - - Update to - [`libp2p-rendezvous` `v0.11.0`](protocols/rendezovus/CHANGELOG.md#0110). - - Update to - [`libp2p-request-response` `v0.23.0`](protocols/request-response/CHANGELOG.md#0230). - - Update to [`libp2p-swarm` `v0.41.0`](swarm/CHANGELOG.md#0410). - - Update to [`libp2p-tcp` `v0.38.0`](transports/tcp/CHANGELOG.md#0380). - - Update to [`libp2p-uds` `v0.37.0`](transports/uds/CHANGELOG.md#0370). - - Update to - [`libp2p-wasm-ext` `v0.38.0`](transports/wasm-ext/CHANGELOG.md#0380). - - Update to - [`libp2p-websocket` `v0.40.0`](transports/websocket/CHANGELOG.md#0400). - - Update to [`libp2p-yamux` `v0.42.0`](muxers/yamux/CHANGELOG.md#0420). +This is a large release. After > 4 years, rust-libp2p ships with an [(alpha) QUIC +implementation](transports/quic/CHANGELOG.md#070-alpha). The [necessary TLS logic is extracted into +its own crate](transports/tls/CHANGELOG.md#010-alpha), and can thus be used detached from QUIC, e.g. +on top of TCP as an alternative to Noise. In addition to these two transports, this release adds +a third, namely [WebRTC (browser-to-server)](transports/webrtc/CHANGELOG.md#040-alpha). But that is +definitely not it. See below for the many other changes packed into this release. + +- Introduce [`libp2p-tls` `v0.1.0-alpha`](transports/tls/CHANGELOG.md#010-alpha). See [PR 2945]. +- Introduce [`libp2p-quic` `v0.7.0-alpha`](transports/quic/CHANGELOG.md#070-alpha). See [PR 2289]. +- Introduce [`libp2p-webrtc` `v0.4.0-alpha`](transports/webrtc/CHANGELOG.md#040-alpha). See [PR 2289]. +- Remove deprecated features: `tcp-tokio`, `mdns-tokio`, `dns-tokio`, `tcp-async-io`, `mdns-async-io`, `dns-async-std`. + See [PR 3001]. +- Remove `NetworkBehaviour` macro export from root crate in favor of re-exported macro from `libp2p::swarm`. + Change your import from `libp2p::NetworkBehaviour` to `libp2p::swarm::NetworkBehaviour`. See [PR 3055]. +- Feature-gate `NetworkBehaviour` macro behind `macros` feature flag. See [PR 3055]. +- Update individual crates. + - Update to [`libp2p-autonat` `v0.89.0`](protocols/autonat/CHANGELOG.md#090). + - Update to [`libp2p-core` `v0.38.0`](core/CHANGELOG.md#0380). + - Update to [`libp2p-dcutr` `v0.8.0`](protocols/dcutr/CHANGELOG.md#080). + - Update to [`libp2p-deflate` `v0.38.0`](transports/deflate/CHANGELOG.md#0380). + - Update to [`libp2p-dns` `v0.38.0`](transports/dns/CHANGELOG.md#0380). + - Update to [`libp2p-floodsub` `v0.41.0`](protocols/floodsub/CHANGELOG.md#0410). + - Update to [`libp2p-gossipsub` `v0.43.0`](protocols/gossipsub/CHANGELOG.md#0430). + - Update to [`libp2p-identify` `v0.41.0`](protocols/identify/CHANGELOG.md#0410). + - Update to [`libp2p-kad` `v0.42.0`](protocols/kad/CHANGELOG.md#0420). + - Update to [`libp2p-mdns` `v0.42.0`](protocols/mdns/CHANGELOG.md#0420). + - Update to [`libp2p-metrics` `v0.11.0`](misc/metrics/CHANGELOG.md#0110). + - Update to [`libp2p-mplex` `v0.38.0`](muxers/mplex/CHANGELOG.md#0380). + - Update to [`libp2p-noise` `v0.41.0`](transports/noise/CHANGELOG.md#0410). + - Update to [`libp2p-ping` `v0.41.0`](protocols/ping/CHANGELOG.md#0410). + - Update to [`libp2p-plaintext` `v0.38.0`](transports/plaintext/CHANGELOG.md#0380). + - Update to [`libp2p-pnet` `v0.22.2`](transports/pnet/CHANGELOG.md#0222). + - Update to [`libp2p-relay` `v0.14.0`](protocols/relay/CHANGELOG.md#0140). + - Update to [`libp2p-rendezvous` `v0.11.0`](protocols/rendezovus/CHANGELOG.md#0110). + - Update to [`libp2p-request-response` `v0.23.0`](protocols/request-response/CHANGELOG.md#0230). + - Update to [`libp2p-swarm` `v0.41.0`](swarm/CHANGELOG.md#0410). + - Update to [`libp2p-tcp` `v0.38.0`](transports/tcp/CHANGELOG.md#0380). + - Update to [`libp2p-uds` `v0.37.0`](transports/uds/CHANGELOG.md#0370). + - Update to [`libp2p-wasm-ext` `v0.38.0`](transports/wasm-ext/CHANGELOG.md#0380). + - Update to [`libp2p-websocket` `v0.40.0`](transports/websocket/CHANGELOG.md#0400). + - Update to [`libp2p-yamux` `v0.42.0`](muxers/yamux/CHANGELOG.md#0420). [PR 2945]: https://github.com/libp2p/rust-libp2p/pull/2945 [PR 3001]: https://github.com/libp2p/rust-libp2p/pull/3001 @@ -307,1117 +278,939 @@ release. ## 0.49.0 -- Remove default features. You need to enable required features explicitly now. - As a quick workaround, you may want to use the new `full` feature which - activates all features. See [PR 2918]. - -- Introduce `tokio` and `async-std` features and deprecate the following ones: - - - `tcp-tokio` in favor of `tcp` + `tokio` - - `mdns-tokio` in favor of `mdns` + `tokio` - - `dns-tokio` in favor of `dns` + `tokio` - - `tcp-async-io` in favor of `tcp` + `async-std` - - `mdns-async-io` in favor of `mdns` + `async-std` - - `dns-async-std` in favor of `dns` + `async-std` - - See [PR 2962]. - -- Update individual crates. - - Update to [`libp2p-autonat` `v0.8.0`](protocols/autonat/CHANGELOG.md#0080). - - Update to [`libp2p-core` `v0.37.0`](core/CHANGELOG.md#0370). - - Update to [`libp2p-dcutr` `v0.7.0`](protocols/dcutr/CHANGELOG.md#0070). - - Update to - [`libp2p-deflate` `v0.37.0`](transports/deflate/CHANGELOG.md#0370). - - Update to [`libp2p-dns` `v0.37.0`](transports/dns/CHANGELOG.md#0370). - - Update to - [`libp2p-floodsub` `v0.40.0`](protocols/floodsub/CHANGELOG.md#0400). - - Update to - [`libp2p-gossipsub` `v0.42.0`](protocols/gossipsub/CHANGELOG.md#0420). - - Update to - [`libp2p-identify` `v0.40.0`](protocols/identify/CHANGELOG.md#0400). - - Update to [`libp2p-kad` `v0.41.0`](protocols/kad/CHANGELOG.md#0410). - - Update to [`libp2p-mdns` `v0.41.0`](protocols/mdns/CHANGELOG.md#0410). - - Update to [`libp2p-metrics` `v0.10.0`](misc/metrics/CHANGELOG.md#0100). - - Update to [`libp2p-mplex` `v0.37.0`](muxers/mplex/CHANGELOG.md#0370). - - Update to [`libp2p-noise` `v0.40.0`](transports/noise/CHANGELOG.md#0400). - - Update to [`libp2p-ping` `v0.40.0`](protocols/ping/CHANGELOG.md#0400). - - Update to - [`libp2p-plaintext` `v0.37.0`](transports/plaintext/CHANGELOG.md#0370). - - Update to [`libp2p-relay` `v0.13.0`](protocols/relay/CHANGELOG.md#0130). - - Update to - [`libp2p-rendezvous` `v0.10.0`](protocols/rendezovus/CHANGELOG.md#0100). - - Update to - [`libp2p-request-response` `v0.22.0`](protocols/request-response/CHANGELOG.md#0220). - - Update to [`libp2p-swarm-derive` `v0.30.1`](swarm-derive/CHANGELOG.md#0301). - - Update to [`libp2p-swarm` `v0.40.0`](swarm/CHANGELOG.md#0400). - - Update to [`libp2p-tcp` `v0.37.0`](transports/tcp/CHANGELOG.md#0370). - - Update to [`libp2p-uds` `v0.36.0`](transports/uds/CHANGELOG.md#0360). - - Update to - [`libp2p-wasm-ext` `v0.37.0`](transports/wasm-ext/CHANGELOG.md#0370). - - Update to - [`libp2p-websocket` `v0.39.0`](transports/websocket/CHANGELOG.md#0390). - - Update to [`libp2p-yamux` `v0.41.0`](muxers/mplex/CHANGELOG.md#0410). +- Remove default features. You need to enable required features explicitly now. As a quick workaround, you may want to use the + new `full` feature which activates all features. See [PR 2918]. + +- Introduce `tokio` and `async-std` features and deprecate the following ones: + - `tcp-tokio` in favor of `tcp` + `tokio` + - `mdns-tokio` in favor of `mdns` + `tokio` + - `dns-tokio` in favor of `dns` + `tokio` + - `tcp-async-io` in favor of `tcp` + `async-std` + - `mdns-async-io` in favor of `mdns` + `async-std` + - `dns-async-std` in favor of `dns` + `async-std` + + See [PR 2962]. + +- Update individual crates. + - Update to [`libp2p-autonat` `v0.8.0`](protocols/autonat/CHANGELOG.md#0080). + - Update to [`libp2p-core` `v0.37.0`](core/CHANGELOG.md#0370). + - Update to [`libp2p-dcutr` `v0.7.0`](protocols/dcutr/CHANGELOG.md#0070). + - Update to [`libp2p-deflate` `v0.37.0`](transports/deflate/CHANGELOG.md#0370). + - Update to [`libp2p-dns` `v0.37.0`](transports/dns/CHANGELOG.md#0370). + - Update to [`libp2p-floodsub` `v0.40.0`](protocols/floodsub/CHANGELOG.md#0400). + - Update to [`libp2p-gossipsub` `v0.42.0`](protocols/gossipsub/CHANGELOG.md#0420). + - Update to [`libp2p-identify` `v0.40.0`](protocols/identify/CHANGELOG.md#0400). + - Update to [`libp2p-kad` `v0.41.0`](protocols/kad/CHANGELOG.md#0410). + - Update to [`libp2p-mdns` `v0.41.0`](protocols/mdns/CHANGELOG.md#0410). + - Update to [`libp2p-metrics` `v0.10.0`](misc/metrics/CHANGELOG.md#0100). + - Update to [`libp2p-mplex` `v0.37.0`](muxers/mplex/CHANGELOG.md#0370). + - Update to [`libp2p-noise` `v0.40.0`](transports/noise/CHANGELOG.md#0400). + - Update to [`libp2p-ping` `v0.40.0`](protocols/ping/CHANGELOG.md#0400). + - Update to [`libp2p-plaintext` `v0.37.0`](transports/plaintext/CHANGELOG.md#0370). + - Update to [`libp2p-relay` `v0.13.0`](protocols/relay/CHANGELOG.md#0130). + - Update to [`libp2p-rendezvous` `v0.10.0`](protocols/rendezovus/CHANGELOG.md#0100). + - Update to [`libp2p-request-response` `v0.22.0`](protocols/request-response/CHANGELOG.md#0220). + - Update to [`libp2p-swarm-derive` `v0.30.1`](swarm-derive/CHANGELOG.md#0301). + - Update to [`libp2p-swarm` `v0.40.0`](swarm/CHANGELOG.md#0400). + - Update to [`libp2p-tcp` `v0.37.0`](transports/tcp/CHANGELOG.md#0370). + - Update to [`libp2p-uds` `v0.36.0`](transports/uds/CHANGELOG.md#0360). + - Update to [`libp2p-wasm-ext` `v0.37.0`](transports/wasm-ext/CHANGELOG.md#0370). + - Update to [`libp2p-websocket` `v0.39.0`](transports/websocket/CHANGELOG.md#0390). + - Update to [`libp2p-yamux` `v0.41.0`](muxers/mplex/CHANGELOG.md#0410). [PR 2918]: https://github.com/libp2p/rust-libp2p/pull/2918 [PR 2962]: https://github.com/libp2p/rust-libp2p/pull/2962 ## 0.48.0 -- Update to [`libp2p-core` `v0.36.0`](core/CHANGELOG.md#0360). +- Update to [`libp2p-core` `v0.36.0`](core/CHANGELOG.md#0360). -- Update to [`libp2p-swarm-derive` `v0.30.0`](swarm-derive/CHANGELOG.md#0300). +- Update to [`libp2p-swarm-derive` `v0.30.0`](swarm-derive/CHANGELOG.md#0300). -- Update to [`libp2p-dcutr` `v0.6.0`](protocols/dcutr/CHANGELOG.md#060). +- Update to [`libp2p-dcutr` `v0.6.0`](protocols/dcutr/CHANGELOG.md#060). -- Update to - [`libp2p-rendezvous` `v0.9.0`](protocols/rendezvous/CHANGELOG.md#090). +- Update to [`libp2p-rendezvous` `v0.9.0`](protocols/rendezvous/CHANGELOG.md#090). -- Update to [`libp2p-ping` `v0.39.0`](protocols/ping/CHANGELOG.md#0390). +- Update to [`libp2p-ping` `v0.39.0`](protocols/ping/CHANGELOG.md#0390). -- Update to [`libp2p-identify` `v0.39.0`](protocols/identify/CHANGELOG.md#0390). +- Update to [`libp2p-identify` `v0.39.0`](protocols/identify/CHANGELOG.md#0390). -- Update to [`libp2p-floodsub` `v0.39.0`](protocols/floodsub/CHANGELOG.md#0390). +- Update to [`libp2p-floodsub` `v0.39.0`](protocols/floodsub/CHANGELOG.md#0390). -- Update to [`libp2p-relay` `v0.12.0`](protocols/relay/CHANGELOG.md#0120). +- Update to [`libp2p-relay` `v0.12.0`](protocols/relay/CHANGELOG.md#0120). -- Update to [`libp2p-metrics` `v0.9.0`](misc/metrics/CHANGELOG.md#090). +- Update to [`libp2p-metrics` `v0.9.0`](misc/metrics/CHANGELOG.md#090). -- Update to [`libp2p-kad` `v0.40.0`](protocols/kad/CHANGELOG.md#0400). +- Update to [`libp2p-kad` `v0.40.0`](protocols/kad/CHANGELOG.md#0400). -- Update to [`libp2p-autonat` `v0.7.0`](protocols/autonat/CHANGELOG.md#070). +- Update to [`libp2p-autonat` `v0.7.0`](protocols/autonat/CHANGELOG.md#070). -- Update to - [`libp2p-request-response` `v0.21.0`](protocols/request-response/CHANGELOG.md#0210). +- Update to [`libp2p-request-response` `v0.21.0`](protocols/request-response/CHANGELOG.md#0210). ## 0.47.0 -- Update to [`libp2p-dcutr` `v0.5.0`](protocols/dcutr/CHANGELOG.md#050). +- Update to [`libp2p-dcutr` `v0.5.0`](protocols/dcutr/CHANGELOG.md#050). -- Update to [`libp2p-derive` `v0.29.0`](swarm-derive/CHANGELOG.md#0290). +- Update to [`libp2p-derive` `v0.29.0`](swarm-derive/CHANGELOG.md#0290). -- Update to - [`libp2p-rendezvous` `v0.8.0`](protocols/rendezvous/CHANGELOG.md#080). +- Update to [`libp2p-rendezvous` `v0.8.0`](protocols/rendezvous/CHANGELOG.md#080). -- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380). +- Update to [`libp2p-ping` `v0.38.0`](protocols/ping/CHANGELOG.md#0380). -- Update to [`libp2p-identify` `v0.38.0`](protocols/identify/CHANGELOG.md#0380). +- Update to [`libp2p-identify` `v0.38.0`](protocols/identify/CHANGELOG.md#0380). -- Update to [`libp2p-floodsub` `v0.38.0`](protocols/floodsub/CHANGELOG.md#0380). +- Update to [`libp2p-floodsub` `v0.38.0`](protocols/floodsub/CHANGELOG.md#0380). -- Update to [`libp2p-relay` `v0.11.0`](protocols/relay/CHANGELOG.md#0110). +- Update to [`libp2p-relay` `v0.11.0`](protocols/relay/CHANGELOG.md#0110). -- Update to [`libp2p-metrics` `v0.8.0`](misc/metrics/CHANGELOG.md#080). +- Update to [`libp2p-metrics` `v0.8.0`](misc/metrics/CHANGELOG.md#080). -- Update to [`libp2p-kad` `v0.39.0`](protocols/kad/CHANGELOG.md#0390). +- Update to [`libp2p-kad` `v0.39.0`](protocols/kad/CHANGELOG.md#0390). -- Update to [`libp2p-autonat` `v0.6.0`](protocols/autonat/CHANGELOG.md#060). +- Update to [`libp2p-autonat` `v0.6.0`](protocols/autonat/CHANGELOG.md#060). -- Update to - [`libp2p-request-response` `v0.20.0`](protocols/request-response/CHANGELOG.md#0200). +- Update to [`libp2p-request-response` `v0.20.0`](protocols/request-response/CHANGELOG.md#0200). -- Update to [`libp2p-swarm` `v0.38.0`](swarm/CHANGELOG.md#0380). +- Update to [`libp2p-swarm` `v0.38.0`](swarm/CHANGELOG.md#0380). ## 0.46.1 -- Update to `libp2p-derive` [`v0.28.0`](swarm-derive/CHANGELOG.md#0280). +- Update to `libp2p-derive` [`v0.28.0`](swarm-derive/CHANGELOG.md#0280). ## 0.46.0 -- Semver bump Rust from `1.56.1` to `1.60.0` . See [PR 2646]. -- Added weak dependencies for features. See [PR 2646]. -- Update individual crates. - - Update to [`libp2p-autonat` `v0.5.0`](protocols/autonat/CHANGELOG.md#050). - - Update to [`libp2p-core` `v0.34.0`](core/CHANGELOG.md#0340). - - Update to [`libp2p-dcutr` `v0.4.0`](protocols/dcutr/CHANGELOG.md#040). - - Update to - [`libp2p-floodsub` `v0.37.0`](protocols/floodsub/CHANGELOG.md#0370). - - Update to - [`libp2p-identify` `v0.37.0`](protocols/identify/CHANGELOG.md#0370). - - Update to [`libp2p-kad` `v0.38.0`](protocols/kad/CHANGELOG.md#0380). - - Update to [`libp2p-metrics` `v0.7.0`](misc/metrics/CHANGELOG.md#070). - - Update to [`libp2p-mplex` `v0.34.0`](muxers/mplex/CHANGELOG.md). - - Update to [`libp2p-noise` `v0.37.0`](transports/noise/CHANGELOG.md#0370). - - Update to [`libp2p-ping` `v0.37.0`](protocols/ping/CHANGELOG.md#0370). - - Update to - [`libp2p-plaintext` `v0.34.0`](transports/plaintext/CHANGELOG.md#0340). - - Update to [`libp2p-relay` `v0.10.0`](protocols/relay/CHANGELOG.md#0100). - - Update to - [`libp2p-rendezvous` `v0.7.0`](protocols/rendezvous/CHANGELOG.md#070). - - Update to - [`libp2p-request-response` `v0.19.0`](protocols/request-response/CHANGELOG.md#0190). - - Update to [`libp2p-swarm` `v0.37.0`](swarm/CHANGELOG.md#0370). - - Update to - [`libp2p-wasm-ext` `v0.34.0`](transports/wasm-ext/CHANGELOG.md#0340). - - Update to [`libp2p-yamux` `v0.38.0`](muxers/yamux/CHANGELOG.md#0380). - - Update to `libp2p-uds` [`v0.33.0`](transports/uds/CHANGELOG.md). +- Semver bump Rust from `1.56.1` to `1.60.0` . See [PR 2646]. +- Added weak dependencies for features. See [PR 2646]. +- Update individual crates. + - Update to [`libp2p-autonat` `v0.5.0`](protocols/autonat/CHANGELOG.md#050). + - Update to [`libp2p-core` `v0.34.0`](core/CHANGELOG.md#0340). + - Update to [`libp2p-dcutr` `v0.4.0`](protocols/dcutr/CHANGELOG.md#040). + - Update to [`libp2p-floodsub` `v0.37.0`](protocols/floodsub/CHANGELOG.md#0370). + - Update to [`libp2p-identify` `v0.37.0`](protocols/identify/CHANGELOG.md#0370). + - Update to [`libp2p-kad` `v0.38.0`](protocols/kad/CHANGELOG.md#0380). + - Update to [`libp2p-metrics` `v0.7.0`](misc/metrics/CHANGELOG.md#070). + - Update to [`libp2p-mplex` `v0.34.0`](muxers/mplex/CHANGELOG.md). + - Update to [`libp2p-noise` `v0.37.0`](transports/noise/CHANGELOG.md#0370). + - Update to [`libp2p-ping` `v0.37.0`](protocols/ping/CHANGELOG.md#0370). + - Update to [`libp2p-plaintext` `v0.34.0`](transports/plaintext/CHANGELOG.md#0340). + - Update to [`libp2p-relay` `v0.10.0`](protocols/relay/CHANGELOG.md#0100). + - Update to [`libp2p-rendezvous` `v0.7.0`](protocols/rendezvous/CHANGELOG.md#070). + - Update to [`libp2p-request-response` `v0.19.0`](protocols/request-response/CHANGELOG.md#0190). + - Update to [`libp2p-swarm` `v0.37.0`](swarm/CHANGELOG.md#0370). + - Update to [`libp2p-wasm-ext` `v0.34.0`](transports/wasm-ext/CHANGELOG.md#0340). + - Update to [`libp2p-yamux` `v0.38.0`](muxers/yamux/CHANGELOG.md#0380). + - Update to `libp2p-uds` [`v0.33.0`](transports/uds/CHANGELOG.md). [PR 2646]: https://github.com/libp2p/rust-libp2p/pull/2646 ## 0.45.1 -- Update individual crates. - - Update to [`libp2p-dcutr` `v0.3.1`](protocols/dcutr/CHANGELOG.md). - - Update to [`libp2p-identify` `v0.36.1`](protocols/identify/CHANGELOG.md). - - Update to [`libp2p-kad` `v0.37.1`](protocols/kad/CHANGELOG.md). - - Update to [`libp2p-relay` `v0.9.1`](protocols/relay/CHANGELOG.md). - - Update to [`libp2p-swarm` `v0.36.1`](swarm/CHANGELOG.md). +- Update individual crates. + - Update to [`libp2p-dcutr` `v0.3.1`](protocols/dcutr/CHANGELOG.md). + - Update to [`libp2p-identify` `v0.36.1`](protocols/identify/CHANGELOG.md). + - Update to [`libp2p-kad` `v0.37.1`](protocols/kad/CHANGELOG.md). + - Update to [`libp2p-relay` `v0.9.1`](protocols/relay/CHANGELOG.md). + - Update to [`libp2p-swarm` `v0.36.1`](swarm/CHANGELOG.md). ## 0.45.0 - -- Update individual crates. - - Update to [`libp2p-plaintext` `v0.33.0`](transports/plaintext/CHANGELOG.md). - - Update to [`libp2p-noise` `v0.36.0`](transports/noise/CHANGELOG.md). - - Update to [`libp2p-wasm-ext` `v0.33.0`](transports/wasm-ext/CHANGELOG.md). - - Update to [`libp2p-yamux` `v0.37.0`](muxers/yamux/CHANGELOG.md). - - Update to [`libp2p-mplex` `v0.33.0`](muxers/mplex/CHANGELOG.md). - - Update to [`libp2p-dcutr` `v0.3.0`](protocols/dcutr/CHANGELOG.md). - - Update to [`libp2p-rendezvous` `v0.6.0`](protocols/rendezvous/CHANGELOG.md). - - Update to [`libp2p-ping` `v0.36.0`](protocols/ping/CHANGELOG.md). - - Update to [`libp2p-identify` `v0.36.0`](protocols/identify/CHANGELOG.md). - - Update to [`libp2p-floodsub` `v0.36.0`](protocols/floodsub/CHANGELOG.md). - - Update to [`libp2p-relay` `v0.9.0`](protocols/relay/CHANGELOG.md). - - Update to [`libp2p-metrics` `v0.6.0`](misc/metrics/CHANGELOG.md). - - Update to [`libp2p-kad` `v0.37.0`](protocols/kad/CHANGELOG.md). - - Update to [`libp2p-autonat` `v0.4.0`](protocols/autonat/CHANGELOG.md). - - Update to - [`libp2p-request-response` `v0.18.0`](protocols/request-response/CHANGELOG.md). - - Update to [`libp2p-swarm` `v0.36.0`](swarm/CHANGELOG.md). - - Update to [`libp2p-core` `v0.33.0`](core/CHANGELOG.md). +- Update individual crates. + - Update to [`libp2p-plaintext` `v0.33.0`](transports/plaintext/CHANGELOG.md). + - Update to [`libp2p-noise` `v0.36.0`](transports/noise/CHANGELOG.md). + - Update to [`libp2p-wasm-ext` `v0.33.0`](transports/wasm-ext/CHANGELOG.md). + - Update to [`libp2p-yamux` `v0.37.0`](muxers/yamux/CHANGELOG.md). + - Update to [`libp2p-mplex` `v0.33.0`](muxers/mplex/CHANGELOG.md). + - Update to [`libp2p-dcutr` `v0.3.0`](protocols/dcutr/CHANGELOG.md). + - Update to [`libp2p-rendezvous` `v0.6.0`](protocols/rendezvous/CHANGELOG.md). + - Update to [`libp2p-ping` `v0.36.0`](protocols/ping/CHANGELOG.md). + - Update to [`libp2p-identify` `v0.36.0`](protocols/identify/CHANGELOG.md). + - Update to [`libp2p-floodsub` `v0.36.0`](protocols/floodsub/CHANGELOG.md). + - Update to [`libp2p-relay` `v0.9.0`](protocols/relay/CHANGELOG.md). + - Update to [`libp2p-metrics` `v0.6.0`](misc/metrics/CHANGELOG.md). + - Update to [`libp2p-kad` `v0.37.0`](protocols/kad/CHANGELOG.md). + - Update to [`libp2p-autonat` `v0.4.0`](protocols/autonat/CHANGELOG.md). + - Update to [`libp2p-request-response` `v0.18.0`](protocols/request-response/CHANGELOG.md). + - Update to [`libp2p-swarm` `v0.36.0`](swarm/CHANGELOG.md). + - Update to [`libp2p-core` `v0.33.0`](core/CHANGELOG.md). ## 0.44.0 -- Update individual crates. - - Update to [`libp2p-dcutr` `v0.2.0`](protocols/dcutr/CHANGELOG.md). - - Update to [`libp2p-dns` `v0.32.1`](transports/dns/CHANGELOG.md). - - Update to [`libp2p-rendezvous` `v0.5.0`](protocols/rendezvous/CHANGELOG.md). - - Update to [`libp2p-ping` `v0.35.0`](protocols/ping/CHANGELOG.md). - - Update to [`libp2p-identify` `v0.35.0`](protocols/identify/CHANGELOG.md). - - Update to [`libp2p-floodsub` `v0.35.0`](protocols/floodsub/CHANGELOG.md). - - Update to [`libp2p-relay` `v0.8.0`](protocols/relay/CHANGELOG.md). - - Update to [`libp2p-metrics` `v0.5.0`](misc/metrics/CHANGELOG.md). - - Update to [`libp2p-kad` `v0.36.0`](protocols/kad/CHANGELOG.md). - - Update to [`libp2p-autonat` `v0.3.0`](protocols/autonat/CHANGELOG.md). - - Update to - [`libp2p-request-response` `v0.17.0`](protocols/request-response/CHANGELOG.md). - - Update to [`libp2p-swarm` `v0.35.0`](swarm/CHANGELOG.md). +- Update individual crates. + - Update to [`libp2p-dcutr` `v0.2.0`](protocols/dcutr/CHANGELOG.md). + - Update to [`libp2p-dns` `v0.32.1`](transports/dns/CHANGELOG.md). + - Update to [`libp2p-rendezvous` `v0.5.0`](protocols/rendezvous/CHANGELOG.md). + - Update to [`libp2p-ping` `v0.35.0`](protocols/ping/CHANGELOG.md). + - Update to [`libp2p-identify` `v0.35.0`](protocols/identify/CHANGELOG.md). + - Update to [`libp2p-floodsub` `v0.35.0`](protocols/floodsub/CHANGELOG.md). + - Update to [`libp2p-relay` `v0.8.0`](protocols/relay/CHANGELOG.md). + - Update to [`libp2p-metrics` `v0.5.0`](misc/metrics/CHANGELOG.md). + - Update to [`libp2p-kad` `v0.36.0`](protocols/kad/CHANGELOG.md). + - Update to [`libp2p-autonat` `v0.3.0`](protocols/autonat/CHANGELOG.md). + - Update to [`libp2p-request-response` `v0.17.0`](protocols/request-response/CHANGELOG.md). + - Update to [`libp2p-swarm` `v0.35.0`](swarm/CHANGELOG.md). ## Version 0.43.0 [2022-02-22] -- Update individual crates. - - - Update to `libp2p-autonat` - [`v0.2.0`](protocols/autonat/CHANGELOG.md#020-2022-02-22). - - Update to `libp2p-core` [`v0.32.0`](core/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-deflate` - [`v0.32.0`](transports/deflate/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-dns` - [`v0.32.0`](transports/dns/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-floodsub` - [`v0.34.0`](protocols/floodsub/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-gossipsub` - [`v0.36.0`](protocols/gossipsub/CHANGELOG.md#0360-2022-02-22). - - Update to `libp2p-identify` - [`v0.34.0`](protocols/identify/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-kad` - [`v0.35.0`](protocols/kad/CHANGELOG.md#0350-2022-02-22). - - Update to `libp2p-mdns` - [`v0.35.0`](protocols/mdns/CHANGELOG.md#0350-2022-02-22). - - Update to `libp2p-metrics` - [`v0.4.0`](misc/metrics/CHANGELOG.md#040-2022-02-22). - - Update to `libp2p-mplex` - [`v0.32.0`](muxers/mplex/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-noise` - [`v0.35.0`](transports/noise/CHANGELOG.md#0350-2022-02-22). - - Update to `libp2p-ping` - [`v0.34.0`](protocols/ping/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-plaintext` - [`v0.32.0`](transports/plaintext/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-relay` - [`v0.7.0`](protocols/relay/CHANGELOG.md#070-2022-02-22). - - Update to `libp2p-rendezvous` - [`v0.4.0`](protocols/rendezvous/CHANGELOG.md#040-2022-02-22). - - Update to `libp2p-request-response` - [`v0.16.0`](protocols/request-response/CHANGELOG.md#0160-2022-02-22). - - Update to `libp2p-swarm` [`v0.34.0`](swarm/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-derive` - [`v0.27.0`](swarm-derive/CHANGELOG.md#0270-2022-02-22). - - Update to `libp2p-tcp` - [`v0.32.0`](transports/tcp/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-uds` - [`v0.32.0`](transports/uds/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-wasm-ext` - [`v0.32.0`](transports/wasm-ext/CHANGELOG.md#0320-2022-02-22). - - Update to `libp2p-websocket` - [`v0.34.0`](transports/websocket/CHANGELOG.md#0340-2022-02-22). - - Update to `libp2p-yamux` - [`v0.36.0`](muxers/yamux/CHANGELOG.md#0360-2022-02-22). - -- Update to `parking_lot` `v0.12.0`. See [PR 2463]. - -- Drop support for gossipsub in the wasm32-unknown-unknown target (see - [PR 2506]). +- Update individual crates. + - Update to `libp2p-autonat` [`v0.2.0`](protocols/autonat/CHANGELOG.md#020-2022-02-22). + - Update to `libp2p-core` [`v0.32.0`](core/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-deflate` [`v0.32.0`](transports/deflate/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-dns` [`v0.32.0`](transports/dns/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-floodsub` [`v0.34.0`](protocols/floodsub/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-gossipsub` [`v0.36.0`](protocols/gossipsub/CHANGELOG.md#0360-2022-02-22). + - Update to `libp2p-identify` [`v0.34.0`](protocols/identify/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-kad` [`v0.35.0`](protocols/kad/CHANGELOG.md#0350-2022-02-22). + - Update to `libp2p-mdns` [`v0.35.0`](protocols/mdns/CHANGELOG.md#0350-2022-02-22). + - Update to `libp2p-metrics` [`v0.4.0`](misc/metrics/CHANGELOG.md#040-2022-02-22). + - Update to `libp2p-mplex` [`v0.32.0`](muxers/mplex/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-noise` [`v0.35.0`](transports/noise/CHANGELOG.md#0350-2022-02-22). + - Update to `libp2p-ping` [`v0.34.0`](protocols/ping/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-plaintext` [`v0.32.0`](transports/plaintext/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-relay` [`v0.7.0`](protocols/relay/CHANGELOG.md#070-2022-02-22). + - Update to `libp2p-rendezvous` [`v0.4.0`](protocols/rendezvous/CHANGELOG.md#040-2022-02-22). + - Update to `libp2p-request-response` [`v0.16.0`](protocols/request-response/CHANGELOG.md#0160-2022-02-22). + - Update to `libp2p-swarm` [`v0.34.0`](swarm/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-derive` [`v0.27.0`](swarm-derive/CHANGELOG.md#0270-2022-02-22). + - Update to `libp2p-tcp` [`v0.32.0`](transports/tcp/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-uds` [`v0.32.0`](transports/uds/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-wasm-ext` [`v0.32.0`](transports/wasm-ext/CHANGELOG.md#0320-2022-02-22). + - Update to `libp2p-websocket` [`v0.34.0`](transports/websocket/CHANGELOG.md#0340-2022-02-22). + - Update to `libp2p-yamux` [`v0.36.0`](muxers/yamux/CHANGELOG.md#0360-2022-02-22). + +- Update to `parking_lot` `v0.12.0`. See [PR 2463]. + +- Drop support for gossipsub in the wasm32-unknown-unknown target (see [PR 2506]). [PR 2506]: https://github.com/libp2p/rust-libp2p/pull/2506 [PR 2463]: https://github.com/libp2p/rust-libp2p/pull/2463/ ## Version 0.42.1 [2022-02-02] -- Update individual crates. - - `libp2p-relay` - - [v0.6.1](protocols/relay/CHANGELOG.md#061-2022-02-02) - - `libp2p-tcp` - - [v0.31.1](transports/tcp/CHANGELOG.md#0311-2022-02-02) +- Update individual crates. + - `libp2p-relay` + - [v0.6.1](protocols/relay/CHANGELOG.md#061-2022-02-02) + - `libp2p-tcp` + - [v0.31.1](transports/tcp/CHANGELOG.md#0311-2022-02-02) ## Version 0.42.0 [2022-01-27] -- Update individual crates. - - - `libp2p-autonat` - - [v0.1.0](protocols/autonat/CHANGELOG.md#010-2022-01-27) - - `libp2p-core` - - [v0.31.0](core/CHANGELOG.md#0310-2022-01-27) - - `libp2p-deflate` - - [v0.31.0](transports/deflate/CHANGELOG.md#0310-2022-01-27) - - `libp2p-dns` - - [v0.31.0](transports/dns/CHANGELOG.md#0310-2022-01-27) - - `libp2p-floodsub` - - [v0.33.0](protocols/floodsub/CHANGELOG.md#0330-2022-01-27) - - `libp2p-gossipsub` - - [v0.35.0](protocols/gossipsub/CHANGELOG.md#0350-2022-01-27) - - `libp2p-identify` - - [v0.33.0](protocols/identify/CHANGELOG.md#0330-2022-01-27) - - `libp2p-kad` - - [v0.34.0](protocols/kad/CHANGELOG.md#0340-2022-01-27) - - `libp2p-mdns` (breaking compatibility with previous versions) - - [v0.34.0](protocols/mdns/CHANGELOG.md#0340-2022-01-27) - - `libp2p-metrics` - - [v0.3.0](misc/metrics/CHANGELOG.md#030-2022-01-27) - - `libp2p-mplex` - - [v0.31.0](muxers/mplex/CHANGELOG.md#0310-2022-01-27) - - `libp2p-noise` - - [v0.34.0](transports/noise/CHANGELOG.md#0340-2022-01-27) - - `libp2p-ping` - - [v0.33.0](protocols/ping/CHANGELOG.md#0330-2022-01-27) - - `libp2p-plaintext` - - [v0.31.0](transports/plaintext/CHANGELOG.md#0310-2022-01-27) - - `libp2p-relay` - - [v0.6.0](protocols/relay/CHANGELOG.md#060-2022-01-27) - - `libp2p-rendezvous` - - [v0.3.0](protocols/rendezvous/CHANGELOG.md#030-2022-01-27) - - `libp2p-request-response` - - [v0.15.0](protocols/request-response/CHANGELOG.md#0150-2022-01-27) - - `libp2p-swarm-derive` - - [v0.26.1](swarm-derive/CHANGELOG.md#0261-2022-01-27) - - `libp2p-swarm` - - [v0.33.0](swarm/CHANGELOG.md#0330-2022-01-27) - - `libp2p-tcp` - - [v0.31.0](transports/tcp/CHANGELOG.md#0310-2022-01-27) - - `libp2p-uds` - - [v0.31.0](transports/uds/CHANGELOG.md#0310-2022-01-27) - - `libp2p-wasm-ext` - - [v0.31.0](transports/wasm-ext/CHANGELOG.md#0310-2022-01-27) - - `libp2p-websocket` - - [v0.33.0](transports/websocket/CHANGELOG.md#0330-2022-01-27) - - `libp2p-yamux` - - [v0.35.0](muxers/yamux/CHANGELOG.md#0350-2022-01-27) - -- Migrate to Rust edition 2021 (see [PR 2339]). +- Update individual crates. + - `libp2p-autonat` + - [v0.1.0](protocols/autonat/CHANGELOG.md#010-2022-01-27) + - `libp2p-core` + - [v0.31.0](core/CHANGELOG.md#0310-2022-01-27) + - `libp2p-deflate` + - [v0.31.0](transports/deflate/CHANGELOG.md#0310-2022-01-27) + - `libp2p-dns` + - [v0.31.0](transports/dns/CHANGELOG.md#0310-2022-01-27) + - `libp2p-floodsub` + - [v0.33.0](protocols/floodsub/CHANGELOG.md#0330-2022-01-27) + - `libp2p-gossipsub` + - [v0.35.0](protocols/gossipsub/CHANGELOG.md#0350-2022-01-27) + - `libp2p-identify` + - [v0.33.0](protocols/identify/CHANGELOG.md#0330-2022-01-27) + - `libp2p-kad` + - [v0.34.0](protocols/kad/CHANGELOG.md#0340-2022-01-27) + - `libp2p-mdns` (breaking compatibility with previous versions) + - [v0.34.0](protocols/mdns/CHANGELOG.md#0340-2022-01-27) + - `libp2p-metrics` + - [v0.3.0](misc/metrics/CHANGELOG.md#030-2022-01-27) + - `libp2p-mplex` + - [v0.31.0](muxers/mplex/CHANGELOG.md#0310-2022-01-27) + - `libp2p-noise` + - [v0.34.0](transports/noise/CHANGELOG.md#0340-2022-01-27) + - `libp2p-ping` + - [v0.33.0](protocols/ping/CHANGELOG.md#0330-2022-01-27) + - `libp2p-plaintext` + - [v0.31.0](transports/plaintext/CHANGELOG.md#0310-2022-01-27) + - `libp2p-relay` + - [v0.6.0](protocols/relay/CHANGELOG.md#060-2022-01-27) + - `libp2p-rendezvous` + - [v0.3.0](protocols/rendezvous/CHANGELOG.md#030-2022-01-27) + - `libp2p-request-response` + - [v0.15.0](protocols/request-response/CHANGELOG.md#0150-2022-01-27) + - `libp2p-swarm-derive` + - [v0.26.1](swarm-derive/CHANGELOG.md#0261-2022-01-27) + - `libp2p-swarm` + - [v0.33.0](swarm/CHANGELOG.md#0330-2022-01-27) + - `libp2p-tcp` + - [v0.31.0](transports/tcp/CHANGELOG.md#0310-2022-01-27) + - `libp2p-uds` + - [v0.31.0](transports/uds/CHANGELOG.md#0310-2022-01-27) + - `libp2p-wasm-ext` + - [v0.31.0](transports/wasm-ext/CHANGELOG.md#0310-2022-01-27) + - `libp2p-websocket` + - [v0.33.0](transports/websocket/CHANGELOG.md#0330-2022-01-27) + - `libp2p-yamux` + - [v0.35.0](muxers/yamux/CHANGELOG.md#0350-2022-01-27) + +- Migrate to Rust edition 2021 (see [PR 2339]). [PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339 ## Version 0.41.0 [2021-11-16] -- Update individual crates. - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-identify` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-metrics` - - `libp2p-ping` - - `libp2p-relay` - - `libp2p-rendezvous` - - `libp2p-request-response` - - `libp2p-swarm-derive` - - `libp2p-swarm` - - `libp2p-websocket` -- Forward `wasm-bindgen` feature to `futures-timer`, `instant`, `parking_lot`, - `getrandom/js` and `rand/wasm-bindgen`. +- Update individual crates. + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-identify` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-metrics` + - `libp2p-ping` + - `libp2p-relay` + - `libp2p-rendezvous` + - `libp2p-request-response` + - `libp2p-swarm-derive` + - `libp2p-swarm` + - `libp2p-websocket` +- Forward `wasm-bindgen` feature to `futures-timer`, `instant`, `parking_lot`, `getrandom/js` and `rand/wasm-bindgen`. ## Version 0.40.0 [2021-11-01] -- Update individual crates. - - - `libp2p-core` - - `libp2p-deflate` - - `libp2p-dns` - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-identify` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-mplex` - - `libp2p-noise` - - `libp2p-ping` - - `libp2p-plaintext` - - `libp2p-relay` - - `libp2p-request-response` - - `libp2p-swarm` - - `libp2p-tcp` - - `libp2p-uds` - - `libp2p-wasm-ext` - - `libp2p-websocket` - - `libp2p-yamux` - -- Re-export the `wasm-bindgen` feature from `parking_lot`, so `libp2p` users can - opt-in to that crate's Wasm support. See [PR 2180]. - -- Add `libp2p-metrics`. +- Update individual crates. + - `libp2p-core` + - `libp2p-deflate` + - `libp2p-dns` + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-identify` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-mplex` + - `libp2p-noise` + - `libp2p-ping` + - `libp2p-plaintext` + - `libp2p-relay` + - `libp2p-request-response` + - `libp2p-swarm` + - `libp2p-tcp` + - `libp2p-uds` + - `libp2p-wasm-ext` + - `libp2p-websocket` + - `libp2p-yamux` + +- Re-export the `wasm-bindgen` feature from `parking_lot`, so + `libp2p` users can opt-in to that crate's Wasm support. See [PR 2180]. + +- Add `libp2p-metrics`. [PR 2180]: https://github.com/libp2p/rust-libp2p/pull/2180/ ## Version 0.39.1 [2021-07-12] -- Update individual crates. - - `libp2p-swarm-derive` +- Update individual crates. + - `libp2p-swarm-derive` ## Version 0.39.0 [2021-07-12] -- Update individual crates. - - `libp2p-core` - - `libp2p-deflate` - - `libp2p-dns` - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-identify` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-mplex` - - `libp2p-noise` - - `libp2p-ping` - - `libp2p-plaintext` - - `libp2p-relay` - - `libp2p-request-response` - - `libp2p-swarm` - - `libp2p-tcp` - - `libp2p-uds` - - `libp2p-wasm-ext` - - `libp2p-websocket` - - `libp2p-yamux` +- Update individual crates. + - `libp2p-core` + - `libp2p-deflate` + - `libp2p-dns` + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-identify` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-mplex` + - `libp2p-noise` + - `libp2p-ping` + - `libp2p-plaintext` + - `libp2p-relay` + - `libp2p-request-response` + - `libp2p-swarm` + - `libp2p-tcp` + - `libp2p-uds` + - `libp2p-wasm-ext` + - `libp2p-websocket` + - `libp2p-yamux` ## Version 0.38.0 [2021-05-17] -- Update individual crates. - - `libp2p-core` - - `libp2p-gossipsub` - - `libp2p-noise` - - `libp2p-pnet` - - `libp2p-wasm-ext` +- Update individual crates. + - `libp2p-core` + - `libp2p-gossipsub` + - `libp2p-noise` + - `libp2p-pnet` + - `libp2p-wasm-ext` ## Version 0.37.1 [2021-04-14] -- Update individual crates. - - `libp2p-swarm-derive` +- Update individual crates. + - `libp2p-swarm-derive` ## Version 0.37.0 [2021-04-13] -- Update individual crates. - - - `libp2p-core` - - `libp2p-dns` - - `libp2p-floodsub` - - `libp2p-gossipsub` - - `libp2p-kad` - - `libp2p-mdns` - - `libp2p-ping` - - `libp2p-relay` - - `libp2p-request-response` - - `libp2p-swarm` - - `libp2p-wasm-ext` - - `libp2p-yamux` - -- Drop support for `wasm32-unknown-unknown` in favor of - `wasm32-unknown-emscripten` and `wasm32-wasi` - [PR 2038](https://github.com/libp2p/rust-libp2p/pull/2038). +- Update individual crates. + - `libp2p-core` + - `libp2p-dns` + - `libp2p-floodsub` + - `libp2p-gossipsub` + - `libp2p-kad` + - `libp2p-mdns` + - `libp2p-ping` + - `libp2p-relay` + - `libp2p-request-response` + - `libp2p-swarm` + - `libp2p-wasm-ext` + - `libp2p-yamux` + +- Drop support for `wasm32-unknown-unknown` in favor of + `wasm32-unknown-emscripten` and `wasm32-wasi` [PR + 2038](https://github.com/libp2p/rust-libp2p/pull/2038). ## Version 0.36.0 [2021-03-17] -- Consolidate top-level utility functions for constructing development - transports. There is now just `development_transport()` (available with - default features) and `tokio_development_transport()` (available when the - corresponding tokio features are enabled). Furthermore, these are now - `async fn`s. The minor variations that also included `pnet` support have been - removed. [PR 1927](https://github.com/libp2p/rust-libp2p/pull/1927) +- Consolidate top-level utility functions for constructing development + transports. There is now just `development_transport()` (available with default features) + and `tokio_development_transport()` (available when the corresponding tokio features are enabled). + Furthermore, these are now `async fn`s. The minor variations that also included `pnet` + support have been removed. + [PR 1927](https://github.com/libp2p/rust-libp2p/pull/1927) -- Update libp2p crates. +- Update libp2p crates. -- Do not leak default features from libp2p crates. - [PR 1986](https://github.com/libp2p/rust-libp2p/pull/1986). +- Do not leak default features from libp2p crates. + [PR 1986](https://github.com/libp2p/rust-libp2p/pull/1986). -- Add `libp2p-relay` to `libp2p` facade crate. +- Add `libp2p-relay` to `libp2p` facade crate. ## Version 0.35.1 [2021-02-17] -- Update `libp2p-yamux` to latest patch version. +- Update `libp2p-yamux` to latest patch version. ## Version 0.35.0 [2021-02-15] -- Use `libp2p-swarm-derive`, the former `libp2p-core-derive`. +- Use `libp2p-swarm-derive`, the former `libp2p-core-derive`. -- Update `libp2p-deflate`, `libp2p-gossipsub`, `libp2p-mdns`, - `libp2p-request-response`, `libp2p-swarm` and `libp2p-tcp`. +- Update `libp2p-deflate`, `libp2p-gossipsub`, `libp2p-mdns`, `libp2p-request-response`, + `libp2p-swarm` and `libp2p-tcp`. ## Version 0.34.0 [2021-01-12] -- Update `libp2p-core` and all dependent crates. +- Update `libp2p-core` and all dependent crates. -- The `tcp-async-std` feature is now `tcp-async-io`, still enabled by default. +- The `tcp-async-std` feature is now `tcp-async-io`, still + enabled by default. ## Version 0.33.0 [2020-12-17] -- Update `libp2p-core` and all dependent crates. +- Update `libp2p-core` and all dependent crates. ## Version 0.32.2 [2020-12-10] -- Update `libp2p-websocket`. +- Update `libp2p-websocket`. ## Version 0.32.1 [2020-12-09] -- Update minimum patch version of `libp2p-websocket`. +- Update minimum patch version of `libp2p-websocket`. ## Version 0.32.0 [2020-12-08] -- Update `libp2p-request-response`. +- Update `libp2p-request-response`. -- Update to `libp2p-mdns-0.26`. +- Update to `libp2p-mdns-0.26`. -- Update `libp2p-websocket` minimum patch version. +- Update `libp2p-websocket` minimum patch version. ## Version 0.31.2 [2020-12-02] -- Bump minimum `libp2p-core` patch version. +- Bump minimum `libp2p-core` patch version. ## Version 0.31.1 [2020-11-26] -- Bump minimum `libp2p-tcp` patch version. +- Bump minimum `libp2p-tcp` patch version. ## Version 0.31.0 [2020-11-25] -- Update `multistream-select` and all dependent crates. +- Update `multistream-select` and all dependent crates. ## Version 0.30.1 [2020-11-11] -- Update `libp2p-plaintext`. +- Update `libp2p-plaintext`. ## Version 0.30.0 [2020-11-09] -- Update `libp2p-mdns`, `libp2p-tcp` and `libp2p-uds` as well as `libp2p-core` - and all its dependers. +- Update `libp2p-mdns`, `libp2p-tcp` and `libp2p-uds` as well as `libp2p-core` + and all its dependers. ## Version 0.29.1 [2020-10-20] -- Update `libp2p-core`. +- Update `libp2p-core`. ## Version 0.29.0 [2020-10-16] -- Update `libp2p-core`, `libp2p-floodsub`, `libp2p-gossipsub`, `libp2p-mplex`, - `libp2p-noise`, `libp2p-plaintext`, `libp2p-pnet`, `libp2p-request-response`, - `libp2p-swarm`, `libp2p-tcp`, `libp2p-websocket` and `parity-multiaddr`. +- Update `libp2p-core`, `libp2p-floodsub`, `libp2p-gossipsub`, `libp2p-mplex`, + `libp2p-noise`, `libp2p-plaintext`, `libp2p-pnet`, `libp2p-request-response`, + `libp2p-swarm`, `libp2p-tcp`, `libp2p-websocket` and `parity-multiaddr`. ## Version 0.28.1 [2020-09-10] -- Update to `libp2p-core` `0.22.1`. +- Update to `libp2p-core` `0.22.1`. ## Version 0.28.0 [2020-09-09] -- Update `libp2p-yamux` to `0.25.0`. _Step 4 of 4 in a multi-release upgrade - process._ See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.25.0`. *Step 4 of 4 in a multi-release + upgrade process.* See the `libp2p-yamux` CHANGELOG for details. ## Version 0.27.0 [2020-09-09] -- Update `libp2p-yamux` to `0.24.0`. _Step 3 of 4 in a multi-release upgrade - process._ See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.24.0`. *Step 3 of 4 in a multi-release + upgrade process.* See the `libp2p-yamux` CHANGELOG for details. ## Version 0.26.0 [2020-09-09] -- Update `libp2p-yamux` to `0.23.0`. _Step 2 of 4 in a multi-release upgrade - process._ See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.23.0`. *Step 2 of 4 in a multi-release + upgrade process.* See the `libp2p-yamux` CHANGELOG for details. ## Version 0.25.0 [2020-09-09] -- Remove the deprecated `libp2p-secio` dependency. To continue to use SECIO, add - an explicit dependency on `libp2p-secio`. However, transitioning to - `libp2p-noise` is strongly recommended. +- Remove the deprecated `libp2p-secio` dependency. To continue to use + SECIO, add an explicit dependency on `libp2p-secio`. However, + transitioning to `libp2p-noise` is strongly recommended. -- Update `libp2p-yamux` to `0.22.0`. _This version starts a multi-release - upgrade process._ See the `libp2p-yamux` CHANGELOG for details. +- Update `libp2p-yamux` to `0.22.0`. *This version starts a multi-release + upgrade process.* See the `libp2p-yamux` CHANGELOG for details. -- Bump `libp2p-noise` to `0.24`. See the `libp2p-noise` changelog for details - about the `LegacyConfig`. +- Bump `libp2p-noise` to `0.24`. See the `libp2p-noise` +changelog for details about the `LegacyConfig`. -- The `ProtocolsHandler` in `libp2p-swarm` has a new associated type - `InboundOpenInfo` ([PR 1714]). +- The `ProtocolsHandler` in `libp2p-swarm` has a new associated type + `InboundOpenInfo` ([PR 1714]). [PR 1714]: https://github.com/libp2p/rust-libp2p/pull/1714 ## Version 0.24.0 [2020-08-18] -- Update `libp2p-core`, `libp2p-gossipsub`, `libp2p-kad`, `libp2p-mdns`, - `libp2p-ping`, `libp2p-request-response`, `libp2p-swarm` and dependent crates. +- Update `libp2p-core`, `libp2p-gossipsub`, `libp2p-kad`, `libp2p-mdns`, + `libp2p-ping`, `libp2p-request-response`, `libp2p-swarm` and dependent crates. ## Version 0.23.0 (2020-08-03) -**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` on an existing -deployment, this version must not be skipped or the provided legacy -configuration for `libp2p-noise` used! +**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` +on an existing deployment, this version must not be skipped +or the provided legacy configuration for `libp2p-noise` used! -- Bump `libp2p-noise` dependency to `0.22`. See the `libp2p-noise` changelog for - details about the `LegacyConfig`. +- Bump `libp2p-noise` dependency to `0.22`. See the `libp2p-noise` +changelog for details about the `LegacyConfig`. -- Refactored bandwidth logging - ([PR 1670](https://github.com/libp2p/rust-libp2p/pull/1670)). +- Refactored bandwidth logging ([PR 1670](https://github.com/libp2p/rust-libp2p/pull/1670)). ## Version 0.22.0 (2020-07-17) -**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` on an existing -deployment using `libp2p-noise`, this version must not be skipped! +**NOTE**: For a smooth upgrade path from `0.21` to `> 0.22` +on an existing deployment using `libp2p-noise`, this version +must not be skipped! -- Bump `libp2p-noise` dependency to `0.21`. +- Bump `libp2p-noise` dependency to `0.21`. ## Version 0.21.1 (2020-07-02) -- Bump `libp2p-websockets` lower bound. +- Bump `libp2p-websockets` lower bound. ## Version 0.21.0 (2020-07-01) -- Conditional compilation fixes for the `wasm32-wasi` target - ([PR 1633](https://github.com/libp2p/rust-libp2p/pull/1633)). +- Conditional compilation fixes for the `wasm32-wasi` target + ([PR 1633](https://github.com/libp2p/rust-libp2p/pull/1633)). -- New `libp2p-request-response` crate - ([PR 1596](https://github.com/libp2p/rust-libp2p/pull/1596)). +- New `libp2p-request-response` crate + ([PR 1596](https://github.com/libp2p/rust-libp2p/pull/1596)). -- Updated libp2p dependencies. +- Updated libp2p dependencies. ## Version 0.19.1 (2020-05-25) -- Temporarily pin all `async-std` dependencies to `< 1.6`. - [PR 1589](https://github.com/libp2p/rust-libp2p/pull/1589) +- Temporarily pin all `async-std` dependencies to `< 1.6`. + [PR 1589](https://github.com/libp2p/rust-libp2p/pull/1589) -- `libp2p-core-derive`: Fully qualified std::result::Result in macro - [PR 1587](https://github.com/libp2p/rust-libp2p/pull/1587) +- `libp2p-core-derive`: Fully qualified std::result::Result in macro + [PR 1587](https://github.com/libp2p/rust-libp2p/pull/1587) ## Version 0.19.0 (2020-05-18) -- `libp2p-core`, `libp2p-swarm`: Added support for multiple dialing attempts per - peer, with a configurable limit. - [PR 1506](https://github.com/libp2p/rust-libp2p/pull/1506) - -- `libp2p-core`: `PeerId`s that use the identity hashing will now be properly - displayed using the string representation of an identity multihash, rather - than the canonical SHA 256 representation. - [PR 1576](https://github.com/libp2p/rust-libp2p/pull/1576) - -- `libp2p-core`: Updated to multihash 0.11.0. - [PR 1566](https://github.com/libp2p/rust-libp2p/pull/1566) - -- `libp2p-core`: Make the number of events buffered to/from tasks configurable. - [PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574) - -- `libp2p-dns`, `parity-multiaddr`: Added support for the `/dns` multiaddr - protocol. Additionally, the `multiaddr::from_url` function will now use `/dns` - instead of `/dns4`. [PR 1575](https://github.com/libp2p/rust-libp2p/pull/1575) - -- `libp2p-noise`: Added the `X25519Spec` protocol suite which uses - libp2p-noise-spec compliant signatures on static keys as well as the `/noise` - protocol upgrade, hence providing a libp2p-noise-spec compliant `XX` - handshake. `IK` and `IX` are still supported with `X25519Spec` though not - guaranteed to be interoperable with other libp2p implementations as these - handshake patterns are not currently included in the libp2p-noise-spec. The - `X25519Spec` implementation will eventually replace the current `X25519` - implementation, with the former being removed. To upgrade without - interruptions, you may temporarily include `NoiseConfig`s for both - implementations as alternatives in your transport upgrade pipeline. - -- `libp2p-kad`: Consider fixed (K_VALUE) amount of peers at closest query - initialization. Unless `KademliaConfig::set_replication_factor` is used change - has no effect. [PR 1536](https://github.com/libp2p/rust-libp2p/pull/1536) - -- `libp2p-kad`: Provide more insight into, and control of, the execution of - queries. All query results are now wrapped in `KademliaEvent::QueryResult`. As - a side-effect of these changes and for as long as the record storage API is - not asynchronous, local storage errors on `put_record` are reported - synchronously in a `Result`, instead of being reported asynchronously by an - event. [PR 1567](https://github.com/libp2p/rust-libp2p/pull/1567) - -- `libp2p-tcp`, `libp2p`: Made the `libp2p-tcp/async-std` feature flag disabled - by default, and split the `libp2p/tcp` feature in two: `tcp-async-std` and - `tcp-tokio`. `tcp-async-std` is still enabled by default. - [PR 1471](https://github.com/libp2p/rust-libp2p/pull/1471) - -- `libp2p-tcp`: On listeners started with an IPv6 multi-address the socket - option `IPV6_V6ONLY` is set to true. Instead of relying on IPv4-mapped IPv6 - address support, two listeners can be started if IPv4 and IPv6 should both be - supported. IPv4 listener addresses are not affected by this change. - [PR 1555](https://github.com/libp2p/rust-libp2p/pull/1555) +- `libp2p-core`, `libp2p-swarm`: Added support for multiple dialing + attempts per peer, with a configurable limit. + [PR 1506](https://github.com/libp2p/rust-libp2p/pull/1506) + +- `libp2p-core`: `PeerId`s that use the identity hashing will now be properly + displayed using the string representation of an identity multihash, rather + than the canonical SHA 256 representation. + [PR 1576](https://github.com/libp2p/rust-libp2p/pull/1576) + +- `libp2p-core`: Updated to multihash 0.11.0. + [PR 1566](https://github.com/libp2p/rust-libp2p/pull/1566) + +- `libp2p-core`: Make the number of events buffered to/from tasks configurable. + [PR 1574](https://github.com/libp2p/rust-libp2p/pull/1574) + +- `libp2p-dns`, `parity-multiaddr`: Added support for the `/dns` multiaddr + protocol. Additionally, the `multiaddr::from_url` function will now use + `/dns` instead of `/dns4`. + [PR 1575](https://github.com/libp2p/rust-libp2p/pull/1575) + +- `libp2p-noise`: Added the `X25519Spec` protocol suite which uses + libp2p-noise-spec compliant signatures on static keys as well as the + `/noise` protocol upgrade, hence providing a libp2p-noise-spec compliant + `XX` handshake. `IK` and `IX` are still supported with `X25519Spec` + though not guaranteed to be interoperable with other libp2p + implementations as these handshake patterns are not currently + included in the libp2p-noise-spec. The `X25519Spec` implementation + will eventually replace the current `X25519` implementation, with + the former being removed. To upgrade without interruptions, you may + temporarily include `NoiseConfig`s for both implementations as + alternatives in your transport upgrade pipeline. + +- `libp2p-kad`: Consider fixed (K_VALUE) amount of peers at closest query + initialization. Unless `KademliaConfig::set_replication_factor` is used change + has no effect. + [PR 1536](https://github.com/libp2p/rust-libp2p/pull/1536) + +- `libp2p-kad`: Provide more insight into, and control of, the execution of + queries. All query results are now wrapped in `KademliaEvent::QueryResult`. + As a side-effect of these changes and for as long as the record storage + API is not asynchronous, local storage errors on `put_record` are reported + synchronously in a `Result`, instead of being reported asynchronously by + an event. + [PR 1567](https://github.com/libp2p/rust-libp2p/pull/1567) + +- `libp2p-tcp`, `libp2p`: Made the `libp2p-tcp/async-std` feature flag + disabled by default, and split the `libp2p/tcp` feature in two: + `tcp-async-std` and `tcp-tokio`. `tcp-async-std` is still enabled by default. + [PR 1471](https://github.com/libp2p/rust-libp2p/pull/1471) + +- `libp2p-tcp`: On listeners started with an IPv6 multi-address the socket + option `IPV6_V6ONLY` is set to true. Instead of relying on IPv4-mapped IPv6 + address support, two listeners can be started if IPv4 and IPv6 should both + be supported. IPv4 listener addresses are not affected by this change. + [PR 1555](https://github.com/libp2p/rust-libp2p/pull/1555) ## Version 0.18.1 (2020-04-17) -- `libp2p-swarm`: Make sure inject_dial_failure is called in all situations. - [PR 1549](https://github.com/libp2p/rust-libp2p/pull/1549) +- `libp2p-swarm`: Make sure inject_dial_failure is called in all situations. + [PR 1549](https://github.com/libp2p/rust-libp2p/pull/1549) ## Version 0.18.0 (2020-04-09) -- `libp2p-core`: Treat connection limit errors as pending connection errors. - [PR 1546](https://github.com/libp2p/rust-libp2p/pull/1546) +- `libp2p-core`: Treat connection limit errors as pending connection errors. + [PR 1546](https://github.com/libp2p/rust-libp2p/pull/1546) -- `libp2p-core-derive`: Disambiguate calls to `NetworkBehaviour::inject_event`. - [PR 1543](https://github.com/libp2p/rust-libp2p/pull/1543) +- `libp2p-core-derive`: Disambiguate calls to `NetworkBehaviour::inject_event`. + [PR 1543](https://github.com/libp2p/rust-libp2p/pull/1543) -- `libp2p-floodsub`: Allow sent messages seen as subscribed. - [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) +- `libp2p-floodsub`: Allow sent messages seen as subscribed. + [PR 1520](https://github.com/libp2p/rust-libp2p/pull/1520) -- `libp2p-kad`: Return peers independent of record existence. - [PR 1544](https://github.com/libp2p/rust-libp2p/pull/1544) +- `libp2p-kad`: Return peers independent of record existence. + [PR 1544](https://github.com/libp2p/rust-libp2p/pull/1544) -- `libp2p-wasm-ext`: Fix "parsed is null" errors being thrown. - [PR 1535](https://github.com/libp2p/rust-libp2p/pull/1535) +- `libp2p-wasm-ext`: Fix "parsed is null" errors being thrown. + [PR 1535](https://github.com/libp2p/rust-libp2p/pull/1535) ## Version 0.17.0 (2020-04-02) -- `libp2p-core`: Finished "identity hashing" for peer IDs migration. - [PR 1460](https://github.com/libp2p/rust-libp2p/pull/1460) -- `libp2p-core`: Remove `poll_broadcast`. - [PR 1527](https://github.com/libp2p/rust-libp2p/pull/1527) -- `libp2p-core`, `libp2p-swarm`: Report addresses of closed listeners. - [PR 1485](https://github.com/libp2p/rust-libp2p/pull/1485) -- `libp2p-core`: Support for multiple connections per peer and configurable - connection limits. See - [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), - [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and - [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. - -- `libp2p-swarm`: Pass the cause of closing a listener to - `inject_listener_closed`. - [PR 1517](https://github.com/libp2p/rust-libp2p/pull/1517) -- `libp2p-swarm`: Support for multiple connections per peer and configurable - connection limits. See - [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), - [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and - [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. -- `libp2p-swarm`: The `SwarmEvent` now returns more events. - [PR 1515](https://github.com/libp2p/rust-libp2p/pull/1515) -- `libp2p-swarm`: New `protocols_handler::multi` module. - [PR 1497](https://github.com/libp2p/rust-libp2p/pull/1497) -- `libp2p-swarm`: Allow configuration of outbound substreams. - [PR 1521](https://github.com/libp2p/rust-libp2p/pull/1521) - -- `libp2p-kad`: Providers returned from a lookup are now deduplicated. - [PR 1528](https://github.com/libp2p/rust-libp2p/pull/1528) -- `libp2p-kad`: Allow customising the maximum packet size. - [PR 1502](https://github.com/libp2p/rust-libp2p/pull/1502) -- `libp2p-kad`: Allow customising the (libp2p) connection keep-alive timeout. - [PR 1477](https://github.com/libp2p/rust-libp2p/pull/1477) -- `libp2p-kad`: Avoid storing records that are expired upon receipt - (optimisation). [PR 1496](https://github.com/libp2p/rust-libp2p/pull/1496) -- `libp2p-kad`: Fixed potential panic on computing record expiry. - [PR 1492](https://github.com/libp2p/rust-libp2p/pull/1492) - -- `libp2p-mplex`: Guard against use of underlying `Sink` upon error or - connection close. [PR 1529](https://github.com/libp2p/rust-libp2p/pull/1529) - -- `multistream-select`: Upgrade to stable futures. - [PR 1484](https://github.com/libp2p/rust-libp2p/pull/1484) - -- `multihash`: Removed the crate in favour of the upstream crate. - [PR 1472](https://github.com/libp2p/rust-libp2p/pull/1472) +- `libp2p-core`: Finished "identity hashing" for peer IDs migration. + [PR 1460](https://github.com/libp2p/rust-libp2p/pull/1460) +- `libp2p-core`: Remove `poll_broadcast`. + [PR 1527](https://github.com/libp2p/rust-libp2p/pull/1527) +- `libp2p-core`, `libp2p-swarm`: Report addresses of closed listeners. + [PR 1485](https://github.com/libp2p/rust-libp2p/pull/1485) +- `libp2p-core`: Support for multiple connections per peer and configurable connection limits. + See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), + [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and + [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. + +- `libp2p-swarm`: Pass the cause of closing a listener to `inject_listener_closed`. + [PR 1517](https://github.com/libp2p/rust-libp2p/pull/1517) +- `libp2p-swarm`: Support for multiple connections per peer and configurable connection limits. + See [PR #1440](https://github.com/libp2p/rust-libp2p/pull/1440), + [PR #1519](https://github.com/libp2p/rust-libp2p/pull/1519) and + [issue #912](https://github.com/libp2p/rust-libp2p/issues/912) for details. +- `libp2p-swarm`: The `SwarmEvent` now returns more events. + [PR 1515](https://github.com/libp2p/rust-libp2p/pull/1515) +- `libp2p-swarm`: New `protocols_handler::multi` module. + [PR 1497](https://github.com/libp2p/rust-libp2p/pull/1497) +- `libp2p-swarm`: Allow configuration of outbound substreams. + [PR 1521](https://github.com/libp2p/rust-libp2p/pull/1521) + +- `libp2p-kad`: Providers returned from a lookup are now deduplicated. + [PR 1528](https://github.com/libp2p/rust-libp2p/pull/1528) +- `libp2p-kad`: Allow customising the maximum packet size. + [PR 1502](https://github.com/libp2p/rust-libp2p/pull/1502) +- `libp2p-kad`: Allow customising the (libp2p) connection keep-alive timeout. + [PR 1477](https://github.com/libp2p/rust-libp2p/pull/1477) +- `libp2p-kad`: Avoid storing records that are expired upon receipt (optimisation). + [PR 1496](https://github.com/libp2p/rust-libp2p/pull/1496) +- `libp2p-kad`: Fixed potential panic on computing record expiry. + [PR 1492](https://github.com/libp2p/rust-libp2p/pull/1492) + +- `libp2p-mplex`: Guard against use of underlying `Sink` upon + error or connection close. + [PR 1529](https://github.com/libp2p/rust-libp2p/pull/1529) + +- `multistream-select`: Upgrade to stable futures. + [PR 1484](https://github.com/libp2p/rust-libp2p/pull/1484) + +- `multihash`: Removed the crate in favour of the upstream crate. + [PR 1472](https://github.com/libp2p/rust-libp2p/pull/1472) ## Version 0.16.2 (2020-02-28) -- Fixed yamux connections not properly closing and being stuck in the - `CLOSE_WAIT` state. -- Added a `websocket_transport()` function in `libp2p-wasm-ext`, behind a Cargo - feature. -- Fixed ambiguity in `IntoProtocolsHandler::select` vs - `ProtocolsHandler::select` in the `NetworkBehaviour` custom derive. +- Fixed yamux connections not properly closing and being stuck in the `CLOSE_WAIT` state. +- Added a `websocket_transport()` function in `libp2p-wasm-ext`, behind a Cargo feature. +- Fixed ambiguity in `IntoProtocolsHandler::select` vs `ProtocolsHandler::select` in the `NetworkBehaviour` custom derive. ## Version 0.16.1 (2020-02-18) -- Fixed wrong representation of `PeerId`s being used in - `Kademlia::get_closest_peers`. -- Implemented `FusedStream` for `Swarm`. +- Fixed wrong representation of `PeerId`s being used in `Kademlia::get_closest_peers`. +- Implemented `FusedStream` for `Swarm`. ## Version 0.16.0 (2020-02-13) -- Removed the `Substream` associated type from the `ProtocolsHandler` trait. The - type of the substream is now always `libp2p::swarm::NegotiatedSubstream`. -- As a consequence of the previous change, most of the implementations of the - `NetworkBehaviour` trait provided by libp2p (`Ping`, `Identify`, `Kademlia`, - `Floodsub`, `Gossipsub`) have lost a generic parameter. -- Removed the first generic parameter (the transport) from `Swarm` and - `ExpandedSwarm`. The transport is now abstracted away in the internals of the - swarm. -- The `Send` and `'static` bounds are now enforced directly on the - `ProtocolsHandler` trait and its associated `InboundUpgrade` and - `OutboundUpgrade` implementations. -- Modified `PeerId`s to compare equal across the identity and SHA256 hashes. As - a consequence, the `Borrow` implementation of `PeerId` now always returns the - bytes representation of a multihash with a SHA256 hash. -- Modified libp2p-floodsub to no longer hash the topic. The new behaviour is now - compatible with go-libp2p and js-libp2p, but is a breaking change with regards - to rust-libp2p. -- Added libp2p-pnet. It makes it possible to protect networks with a pre-shared - key (PSK). -- Modified the `poll_method` parameter of the `NetworkBehaviour` custom derive. - The expected method now takes an additional parameter of type - `impl PollParameters` to be consistent with the `NetworkBehaviour::poll` - method. -- libp2p-noise now compiles for WASM targets. -- Changed libp2p-noise to grow its memory buffers dynamically. This should - reduce the overall memory usage of connections that use the noise encryption. -- Fixed libp2p-gossipsub to no longer close the connection if the inbound - substream is closed by the remote. -- All crates prefixed with `libp2p-` now use the same version number. -- Added a new variant `ListenerEvent::Error` for listeners to report non-fatal - errors. `libp2p-tcp` uses this variant to report errors that happen on remote - sockets before they have been accepted and errors when trying to determine the - local machine's IP address. +- Removed the `Substream` associated type from the `ProtocolsHandler` trait. The type of the substream is now always `libp2p::swarm::NegotiatedSubstream`. +- As a consequence of the previous change, most of the implementations of the `NetworkBehaviour` trait provided by libp2p (`Ping`, `Identify`, `Kademlia`, `Floodsub`, `Gossipsub`) have lost a generic parameter. +- Removed the first generic parameter (the transport) from `Swarm` and `ExpandedSwarm`. The transport is now abstracted away in the internals of the swarm. +- The `Send` and `'static` bounds are now enforced directly on the `ProtocolsHandler` trait and its associated `InboundUpgrade` and `OutboundUpgrade` implementations. +- Modified `PeerId`s to compare equal across the identity and SHA256 hashes. As a consequence, the `Borrow` implementation of `PeerId` now always returns the bytes representation of a multihash with a SHA256 hash. +- Modified libp2p-floodsub to no longer hash the topic. The new behaviour is now compatible with go-libp2p and js-libp2p, but is a breaking change with regards to rust-libp2p. +- Added libp2p-pnet. It makes it possible to protect networks with a pre-shared key (PSK). +- Modified the `poll_method` parameter of the `NetworkBehaviour` custom derive. The expected method now takes an additional parameter of type `impl PollParameters` to be consistent with the `NetworkBehaviour::poll` method. +- libp2p-noise now compiles for WASM targets. +- Changed libp2p-noise to grow its memory buffers dynamically. This should reduce the overall memory usage of connections that use the noise encryption. +- Fixed libp2p-gossipsub to no longer close the connection if the inbound substream is closed by the remote. +- All crates prefixed with `libp2p-` now use the same version number. +- Added a new variant `ListenerEvent::Error` for listeners to report non-fatal errors. `libp2p-tcp` uses this variant to report errors that happen on remote sockets before they have been accepted and errors when trying to determine the local machine's IP address. ## Version 0.15.0 (2020-01-24) -- Added `libp2p-gossipsub`. -- Added `SwarmBuilder::executor` to allow configuring which tasks executor to - use. -- Added `TokioTcpConfig` in `libp2p-tcp` and `TokioUdsConfig` in `libp2p-uds` - behind `tokio` features. These structs use `tokio` and require a `tokio` - runtime executor to be configured via `SwarmBuilder::executor`. -- Changed the `OutboundUpgrade` and `InboundUpgrade` traits to no longer be - passed a `Negotiated` but just a `C`. The `Negotiated` is now in the trait - bounds requirements of `ProtocolsHandler`. -- Fixed `libp2p-wasm-ext` returning `Err(WouldBlock)` rather than `Pending`. -- Fixed `libp2p-dns` not segregating DNS4 and DNS6. -- Removed some unnecessary `Unpin` requirements on futures. -- Changed `Mdns::new` to no longer be `async`. -- Fixed `libp2p-kad` keeping connections alive when it shouldn't. -- Fixed `InboundUpgrade` not always properly implemented on `NoiseConfig`. +- Added `libp2p-gossipsub`. +- Added `SwarmBuilder::executor` to allow configuring which tasks executor to use. +- Added `TokioTcpConfig` in `libp2p-tcp` and `TokioUdsConfig` in `libp2p-uds` behind `tokio` features. These structs use `tokio` and require a `tokio` runtime executor to be configured via `SwarmBuilder::executor`. +- Changed the `OutboundUpgrade` and `InboundUpgrade` traits to no longer be passed a `Negotiated` but just a `C`. The `Negotiated` is now in the trait bounds requirements of `ProtocolsHandler`. +- Fixed `libp2p-wasm-ext` returning `Err(WouldBlock)` rather than `Pending`. +- Fixed `libp2p-dns` not segregating DNS4 and DNS6. +- Removed some unnecessary `Unpin` requirements on futures. +- Changed `Mdns::new` to no longer be `async`. +- Fixed `libp2p-kad` keeping connections alive when it shouldn't. +- Fixed `InboundUpgrade` not always properly implemented on `NoiseConfig`. ## Version 0.14.0-alpha.1 (2020-01-07) -- Upgraded the crate to stable futures. -- Use varints instead of fixed sized (4 byte) integers to delimit plaintext 2.0 - messages to align implementation with the specification. -- Refactored the `core::upgrade` module to provide async functions. -- Changed the `Stream` trait implementation of `Swarm` to no longer return a - `Result`. -- Added the `Swarm::next` and `Swarm::next_event` functions and the `SwarmEvent` - enum. -- Changed `ProtocolsHandler::poll` to no longer return an error. Instead, - `ProtocolsHandlerEvent` has a new `Close` variant which corresponds to what an - error represented before. -- Changed all the traits that have a `poll` function (i.e. `NetworkBehaviour`, - `ProtocolsHandler`, `NodeHandler`) to have an additional `&mut Context` - parameter, to reflect the changes in the `Future` trait. -- Revamped the API of `libp2p_websockets::framed`. -- Added protocol string to `Error::UnknownProtocolString`. +- Upgraded the crate to stable futures. +- Use varints instead of fixed sized (4 byte) integers to delimit plaintext 2.0 messages to align implementation with the specification. +- Refactored the `core::upgrade` module to provide async functions. +- Changed the `Stream` trait implementation of `Swarm` to no longer return a `Result`. +- Added the `Swarm::next` and `Swarm::next_event` functions and the `SwarmEvent` enum. +- Changed `ProtocolsHandler::poll` to no longer return an error. Instead, `ProtocolsHandlerEvent` has a new `Close` variant which corresponds to what an error represented before. +- Changed all the traits that have a `poll` function (i.e. `NetworkBehaviour`, `ProtocolsHandler`, `NodeHandler`) to have an additional `&mut Context` parameter, to reflect the changes in the `Future` trait. +- Revamped the API of `libp2p_websockets::framed`. +- Added protocol string to `Error::UnknownProtocolString`. ## Version 0.13.2 (2020-01-02) -- Fixed the `libp2p-noise` handshake not flushing the underlying stream before - waiting for a response. -- Fixed semver issue with the `protobuf` crate. +- Fixed the `libp2p-noise` handshake not flushing the underlying stream before waiting for a response. +- Fixed semver issue with the `protobuf` crate. ## Version 0.13.1 (2019-11-13) -- Maintenance release to bump dependencies and deal with an accidental breaking - change in multihash 0.1.4. +- Maintenance release to bump dependencies and deal with an accidental breaking change in multihash 0.1.4. ## Version 0.13.0 (2019-11-05) -- Reworked the transport upgrade API. See - https://github.com/libp2p/rust-libp2p/pull/1240 for more information. -- Added a parameter allowing to choose the protocol negotiation protocol when - upgrading a connection or a substream. See - https://github.com/libp2p/rust-libp2p/pull/1245 for more information. -- Added an alternative `multistream-select` protocol called `V1Lazy`. -- Added `PlainText2Config` that implements the `/plaintext/2.0.0` protocol. -- Refactored `libp2p-identify`. Some items have been renamed. -- Now accepting `PeerId`s using the `identity` hashing algorithm as valid. -- Removed `libp2p-observed` and `libp2p-ratelimit`. -- Fixed mDNS long peer IDs not being transmitted properly. -- Added some `Debug` trait implementations. -- Fixed potential arithmetic overflows in `libp2p-kad` and `multistream-select`. +- Reworked the transport upgrade API. See https://github.com/libp2p/rust-libp2p/pull/1240 for more information. +- Added a parameter allowing to choose the protocol negotiation protocol when upgrading a connection or a substream. See https://github.com/libp2p/rust-libp2p/pull/1245 for more information. +- Added an alternative `multistream-select` protocol called `V1Lazy`. +- Added `PlainText2Config` that implements the `/plaintext/2.0.0` protocol. +- Refactored `libp2p-identify`. Some items have been renamed. +- Now accepting `PeerId`s using the `identity` hashing algorithm as valid. +- Removed `libp2p-observed` and `libp2p-ratelimit`. +- Fixed mDNS long peer IDs not being transmitted properly. +- Added some `Debug` trait implementations. +- Fixed potential arithmetic overflows in `libp2p-kad` and `multistream-select`. ## Version 0.12.0 (2019-08-15) -- In some situations, `multistream-select` will now assume that protocol - negotiation immediately succeeds. If it turns out that it failed, an error is - generated when reading or writing from/to the stream. -- Replaced `listen_addr` with `local_addr` in events related to incoming - connections. The address no longer has to match a previously-reported address. -- Listeners now have an identifier and can be stopped. -- Added `NetworkBehaviour::inject_listener_error` and - `NetworkBehaviour::inject_listener_closed`. For diagnostic purposes, listeners - can now report errors on incoming connections, such as when calling - `accept(2)` fails. -- Fixed tasks sometimes not being notified when a network event happens in - `libp2p-mplex`. -- Fixed a memory leak in `libp2p-kad`. -- Added `Toggle::is_enabled()`. -- Removed `IdentifyTransport`. +- In some situations, `multistream-select` will now assume that protocol negotiation immediately succeeds. If it turns out that it failed, an error is generated when reading or writing from/to the stream. +- Replaced `listen_addr` with `local_addr` in events related to incoming connections. The address no longer has to match a previously-reported address. +- Listeners now have an identifier and can be stopped. +- Added `NetworkBehaviour::inject_listener_error` and `NetworkBehaviour::inject_listener_closed`. For diagnostic purposes, listeners can now report errors on incoming connections, such as when calling `accept(2)` fails. +- Fixed tasks sometimes not being notified when a network event happens in `libp2p-mplex`. +- Fixed a memory leak in `libp2p-kad`. +- Added `Toggle::is_enabled()`. +- Removed `IdentifyTransport`. ## Version 0.11.0 (2019-07-18) -- `libp2p-kad`: Completed the core functionality of the record storage API, - thereby extending the `RecordStore` for provider records. All records expire - by default and are subject to regular republication and caching as per the - Kademlia spec(s). Expiration and publication intervals are configurable - through the `KademliaConfig`. -- `libp2p-kad`: The routing table now never stores peers without a known - (listen) address. In particular, on receiving a new inbound connection, the - Kademlia behaviour now emits `KademliaEvent::UnroutablePeer` to indicate that - in order for the peer to be added to the routing table and hence considered a - reachable node in the DHT, a listen address of the peer must be discovered and - reported via `Kademlia::add_address`. This is usually achieved through the use - of the `Identify` protocol on the same connection(s). -- `libp2p-kad`: Documentation updates. -- Extracted the `swarm` and `protocols_handler`-related contents from - `libp2p-core` to a new `libp2p-swarm` crate. -- Renamed `RawSwarm` to `Network`. -- Added `Floodsub::publish_any`. -- Replaced unbounded channels with bounded ones at the boundary between the - `Network` (formerly `RawSwarm`) and `NodeHandler`. The node handlers will now - wait if the main task is busy, instead of continuing to push events to the - channel. -- Fixed the `address_translation` function ignoring `/dns` addresses. +- `libp2p-kad`: Completed the core functionality of the record storage API, thereby extending the `RecordStore` for provider records. All records expire by default and are subject to regular republication and caching as per the Kademlia spec(s). Expiration and publication intervals are configurable through the `KademliaConfig`. +- `libp2p-kad`: The routing table now never stores peers without a known (listen) address. In particular, on receiving a new inbound connection, the Kademlia behaviour now emits `KademliaEvent::UnroutablePeer` to indicate that in order for the peer to be added to the routing table and hence considered a reachable node in the DHT, a listen address of the peer must be discovered and reported via `Kademlia::add_address`. This is usually achieved through the use of the `Identify` protocol on the same connection(s). +- `libp2p-kad`: Documentation updates. +- Extracted the `swarm` and `protocols_handler`-related contents from `libp2p-core` to a new `libp2p-swarm` crate. +- Renamed `RawSwarm` to `Network`. +- Added `Floodsub::publish_any`. +- Replaced unbounded channels with bounded ones at the boundary between the `Network` (formerly `RawSwarm`) and `NodeHandler`. The node handlers will now wait if the main task is busy, instead of continuing to push events to the channel. +- Fixed the `address_translation` function ignoring `/dns` addresses. ## Version 0.10.0 (2019-06-25) -- `PollParameters` is now a trait instead of a struct. -- The `Swarm` can now be customized with connection information. -- Fixed write-only substreams now delivering data properly. -- Fixed the TCP listener accidentally shutting down if an incoming socket was - closed too quickly. -- Improved the heuristics for determining external multiaddresses based on - reports. -- Various fixes to Kademlia iterative queries and the WebSockets transport. +- `PollParameters` is now a trait instead of a struct. +- The `Swarm` can now be customized with connection information. +- Fixed write-only substreams now delivering data properly. +- Fixed the TCP listener accidentally shutting down if an incoming socket was closed too quickly. +- Improved the heuristics for determining external multiaddresses based on reports. +- Various fixes to Kademlia iterative queries and the WebSockets transport. ## Version 0.9.1 (2019-06-05) -- `EitherOutput` now implements `Stream` and `Sink` if their variants also - implement these traits. -- `libp2p::websocket::error::Error` now implements `Sync`. +- `EitherOutput` now implements `Stream` and `Sink` if their variants also implement these traits. +- `libp2p::websocket::error::Error` now implements `Sync`. ## Version 0.9.0 (2019-06-04) -- Major fixes and performance improvements to libp2p-kad. -- Initial prototype for record storage in libp2p-kad. -- Rewrote the implementation of WebSockets. It now properly supports WebSockets - Secure (WSS). -- Removed `BrowserWsConfig`. Please use `libp2p::wasm_ext::ExtTransport` - instead. -- Added a `Path` parameter to `multiaddr::Protocol::WS` and `WSS`. The string - representation when a path is present is respectively `x-parity-ws/` and - `x-parity-wss/` where `` is percent-encoded. -- Fixed an issue with `libp2p-tcp` where the wrong listened address was - returned, if the actual address was loopback. -- Added `core::upgrade::OptionalUpgrade`. -- Added some utility functions in `core::identity::secp256k1`. -- It is now possible to inject an artificial connection in the `RawSwarm`. +- Major fixes and performance improvements to libp2p-kad. +- Initial prototype for record storage in libp2p-kad. +- Rewrote the implementation of WebSockets. It now properly supports WebSockets Secure (WSS). +- Removed `BrowserWsConfig`. Please use `libp2p::wasm_ext::ExtTransport` instead. +- Added a `Path` parameter to `multiaddr::Protocol::WS` and `WSS`. The string representation when a path is present is respectively `x-parity-ws/` and `x-parity-wss/` where `` is percent-encoded. +- Fixed an issue with `libp2p-tcp` where the wrong listened address was returned, if the actual address was loopback. +- Added `core::upgrade::OptionalUpgrade`. +- Added some utility functions in `core::identity::secp256k1`. +- It is now possible to inject an artificial connection in the `RawSwarm`. ## Version 0.8.1 (2019-05-15) -- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. +- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. ## Version 0.8.0 (2019-05-15) -- Crate now successfully runs from within the browser when compiled to WASM. -- Modified the constructors of `NoiseConfig` to accept any type of public key. - The Noise handshake has consequently been modified. -- Changed the `StreamMuxer` trait to have an `Error` associated type. -- The `Swarm` now ranks externally-visible multiaddresses by how often they have - been reported, ensuring that weird or malicious reports don't affect - connectivity too much. -- Added `IntoProtocolsHandler::inbound_protocol`. Must return the same value as - what `ProtocolsHandler::listen_protocol` would return. -- `IntoProtocolsHandler::into_handler` now takes a second parameter with the - `&ConnectedPoint` to the node we are connected to. -- Replaced the `secp256k1` crate with `libsecp256k1`. -- Fixed `Kademlia::add_providing` taking a `PeerId` instead of a `Multihash`. -- Fixed various bugs in the implementation of `Kademlia`. -- Added `OneSubstreamMuxer`. -- Added the `libp2p-wasm-ext` crate. -- Added `multiaddr::from_url`. -- Added `OptionalTransport`. +- Crate now successfully runs from within the browser when compiled to WASM. +- Modified the constructors of `NoiseConfig` to accept any type of public key. The Noise handshake has consequently been modified. +- Changed the `StreamMuxer` trait to have an `Error` associated type. +- The `Swarm` now ranks externally-visible multiaddresses by how often they have been reported, ensuring that weird or malicious reports don't affect connectivity too much. +- Added `IntoProtocolsHandler::inbound_protocol`. Must return the same value as what `ProtocolsHandler::listen_protocol` would return. +- `IntoProtocolsHandler::into_handler` now takes a second parameter with the `&ConnectedPoint` to the node we are connected to. +- Replaced the `secp256k1` crate with `libsecp256k1`. +- Fixed `Kademlia::add_providing` taking a `PeerId` instead of a `Multihash`. +- Fixed various bugs in the implementation of `Kademlia`. +- Added `OneSubstreamMuxer`. +- Added the `libp2p-wasm-ext` crate. +- Added `multiaddr::from_url`. +- Added `OptionalTransport`. ## Version 0.7.1 (2019-05-15) -- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. +- Fixed a vulnerability in ED25519 signatures verification in libp2p-core. ## Version 0.7.0 (2019-04-23) -- Fixed the inactive connections shutdown mechanism not working. -- `Transport::listen_on` must now return a `Stream` that produces - `ListenEvent`s. This makes it possible to notify about listened addresses at a - later point in time. -- `Transport::listen_on` no longer returns an address we're listening on. This - is done through `ListenEvent`s. All other `listen_on` methods have been - updated accordingly. -- Added `NetworkBehaviour::inject_new_listen_addr`, - `NetworkBehaviour::inject_expired_listen_addr` and - `NetworkBehaviour::inject_new_external_addr`. -- `ProtocolsHandler::listen_protocol` and - `ProtocolsHandlerEvent::OutboundSubstreamRequest` must now return a - `SubstreamProtocol` struct containing a timeout for the upgrade. -- `Ping::new` now requires a `PingConfig`, which can be created with - `PingConfig::new`. -- Removed `Transport::nat_traversal` in favour of a stand-alone - `address_translation` function in `libp2p-core`. -- Reworked the API of `Multiaddr`. -- Removed the `ToMultiaddr` trait in favour of `TryFrom`. -- Added `Swarm::ban_peer_id` and `Swarm::unban_peer_id`. -- The `TPeerId` generic parameter of `RawSwarm` is now `TConnInfo` and must now - implement a `ConnectionInfo` trait. -- Reworked the `PingEvent`. -- Renamed `KeepAlive::Forever` to `Yes` and `KeepAlive::Now` to `No`. +- Fixed the inactive connections shutdown mechanism not working. +- `Transport::listen_on` must now return a `Stream` that produces `ListenEvent`s. This makes it possible to notify about listened addresses at a later point in time. +- `Transport::listen_on` no longer returns an address we're listening on. This is done through `ListenEvent`s. All other `listen_on` methods have been updated accordingly. +- Added `NetworkBehaviour::inject_new_listen_addr`, `NetworkBehaviour::inject_expired_listen_addr` and `NetworkBehaviour::inject_new_external_addr`. +- `ProtocolsHandler::listen_protocol` and `ProtocolsHandlerEvent::OutboundSubstreamRequest` must now return a `SubstreamProtocol` struct containing a timeout for the upgrade. +- `Ping::new` now requires a `PingConfig`, which can be created with `PingConfig::new`. +- Removed `Transport::nat_traversal` in favour of a stand-alone `address_translation` function in `libp2p-core`. +- Reworked the API of `Multiaddr`. +- Removed the `ToMultiaddr` trait in favour of `TryFrom`. +- Added `Swarm::ban_peer_id` and `Swarm::unban_peer_id`. +- The `TPeerId` generic parameter of `RawSwarm` is now `TConnInfo` and must now implement a `ConnectionInfo` trait. +- Reworked the `PingEvent`. +- Renamed `KeepAlive::Forever` to `Yes` and `KeepAlive::Now` to `No`. ## Version 0.6.0 (2019-03-29) -- Replaced `NetworkBehaviour::inject_dial_failure` with `inject_dial_failure` - and `inject_addr_reach_failure`. The former is called when we have finished - trying to dial a node without success, while the latter is called when we have - failed to reach a specific address. -- Fixed Kademlia storing a different hash than the reference implementation. -- Lots of bugfixes in Kademlia. -- Modified the `InboundUpgrade` and `OutboundUpgrade` trait to take a - `Negotiated` instead of `TSocket`. -- `PollParameters::external_addresses` now returns `Multiaddr`es as reference - instead of by value. -- Added `Swarm::external_addresses`. -- Added a `core::swarm::toggle::Toggle` that allows having a disabled - `NetworkBehaviour`. +- Replaced `NetworkBehaviour::inject_dial_failure` with `inject_dial_failure` and + `inject_addr_reach_failure`. The former is called when we have finished trying to dial a node + without success, while the latter is called when we have failed to reach a specific address. +- Fixed Kademlia storing a different hash than the reference implementation. +- Lots of bugfixes in Kademlia. +- Modified the `InboundUpgrade` and `OutboundUpgrade` trait to take a `Negotiated` instead + of `TSocket`. +- `PollParameters::external_addresses` now returns `Multiaddr`es as reference instead of by value. +- Added `Swarm::external_addresses`. +- Added a `core::swarm::toggle::Toggle` that allows having a disabled `NetworkBehaviour`. ## Version 0.5.0 (2019-03-13) -- Moved the `SecioKeypair` struct in `core/identity` and renamed it to - `Keypair`. -- mplex now supports half-closed substreams. -- Renamed `StreamMuxer::shutdown()` to `close()`. -- Closing a muxer with the `close()` method (formerly `shutdown`) now "destroys" - all the existing substreams. After `close()` as been called, they all return - either EOF or an error. -- The `shutdown_substream()` method now closes only the writing side of the - substream, and you can continue reading from it until EOF or until you delete - it. This was actually already more or less the case before, but it wasn't - properly reflected in the API or the documentation. -- `poll_inbound()` and `poll_outbound()` no longer return an `Option`, as `None` - was the same as returning an error. -- Removed the `NodeClosed` events and renamed `NodeError` to `NodeClosed`. From - the API's point of view, a connection now always closes with an error. -- Added the `NodeHandlerWrapperError` enum that describes an error generated by - the protocols handlers grouped together. It is either `UselessTimeout` or - `Handler`. This allows properly reporting closing a connection because it is - useless. -- Removed `NodeHandler::inject_inbound_closed`, - `NodeHandler::inject_outbound_closed`, `NodeHandler::shutdown`, and - `ProtocolsHandler::shutdown`. The handler is now dropped when a shutdown - process starts. This should greatly simplify writing a handler. -- `StreamMuxer::close` now implies `flush_all`. -- Removed the `Shutdown` enum from `stream_muxer`. -- Removed `ProtocolsHandler::fuse()`. -- Reworked some API of `core/nodes/node.rs` and `core/nodes/handled_node.rs`. -- The core now works even outside of a tokio context. +- Moved the `SecioKeypair` struct in `core/identity` and renamed it to `Keypair`. +- mplex now supports half-closed substreams. +- Renamed `StreamMuxer::shutdown()` to `close()`. +- Closing a muxer with the `close()` method (formerly `shutdown`) now "destroys" all the existing substreams. After `close()` as been called, they all return either EOF or an error. +- The `shutdown_substream()` method now closes only the writing side of the substream, and you can continue reading from it until EOF or until you delete it. This was actually already more or less the case before, but it wasn't properly reflected in the API or the documentation. +- `poll_inbound()` and `poll_outbound()` no longer return an `Option`, as `None` was the same as returning an error. +- Removed the `NodeClosed` events and renamed `NodeError` to `NodeClosed`. From the API's point of view, a connection now always closes with an error. +- Added the `NodeHandlerWrapperError` enum that describes an error generated by the protocols handlers grouped together. It is either `UselessTimeout` or `Handler`. This allows properly reporting closing a connection because it is useless. +- Removed `NodeHandler::inject_inbound_closed`, `NodeHandler::inject_outbound_closed`, `NodeHandler::shutdown`, and `ProtocolsHandler::shutdown`. The handler is now dropped when a shutdown process starts. This should greatly simplify writing a handler. +- `StreamMuxer::close` now implies `flush_all`. +- Removed the `Shutdown` enum from `stream_muxer`. +- Removed `ProtocolsHandler::fuse()`. +- Reworked some API of `core/nodes/node.rs` and `core/nodes/handled_node.rs`. +- The core now works even outside of a tokio context. ## Version 0.4.2 (2019-02-27) -- Fixed periodic pinging not working. +- Fixed periodic pinging not working. ## Version 0.4.1 (2019-02-20) -- Fixed wrong version of libp2p-noise. +- Fixed wrong version of libp2p-noise. ## Version 0.4.0 (2019-02-20) -- The `multiaddr!` macro has been moved to the `multiaddr` crate and is now - reexported under the name `build_multiaddr!`. -- Modified the functions in `upgrade::transfer` to be more convenient to use. -- Now properly sending external addresses in the identify protocol. -- Fixed duplicate addresses being reported in identify and Kademlia. -- Fixed infinite looping in the functions in `upgrade::transfer`. -- Fixed infinite loop on graceful node shutdown with the - `ProtocolsHandlerSelect`. -- Fixed various issues with nodes dialing each other simultaneously. -- Added the `StreamMuxer::is_remote_acknowledged()` method. -- Added a `BandwidthLogging` transport wrapper that logs the bandwidth - consumption. -- The addresses to try dialing when dialing a node is now refreshed by the - `Swarm` when necessary. -- Lots of modifications to the semi-private structs in `core/nodes`. -- Added `IdentifyEvent::SendBack`, when we send back our information. -- Rewrote the `MemoryTransport` to be similar to the `TcpConfig`. +- The `multiaddr!` macro has been moved to the `multiaddr` crate and is now reexported under the name `build_multiaddr!`. +- Modified the functions in `upgrade::transfer` to be more convenient to use. +- Now properly sending external addresses in the identify protocol. +- Fixed duplicate addresses being reported in identify and Kademlia. +- Fixed infinite looping in the functions in `upgrade::transfer`. +- Fixed infinite loop on graceful node shutdown with the `ProtocolsHandlerSelect`. +- Fixed various issues with nodes dialing each other simultaneously. +- Added the `StreamMuxer::is_remote_acknowledged()` method. +- Added a `BandwidthLogging` transport wrapper that logs the bandwidth consumption. +- The addresses to try dialing when dialing a node is now refreshed by the `Swarm` when necessary. +- Lots of modifications to the semi-private structs in `core/nodes`. +- Added `IdentifyEvent::SendBack`, when we send back our information. +- Rewrote the `MemoryTransport` to be similar to the `TcpConfig`. ## Version 0.3.1 (2019-02-02) -- Added `NetworkBehaviour::inject_replaced` that is called whenever we replace a - connection with a different connection to the same peer. -- Fixed various issues with Kademlia. +- Added `NetworkBehaviour::inject_replaced` that is called whenever we replace a connection with a different connection to the same peer. +- Fixed various issues with Kademlia. ## Version 0.3.0 (2019-01-30) -- Removed the `topology` module and everything it contained, including the - `Topology` trait. -- Added `libp2p-noise` that supports Noise handshakes, as an alternative to - `libp2p-secio`. -- Updated `ring` to version 0.14. -- Creating a `Swarm` now expects the `PeerId` of the local node, instead of a - `Topology`. -- Added `NetworkBehaviour::addresses_of_peer` that returns the addresses a - `NetworkBehaviour` knows about a given peer. This exists as a replacement for - the topology. -- The `Kademlia` and `Mdns` behaviours now report and store the list of - addresses they discover. -- You must now call `Floodsub::add_node_to_partial_view()` and - `Floodsub::remove_node_from_partial_view` to add/remove nodes from the list of - nodes that floodsub must send messages to. -- Added `NetworkBehaviour::inject_dial_failure` that is called when we fail to - dial an address. -- `ProtocolsHandler::connection_keep_alive()` now returns a `KeepAlive` enum - that provides more fine grained control. -- The `NodeHandlerWrapper` no longer has a 5 seconds inactivity timeout. This is - now handled entirely by `ProtocolsHandler::connection_keep_alive()`. -- Now properly denying connections incoming from the same `PeerId` as ours. -- Added a `SwarmBuilder`. The `incoming_limit` method lets you configure the - number of simultaneous incoming connections. -- Removed `FloodsubHandler`, `PingListenHandler` and `PeriodicPingHandler`. -- The structs in `core::nodes` are now generic over the `PeerId`. -- Added `SecioKeypair::ed25519_raw_key()`. -- Fix improper connection shutdown in `ProtocolsHandler`. +- Removed the `topology` module and everything it contained, including the `Topology` trait. +- Added `libp2p-noise` that supports Noise handshakes, as an alternative to `libp2p-secio`. +- Updated `ring` to version 0.14. +- Creating a `Swarm` now expects the `PeerId` of the local node, instead of a `Topology`. +- Added `NetworkBehaviour::addresses_of_peer` that returns the addresses a `NetworkBehaviour` knows about a given peer. This exists as a replacement for the topology. +- The `Kademlia` and `Mdns` behaviours now report and store the list of addresses they discover. +- You must now call `Floodsub::add_node_to_partial_view()` and `Floodsub::remove_node_from_partial_view` to add/remove nodes from the list of nodes that floodsub must send messages to. +- Added `NetworkBehaviour::inject_dial_failure` that is called when we fail to dial an address. +- `ProtocolsHandler::connection_keep_alive()` now returns a `KeepAlive` enum that provides more fine grained control. +- The `NodeHandlerWrapper` no longer has a 5 seconds inactivity timeout. This is now handled entirely by `ProtocolsHandler::connection_keep_alive()`. +- Now properly denying connections incoming from the same `PeerId` as ours. +- Added a `SwarmBuilder`. The `incoming_limit` method lets you configure the number of simultaneous incoming connections. +- Removed `FloodsubHandler`, `PingListenHandler` and `PeriodicPingHandler`. +- The structs in `core::nodes` are now generic over the `PeerId`. +- Added `SecioKeypair::ed25519_raw_key()`. +- Fix improper connection shutdown in `ProtocolsHandler`. ## Version 0.2.2 (2019-01-14) -- Fixed improper dependencies versions causing deriving `NetworkBehaviour` to - generate an error. +- Fixed improper dependencies versions causing deriving `NetworkBehaviour` to generate an error. ## Version 0.2.1 (2019-01-14) -- Added the `IntoNodeHandler` and `IntoProtocolsHandler` traits, allowing node - handlers and protocol handlers to know the `PeerId` of the node they are - interacting with. +- Added the `IntoNodeHandler` and `IntoProtocolsHandler` traits, allowing node handlers and protocol handlers to know the `PeerId` of the node they are interacting with. ## Version 0.2 (2019-01-10) -- The `Transport` trait now has an `Error` associated type instead of always - using `std::io::Error`. -- Merged `PeriodicPing` and `PingListen` into one `Ping` behaviour. -- `Floodsub` now generates `FloodsubEvent`s instead of direct floodsub messages. -- Added `ProtocolsHandler::connection_keep_alive`. If all the handlers return - `false`, then the connection to the remote node will automatically be - gracefully closed after a few seconds. -- The crate now successfully compiles for the `wasm32-unknown-unknown` target. -- Updated `ring` to version 0.13. -- Updated `secp256k1` to version 0.12. -- The enum returned by `RawSwarm::peer()` can now return `LocalNode`. This makes - it impossible to accidentally attempt to dial the local node. -- Removed `Transport::map_err_dial`. -- Removed the `Result` from some connection-related methods in the `RawSwarm`, - as they could never error. -- If a node doesn't respond to pings, we now generate an error on the connection - instead of trying to gracefully close it. +- The `Transport` trait now has an `Error` associated type instead of always using `std::io::Error`. +- Merged `PeriodicPing` and `PingListen` into one `Ping` behaviour. +- `Floodsub` now generates `FloodsubEvent`s instead of direct floodsub messages. +- Added `ProtocolsHandler::connection_keep_alive`. If all the handlers return `false`, then the connection to the remote node will automatically be gracefully closed after a few seconds. +- The crate now successfully compiles for the `wasm32-unknown-unknown` target. +- Updated `ring` to version 0.13. +- Updated `secp256k1` to version 0.12. +- The enum returned by `RawSwarm::peer()` can now return `LocalNode`. This makes it impossible to accidentally attempt to dial the local node. +- Removed `Transport::map_err_dial`. +- Removed the `Result` from some connection-related methods in the `RawSwarm`, as they could never error. +- If a node doesn't respond to pings, we now generate an error on the connection instead of trying to gracefully close it. From 3359e60473195e8f5c277e9d2d168720f93cebd6 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:43:36 +0900 Subject: [PATCH 161/179] Correct bad merge --- transports/dns/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index 1f56d43021a..7d92cc8ecfc 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -235,7 +235,7 @@ where addr: Multiaddr, dial_opts: DialOpts, ) -> Result> { - self.do_dial(addr, dial_opts) + Ok(self.do_dial(addr, dial_opts)) } fn poll( @@ -262,10 +262,7 @@ where &mut self, addr: Multiaddr, dial_opts: DialOpts, - ) -> Result< - ::Dial, - TransportError<::Error>, - > { + ) -> ::Dial { let resolver = self.resolver.clone(); let inner = self.inner.clone(); From fc14ba877ed71da4960ae7eeccfa857347437a38 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:47:21 +0900 Subject: [PATCH 162/179] Delete unused files --- protocols/autonat/src/behaviour.rs | 689 ------------------ protocols/autonat/src/behaviour/as_client.rs | 361 --------- protocols/autonat/src/behaviour/as_server.rs | 427 ----------- protocols/autonat/src/generated/mod.rs | 2 - protocols/autonat/src/generated/structs.proto | 37 - protocols/autonat/src/generated/structs.rs | 242 ------ protocols/autonat/src/protocol.rs | 343 --------- 7 files changed, 2101 deletions(-) delete mode 100644 protocols/autonat/src/behaviour.rs delete mode 100644 protocols/autonat/src/behaviour/as_client.rs delete mode 100644 protocols/autonat/src/behaviour/as_server.rs delete mode 100644 protocols/autonat/src/generated/mod.rs delete mode 100644 protocols/autonat/src/generated/structs.proto delete mode 100644 protocols/autonat/src/generated/structs.rs delete mode 100644 protocols/autonat/src/protocol.rs diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs deleted file mode 100644 index 64bebfb6233..00000000000 --- a/protocols/autonat/src/behaviour.rs +++ /dev/null @@ -1,689 +0,0 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -mod as_client; -mod as_server; - -use crate::protocol::{AutoNatCodec, DialRequest, DialResponse, ResponseError}; -use crate::DEFAULT_PROTOCOL_NAME; -use as_client::AsClient; -pub use as_client::{OutboundProbeError, OutboundProbeEvent}; -use as_server::AsServer; -pub use as_server::{InboundProbeError, InboundProbeEvent}; -use futures_timer::Delay; -use libp2p_core::transport::PortUse; -use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_request_response::{ - self as request_response, InboundRequestId, OutboundRequestId, ProtocolSupport, ResponseChannel, -}; -use libp2p_swarm::{ - behaviour::{AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}, - ConnectionDenied, ConnectionId, ListenAddresses, NetworkBehaviour, THandler, THandlerInEvent, - THandlerOutEvent, ToSwarm, -}; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - iter, - task::{Context, Poll}, - time::Duration, -}; -use web_time::Instant; - -/// Config for the [`Behaviour`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config { - /// Timeout for requests. - pub timeout: Duration, - - // Client Config - /// Delay on init before starting the fist probe. - pub boot_delay: Duration, - /// Interval in which the NAT should be tested again if max confidence was reached in a status. - pub refresh_interval: Duration, - /// Interval in which the NAT status should be re-tried if it is currently unknown - /// or max confidence was not reached yet. - pub retry_interval: Duration, - /// Throttle period for re-using a peer as server for a dial-request. - pub throttle_server_period: Duration, - /// Use connected peers as servers for probes. - pub use_connected: bool, - /// Max confidence that can be reached in a public / private NAT status. - /// Note: for [`NatStatus::Unknown`] the confidence is always 0. - pub confidence_max: usize, - - // Server Config - /// Max addresses that are tried per peer. - pub max_peer_addresses: usize, - /// Max total dial requests done in `[Config::throttle_clients_period`]. - pub throttle_clients_global_max: usize, - /// Max dial requests done in `[Config::throttle_clients_period`] for a peer. - pub throttle_clients_peer_max: usize, - /// Period for throttling clients requests. - pub throttle_clients_period: Duration, - /// As a server reject probes for clients that are observed at a non-global ip address. - /// Correspondingly as a client only pick peers as server that are not observed at a - /// private ip address. Note that this does not apply for servers that are added via - /// [`Behaviour::add_server`]. - pub only_global_ips: bool, -} - -impl Default for Config { - fn default() -> Self { - Config { - timeout: Duration::from_secs(30), - boot_delay: Duration::from_secs(15), - retry_interval: Duration::from_secs(90), - refresh_interval: Duration::from_secs(15 * 60), - throttle_server_period: Duration::from_secs(90), - use_connected: true, - confidence_max: 3, - max_peer_addresses: 16, - throttle_clients_global_max: 30, - throttle_clients_peer_max: 3, - throttle_clients_period: Duration::from_secs(1), - only_global_ips: true, - } - } -} - -/// Assumed NAT status. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum NatStatus { - Public(Multiaddr), - Private, - Unknown, -} - -impl NatStatus { - pub fn is_public(&self) -> bool { - matches!(self, NatStatus::Public(..)) - } -} - -/// Unique identifier for a probe. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ProbeId(usize); - -impl ProbeId { - fn next(&mut self) -> ProbeId { - let current = *self; - self.0 += 1; - current - } -} - -/// Event produced by [`Behaviour`]. -#[derive(Debug)] -pub enum Event { - /// Event on an inbound probe. - InboundProbe(InboundProbeEvent), - /// Event on an outbound probe. - OutboundProbe(OutboundProbeEvent), - /// The assumed NAT changed. - StatusChanged { - /// Former status. - old: NatStatus, - /// New status. - new: NatStatus, - }, -} - -/// [`NetworkBehaviour`] for AutoNAT. -/// -/// The behaviour frequently runs probes to determine whether the local peer is behind NAT and/ or a firewall, or -/// publicly reachable. -/// In a probe, a dial-back request is sent to a peer that is randomly selected from the list of fixed servers and -/// connected peers. Upon receiving a dial-back request, the remote tries to dial the included addresses. When a -/// first address was successfully dialed, a status Ok will be send back together with the dialed address. If no address -/// can be reached a dial-error is send back. -/// Based on the received response, the sender assumes themselves to be public or private. -/// The status is retried in a frequency of [`Config::retry_interval`] or [`Config::retry_interval`], depending on whether -/// enough confidence in the assumed NAT status was reached or not. -/// The confidence increases each time a probe confirms the assumed status, and decreases if a different status is reported. -/// If the confidence is 0, the status is flipped and the Behaviour will report the new status in an `OutEvent`. -pub struct Behaviour { - // Local peer id - local_peer_id: PeerId, - - // Inner behaviour for sending requests and receiving the response. - inner: request_response::Behaviour, - - config: Config, - - // Additional peers apart from the currently connected ones, that may be used for probes. - servers: HashSet, - - // Assumed NAT status. - nat_status: NatStatus, - - // Confidence in the assumed NAT status. - confidence: usize, - - // Timer for the next probe. - schedule_probe: Delay, - - // Ongoing inbound requests, where no response has been sent back to the remote yet. - ongoing_inbound: HashMap< - PeerId, - ( - ProbeId, - InboundRequestId, - Vec, - ResponseChannel, - ), - >, - - // Ongoing outbound probes and mapped to the inner request id. - ongoing_outbound: HashMap, - - // Connected peers with the observed address of each connection. - // If the endpoint of a connection is relayed or not global (in case of Config::only_global_ips), - // the observed address is `None`. - connected: HashMap>>, - - // Used servers in recent outbound probes that are throttled through Config::throttle_server_period. - throttled_servers: Vec<(PeerId, Instant)>, - - // Recent probes done for clients - throttled_clients: Vec<(PeerId, Instant)>, - - last_probe: Option, - - pending_actions: VecDeque::ToSwarm, THandlerInEvent>>, - - probe_id: ProbeId, - - listen_addresses: ListenAddresses, - other_candidates: HashSet, -} - -impl Behaviour { - pub fn new(local_peer_id: PeerId, config: Config) -> Self { - let protocols = iter::once((DEFAULT_PROTOCOL_NAME, ProtocolSupport::Full)); - let inner = request_response::Behaviour::with_codec( - AutoNatCodec, - protocols, - request_response::Config::default().with_request_timeout(config.timeout), - ); - Self { - local_peer_id, - inner, - schedule_probe: Delay::new(config.boot_delay), - config, - servers: HashSet::new(), - ongoing_inbound: HashMap::default(), - ongoing_outbound: HashMap::default(), - connected: HashMap::default(), - nat_status: NatStatus::Unknown, - confidence: 0, - throttled_servers: Vec::new(), - throttled_clients: Vec::new(), - last_probe: None, - pending_actions: VecDeque::new(), - probe_id: ProbeId(0), - listen_addresses: Default::default(), - other_candidates: Default::default(), - } - } - - /// Assumed public address of the local peer. - /// Returns `None` in case of status [`NatStatus::Private`] or [`NatStatus::Unknown`]. - pub fn public_address(&self) -> Option<&Multiaddr> { - match &self.nat_status { - NatStatus::Public(address) => Some(address), - _ => None, - } - } - - /// Assumed NAT status. - pub fn nat_status(&self) -> NatStatus { - self.nat_status.clone() - } - - /// Confidence in the assumed NAT status. - pub fn confidence(&self) -> usize { - self.confidence - } - - /// Add a peer to the list over servers that may be used for probes. - /// These peers are used for dial-request even if they are currently not connection, in which case a connection will be - /// establish before sending the dial-request. - pub fn add_server(&mut self, peer: PeerId, address: Option) { - self.servers.insert(peer); - if let Some(addr) = address { - #[allow(deprecated)] - self.inner.add_address(&peer, addr); - } - } - - /// Remove a peer from the list of servers. - /// See [`Behaviour::add_server`] for more info. - pub fn remove_server(&mut self, peer: &PeerId) { - self.servers.retain(|p| p != peer); - } - - /// Explicitly probe the provided address for external reachability. - pub fn probe_address(&mut self, candidate: Multiaddr) { - self.other_candidates.insert(candidate); - self.as_client().on_new_address(); - } - - fn as_client(&mut self) -> AsClient { - AsClient { - inner: &mut self.inner, - local_peer_id: self.local_peer_id, - config: &self.config, - connected: &self.connected, - probe_id: &mut self.probe_id, - servers: &self.servers, - throttled_servers: &mut self.throttled_servers, - nat_status: &mut self.nat_status, - confidence: &mut self.confidence, - ongoing_outbound: &mut self.ongoing_outbound, - last_probe: &mut self.last_probe, - schedule_probe: &mut self.schedule_probe, - listen_addresses: &self.listen_addresses, - other_candidates: &self.other_candidates, - } - } - - fn as_server(&mut self) -> AsServer { - AsServer { - inner: &mut self.inner, - config: &self.config, - connected: &self.connected, - probe_id: &mut self.probe_id, - throttled_clients: &mut self.throttled_clients, - ongoing_inbound: &mut self.ongoing_inbound, - } - } - - fn on_connection_established( - &mut self, - ConnectionEstablished { - peer_id: peer, - connection_id: conn, - endpoint, - .. - }: ConnectionEstablished, - ) { - let connections = self.connected.entry(peer).or_default(); - let addr = endpoint.get_remote_address(); - let observed_addr = - if !endpoint.is_relayed() && (!self.config.only_global_ips || addr.is_global_ip()) { - Some(addr.clone()) - } else { - None - }; - connections.insert(conn, observed_addr); - - match endpoint { - ConnectedPoint::Dialer { - address, - role_override: Endpoint::Dialer, - .. - } => { - if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { - self.pending_actions - .push_back(ToSwarm::GenerateEvent(Event::InboundProbe(event))); - } - } - ConnectedPoint::Dialer { - address: _, - role_override: Endpoint::Listener, - .. - } => { - // Outgoing connection was dialed as a listener. In other words outgoing connection - // was dialed as part of a hole punch. `libp2p-autonat` never attempts to hole - // punch, thus this connection has not been requested by this [`NetworkBehaviour`]. - } - ConnectedPoint::Listener { .. } => self.as_client().on_inbound_connection(), - } - } - - fn on_connection_closed( - &mut self, - ConnectionClosed { - peer_id, - connection_id, - remaining_established, - .. - }: ConnectionClosed, - ) { - if remaining_established == 0 { - self.connected.remove(&peer_id); - } else { - let connections = self - .connected - .get_mut(&peer_id) - .expect("Peer is connected."); - connections.remove(&connection_id); - } - } - - fn on_dial_failure(&mut self, DialFailure { peer_id, error, .. }: DialFailure) { - if let Some(event) = self.as_server().on_outbound_dial_error(peer_id, error) { - self.pending_actions - .push_back(ToSwarm::GenerateEvent(Event::InboundProbe(event))); - } - } - - fn on_address_change( - &mut self, - AddressChange { - peer_id: peer, - connection_id: conn, - old, - new, - }: AddressChange, - ) { - if old.is_relayed() && new.is_relayed() { - return; - } - let connections = self.connected.get_mut(&peer).expect("Peer is connected."); - let addr = new.get_remote_address(); - let observed_addr = - if !new.is_relayed() && (!self.config.only_global_ips || addr.is_global_ip()) { - Some(addr.clone()) - } else { - None - }; - connections.insert(conn, observed_addr); - } -} - -impl NetworkBehaviour for Behaviour { - type ConnectionHandler = - as NetworkBehaviour>::ConnectionHandler; - type ToSwarm = Event; - - #[tracing::instrument(level = "trace", name = "NetworkBehaviour::poll", skip(self, cx))] - fn poll( - &mut self, - cx: &mut Context<'_>, - ) -> Poll>> { - loop { - if let Some(event) = self.pending_actions.pop_front() { - return Poll::Ready(event); - } - - match self.inner.poll(cx) { - Poll::Ready(ToSwarm::GenerateEvent(event)) => { - let actions = match event { - request_response::Event::Message { - message: request_response::Message::Response { .. }, - .. - } - | request_response::Event::OutboundFailure { .. } => { - self.as_client().handle_event(event) - } - request_response::Event::Message { - message: request_response::Message::Request { .. }, - .. - } - | request_response::Event::InboundFailure { .. } => { - self.as_server().handle_event(event) - } - request_response::Event::ResponseSent { .. } => VecDeque::new(), - }; - - self.pending_actions.extend(actions); - continue; - } - Poll::Ready(action) => { - self.pending_actions - .push_back(action.map_out(|_| unreachable!())); - continue; - } - Poll::Pending => {} - } - - match self.as_client().poll_auto_probe(cx) { - Poll::Ready(event) => { - self.pending_actions - .push_back(ToSwarm::GenerateEvent(Event::OutboundProbe(event))); - continue; - } - Poll::Pending => {} - } - - return Poll::Pending; - } - } - - fn handle_pending_inbound_connection( - &mut self, - connection_id: ConnectionId, - local_addr: &Multiaddr, - remote_addr: &Multiaddr, - ) -> Result<(), ConnectionDenied> { - self.inner - .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) - } - - fn handle_established_inbound_connection( - &mut self, - connection_id: ConnectionId, - peer: PeerId, - local_addr: &Multiaddr, - remote_addr: &Multiaddr, - ) -> Result, ConnectionDenied> { - self.inner.handle_established_inbound_connection( - connection_id, - peer, - local_addr, - remote_addr, - ) - } - - fn handle_pending_outbound_connection( - &mut self, - connection_id: ConnectionId, - maybe_peer: Option, - addresses: &[Multiaddr], - effective_role: Endpoint, - ) -> Result, ConnectionDenied> { - self.inner.handle_pending_outbound_connection( - connection_id, - maybe_peer, - addresses, - effective_role, - ) - } - - fn handle_established_outbound_connection( - &mut self, - connection_id: ConnectionId, - peer: PeerId, - addr: &Multiaddr, - role_override: Endpoint, - port_use: PortUse, - ) -> Result, ConnectionDenied> { - self.inner.handle_established_outbound_connection( - connection_id, - peer, - addr, - role_override, - port_use, - ) - } - - fn on_swarm_event(&mut self, event: FromSwarm) { - self.listen_addresses.on_swarm_event(&event); - self.inner.on_swarm_event(event); - - match event { - FromSwarm::ConnectionEstablished(e) => self.on_connection_established(e), - FromSwarm::ConnectionClosed(e) => self.on_connection_closed(e), - FromSwarm::DialFailure(e) => self.on_dial_failure(e), - FromSwarm::AddressChange(e) => self.on_address_change(e), - FromSwarm::NewListenAddr(_) => { - self.as_client().on_new_address(); - } - FromSwarm::ExpiredListenAddr(e) => { - self.as_client().on_expired_address(e.addr); - } - FromSwarm::ExternalAddrExpired(e) => { - self.as_client().on_expired_address(e.addr); - } - FromSwarm::NewExternalAddrCandidate(e) => { - self.probe_address(e.addr.to_owned()); - } - _ => {} - } - } - - fn on_connection_handler_event( - &mut self, - peer_id: PeerId, - connection_id: ConnectionId, - event: THandlerOutEvent, - ) { - self.inner - .on_connection_handler_event(peer_id, connection_id, event) - } -} - -type Action = ToSwarm<::ToSwarm, THandlerInEvent>; - -// Trait implemented for `AsClient` and `AsServer` to handle events from the inner [`request_response::Behaviour`] Protocol. -trait HandleInnerEvent { - fn handle_event( - &mut self, - event: request_response::Event, - ) -> VecDeque; -} - -trait GlobalIp { - fn is_global_ip(&self) -> bool; -} - -impl GlobalIp for Multiaddr { - fn is_global_ip(&self) -> bool { - match self.iter().next() { - Some(Protocol::Ip4(a)) => a.is_global_ip(), - Some(Protocol::Ip6(a)) => a.is_global_ip(), - _ => false, - } - } -} - -impl GlobalIp for std::net::Ipv4Addr { - // NOTE: The below logic is copied from `std::net::Ipv4Addr::is_global`, which is at the time of - // writing behind the unstable `ip` feature. - // See https://github.com/rust-lang/rust/issues/27709 for more info. - fn is_global_ip(&self) -> bool { - // Check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two - // globally routable addresses in the 192.0.0.0/24 range. - if u32::from_be_bytes(self.octets()) == 0xc0000009 - || u32::from_be_bytes(self.octets()) == 0xc000000a - { - return true; - } - - // Copied from the unstable method `std::net::Ipv4Addr::is_shared`. - fn is_shared(addr: &std::net::Ipv4Addr) -> bool { - addr.octets()[0] == 100 && (addr.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - // Copied from the unstable method `std::net::Ipv4Addr::is_reserved`. - // - // **Warning**: As IANA assigns new addresses, this logic will be - // updated. This may result in non-reserved addresses being - // treated as reserved in code that relies on an outdated version - // of this method. - fn is_reserved(addr: &std::net::Ipv4Addr) -> bool { - addr.octets()[0] & 240 == 240 && !addr.is_broadcast() - } - - // Copied from the unstable method `std::net::Ipv4Addr::is_benchmarking`. - fn is_benchmarking(addr: &std::net::Ipv4Addr) -> bool { - addr.octets()[0] == 198 && (addr.octets()[1] & 0xfe) == 18 - } - - !self.is_private() - && !self.is_loopback() - && !self.is_link_local() - && !self.is_broadcast() - && !self.is_documentation() - && !is_shared(self) - // addresses reserved for future protocols (`192.0.0.0/24`) - && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - && !is_reserved(self) - && !is_benchmarking(self) - // Make sure the address is not in 0.0.0.0/8 - && self.octets()[0] != 0 - } -} - -impl GlobalIp for std::net::Ipv6Addr { - // NOTE: The below logic is copied from `std::net::Ipv6Addr::is_global`, which is at the time of - // writing behind the unstable `ip` feature. - // See https://github.com/rust-lang/rust/issues/27709 for more info. - // - // Note that contrary to `Ipv4Addr::is_global_ip` this currently checks for global scope - // rather than global reachability. - fn is_global_ip(&self) -> bool { - // Copied from the unstable method `std::net::Ipv6Addr::is_unicast`. - fn is_unicast(addr: &std::net::Ipv6Addr) -> bool { - !addr.is_multicast() - } - // Copied from the unstable method `std::net::Ipv6Addr::is_unicast_link_local`. - fn is_unicast_link_local(addr: &std::net::Ipv6Addr) -> bool { - (addr.segments()[0] & 0xffc0) == 0xfe80 - } - // Copied from the unstable method `std::net::Ipv6Addr::is_unique_local`. - fn is_unique_local(addr: &std::net::Ipv6Addr) -> bool { - (addr.segments()[0] & 0xfe00) == 0xfc00 - } - // Copied from the unstable method `std::net::Ipv6Addr::is_documentation`. - fn is_documentation(addr: &std::net::Ipv6Addr) -> bool { - (addr.segments()[0] == 0x2001) && (addr.segments()[1] == 0xdb8) - } - - // Copied from the unstable method `std::net::Ipv6Addr::is_unicast_global`. - fn is_unicast_global(addr: &std::net::Ipv6Addr) -> bool { - is_unicast(addr) - && !addr.is_loopback() - && !is_unicast_link_local(addr) - && !is_unique_local(addr) - && !addr.is_unspecified() - && !is_documentation(addr) - } - - // Variation of unstable method [`std::net::Ipv6Addr::multicast_scope`] that instead of the - // `Ipv6MulticastScope` just returns if the scope is global or not. - // Equivalent to `Ipv6Addr::multicast_scope(..).map(|scope| matches!(scope, Ipv6MulticastScope::Global))`. - fn is_multicast_scope_global(addr: &std::net::Ipv6Addr) -> Option { - match addr.segments()[0] & 0x000f { - 14 => Some(true), // Global multicast scope. - 1..=5 | 8 => Some(false), // Local multicast scope. - _ => None, // Unknown multicast scope. - } - } - - match is_multicast_scope_global(self) { - Some(true) => true, - None => is_unicast_global(self), - _ => false, - } - } -} diff --git a/protocols/autonat/src/behaviour/as_client.rs b/protocols/autonat/src/behaviour/as_client.rs deleted file mode 100644 index 8960163ccb3..00000000000 --- a/protocols/autonat/src/behaviour/as_client.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::ResponseError; - -use super::{ - Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, NatStatus, - ProbeId, -}; -use futures::FutureExt; -use futures_timer::Delay; -use libp2p_core::Multiaddr; -use libp2p_identity::PeerId; -use libp2p_request_response::{self as request_response, OutboundFailure, OutboundRequestId}; -use libp2p_swarm::{ConnectionId, ListenAddresses, ToSwarm}; -use rand::{seq::SliceRandom, thread_rng}; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - task::{Context, Poll}, - time::Duration, -}; -use web_time::Instant; - -/// Outbound probe failed or was aborted. -#[derive(Debug)] -pub enum OutboundProbeError { - /// Probe was aborted because no server is known, or all servers - /// are throttled through [`Config::throttle_server_period`]. - NoServer, - /// Probe was aborted because the local peer has no listening or - /// external addresses. - NoAddresses, - /// Sending the dial-back request or receiving a response failed. - OutboundRequest(OutboundFailure), - /// The server refused or failed to dial us. - Response(ResponseError), -} - -#[derive(Debug)] -pub enum OutboundProbeEvent { - /// A dial-back request was sent to a remote peer. - Request { - probe_id: ProbeId, - /// Peer to which the request is sent. - peer: PeerId, - }, - /// The remote successfully dialed one of our addresses. - Response { - probe_id: ProbeId, - /// Id of the peer that sent the response. - peer: PeerId, - /// The address at which the remote succeeded to dial us. - address: Multiaddr, - }, - /// The outbound request failed, was rejected, or the remote could dial - /// none of our addresses. - Error { - probe_id: ProbeId, - /// Id of the peer used for the probe. - /// `None` if the probe was aborted due to no addresses or no qualified server. - peer: Option, - error: OutboundProbeError, - }, -} - -/// View over [`super::Behaviour`] in a client role. -pub(crate) struct AsClient<'a> { - pub(crate) inner: &'a mut request_response::Behaviour, - pub(crate) local_peer_id: PeerId, - pub(crate) config: &'a Config, - pub(crate) connected: &'a HashMap>>, - pub(crate) probe_id: &'a mut ProbeId, - pub(crate) servers: &'a HashSet, - pub(crate) throttled_servers: &'a mut Vec<(PeerId, Instant)>, - pub(crate) nat_status: &'a mut NatStatus, - pub(crate) confidence: &'a mut usize, - pub(crate) ongoing_outbound: &'a mut HashMap, - pub(crate) last_probe: &'a mut Option, - pub(crate) schedule_probe: &'a mut Delay, - pub(crate) listen_addresses: &'a ListenAddresses, - pub(crate) other_candidates: &'a HashSet, -} - -impl<'a> HandleInnerEvent for AsClient<'a> { - fn handle_event( - &mut self, - event: request_response::Event, - ) -> VecDeque { - match event { - request_response::Event::Message { - peer, - message: - request_response::Message::Response { - request_id, - response, - }, - } => { - tracing::debug!(?response, "Outbound dial-back request returned response"); - - let probe_id = self - .ongoing_outbound - .remove(&request_id) - .expect("OutboundRequestId exists."); - - let event = match response.result.clone() { - Ok(address) => OutboundProbeEvent::Response { - probe_id, - peer, - address, - }, - Err(e) => OutboundProbeEvent::Error { - probe_id, - peer: Some(peer), - error: OutboundProbeError::Response(e), - }, - }; - - let mut actions = VecDeque::with_capacity(3); - - actions.push_back(ToSwarm::GenerateEvent(Event::OutboundProbe(event))); - - if let Some(old) = self.handle_reported_status(response.result.clone().into()) { - actions.push_back(ToSwarm::GenerateEvent(Event::StatusChanged { - old, - new: self.nat_status.clone(), - })); - } - - if let Ok(address) = response.result { - actions.push_back(ToSwarm::ExternalAddrConfirmed(address)); - } - - actions - } - request_response::Event::OutboundFailure { - peer, - error, - request_id, - } => { - tracing::debug!( - %peer, - "Outbound Failure {} when on dial-back request to peer.", - error, - ); - let probe_id = self - .ongoing_outbound - .remove(&request_id) - .unwrap_or_else(|| self.probe_id.next()); - - self.schedule_probe.reset(Duration::ZERO); - - VecDeque::from([ToSwarm::GenerateEvent(Event::OutboundProbe( - OutboundProbeEvent::Error { - probe_id, - peer: Some(peer), - error: OutboundProbeError::OutboundRequest(error), - }, - ))]) - } - _ => VecDeque::default(), - } - } -} - -impl<'a> AsClient<'a> { - pub(crate) fn poll_auto_probe(&mut self, cx: &mut Context<'_>) -> Poll { - match self.schedule_probe.poll_unpin(cx) { - Poll::Ready(()) => { - self.schedule_probe.reset(self.config.retry_interval); - - let addresses = self - .other_candidates - .iter() - .chain(self.listen_addresses.iter()) - .cloned() - .collect(); - - let probe_id = self.probe_id.next(); - let event = match self.do_probe(probe_id, addresses) { - Ok(peer) => OutboundProbeEvent::Request { probe_id, peer }, - Err(error) => { - self.handle_reported_status(NatStatus::Unknown); - OutboundProbeEvent::Error { - probe_id, - peer: None, - error, - } - } - }; - Poll::Ready(event) - } - Poll::Pending => Poll::Pending, - } - } - - // An inbound connection can indicate that we are public; adjust the delay to the next probe. - pub(crate) fn on_inbound_connection(&mut self) { - if *self.confidence == self.config.confidence_max { - if self.nat_status.is_public() { - self.schedule_next_probe(self.config.refresh_interval * 2); - } else { - self.schedule_next_probe(self.config.refresh_interval / 5); - } - } - } - - pub(crate) fn on_new_address(&mut self) { - if !self.nat_status.is_public() { - // New address could be publicly reachable, trigger retry. - if *self.confidence > 0 { - *self.confidence -= 1; - } - self.schedule_next_probe(self.config.retry_interval); - } - } - - pub(crate) fn on_expired_address(&mut self, addr: &Multiaddr) { - if let NatStatus::Public(public_address) = self.nat_status { - if public_address == addr { - *self.confidence = 0; - *self.nat_status = NatStatus::Unknown; - self.schedule_next_probe(Duration::ZERO); - } - } - } - - // Select a random server for the probe. - fn random_server(&mut self) -> Option { - // Update list of throttled servers. - let i = self.throttled_servers.partition_point(|(_, time)| { - *time + self.config.throttle_server_period < Instant::now() - }); - self.throttled_servers.drain(..i); - - let mut servers: Vec<&PeerId> = self.servers.iter().collect(); - - if self.config.use_connected { - servers.extend(self.connected.iter().filter_map(|(id, addrs)| { - // Filter servers for which no qualified address is known. - // This is the case if the connection is relayed or the address is - // not global (in case of Config::only_global_ips). - addrs.values().any(|a| a.is_some()).then_some(id) - })); - } - - servers.retain(|s| !self.throttled_servers.iter().any(|(id, _)| s == &id)); - - servers.choose(&mut thread_rng()).map(|&&p| p) - } - - // Send a dial-request to a randomly selected server. - // Returns the server that is used in this probe. - // `Err` if there are no qualified servers or no addresses. - fn do_probe( - &mut self, - probe_id: ProbeId, - addresses: Vec, - ) -> Result { - let _ = self.last_probe.insert(Instant::now()); - if addresses.is_empty() { - tracing::debug!("Outbound dial-back request aborted: No dial-back addresses"); - return Err(OutboundProbeError::NoAddresses); - } - - let server = self.random_server().ok_or(OutboundProbeError::NoServer)?; - - let request_id = self.inner.send_request( - &server, - DialRequest { - peer_id: self.local_peer_id, - addresses, - }, - ); - self.throttled_servers.push((server, Instant::now())); - tracing::debug!(peer=%server, "Send dial-back request to peer"); - self.ongoing_outbound.insert(request_id, probe_id); - Ok(server) - } - - // Set the delay to the next probe based on the time of our last probe - // and the specified delay. - fn schedule_next_probe(&mut self, delay: Duration) { - let Some(last_probe_instant) = self.last_probe else { - return; - }; - let schedule_next = *last_probe_instant + delay; - self.schedule_probe - .reset(schedule_next.saturating_duration_since(Instant::now())); - } - - // Adapt current confidence and NAT status to the status reported by the latest probe. - // Return the old status if it flipped. - fn handle_reported_status(&mut self, reported_status: NatStatus) -> Option { - self.schedule_next_probe(self.config.retry_interval); - - if matches!(reported_status, NatStatus::Unknown) { - return None; - } - - if reported_status == *self.nat_status { - if *self.confidence < self.config.confidence_max { - *self.confidence += 1; - } - // Delay with (usually longer) refresh-interval. - if *self.confidence >= self.config.confidence_max { - self.schedule_next_probe(self.config.refresh_interval); - } - return None; - } - - if reported_status.is_public() && self.nat_status.is_public() { - // Different address than the currently assumed public address was reported. - // Switch address, but don't report as flipped. - *self.nat_status = reported_status; - return None; - } - if *self.confidence > 0 { - // Reduce confidence but keep old status. - *self.confidence -= 1; - return None; - } - - tracing::debug!( - old_status=?self.nat_status, - new_status=?reported_status, - "Flipped assumed NAT status" - ); - - let old_status = self.nat_status.clone(); - *self.nat_status = reported_status; - - Some(old_status) - } -} - -impl From> for NatStatus { - fn from(result: Result) -> Self { - match result { - Ok(addr) => NatStatus::Public(addr), - Err(ResponseError::DialError) => NatStatus::Private, - _ => NatStatus::Unknown, - } - } -} diff --git a/protocols/autonat/src/behaviour/as_server.rs b/protocols/autonat/src/behaviour/as_server.rs deleted file mode 100644 index 8f1d6642de5..00000000000 --- a/protocols/autonat/src/behaviour/as_server.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use super::{ - Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, - ResponseError, -}; -use libp2p_core::{multiaddr::Protocol, Multiaddr}; -use libp2p_identity::PeerId; -use libp2p_request_response::{ - self as request_response, InboundFailure, InboundRequestId, ResponseChannel, -}; -use libp2p_swarm::{ - dial_opts::{DialOpts, PeerCondition}, - ConnectionId, DialError, ToSwarm, -}; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - num::NonZeroU8, -}; -use web_time::Instant; - -/// Inbound probe failed. -#[derive(Debug)] -pub enum InboundProbeError { - /// Receiving the dial-back request or sending a response failed. - InboundRequest(InboundFailure), - /// We refused or failed to dial the client. - Response(ResponseError), -} - -#[derive(Debug)] -pub enum InboundProbeEvent { - /// A dial-back request was received from a remote peer. - Request { - probe_id: ProbeId, - /// Peer that sent the request. - peer: PeerId, - /// The addresses that will be attempted to dial. - addresses: Vec, - }, - /// A dial request to the remote was successful. - Response { - probe_id: ProbeId, - /// Peer to which the response is sent. - peer: PeerId, - address: Multiaddr, - }, - /// The inbound request failed, was rejected, or none of the remote's - /// addresses could be dialed. - Error { - probe_id: ProbeId, - /// Peer that sent the dial-back request. - peer: PeerId, - error: InboundProbeError, - }, -} - -/// View over [`super::Behaviour`] in a server role. -pub(crate) struct AsServer<'a> { - pub(crate) inner: &'a mut request_response::Behaviour, - pub(crate) config: &'a Config, - pub(crate) connected: &'a HashMap>>, - pub(crate) probe_id: &'a mut ProbeId, - pub(crate) throttled_clients: &'a mut Vec<(PeerId, Instant)>, - #[allow(clippy::type_complexity)] - pub(crate) ongoing_inbound: &'a mut HashMap< - PeerId, - ( - ProbeId, - InboundRequestId, - Vec, - ResponseChannel, - ), - >, -} - -impl<'a> HandleInnerEvent for AsServer<'a> { - fn handle_event( - &mut self, - event: request_response::Event, - ) -> VecDeque { - match event { - request_response::Event::Message { - peer, - message: - request_response::Message::Request { - request_id, - request, - channel, - }, - } => { - let probe_id = self.probe_id.next(); - match self.resolve_inbound_request(peer, request) { - Ok(addrs) => { - tracing::debug!( - %peer, - "Inbound dial request from peer with dial-back addresses {:?}", - addrs - ); - - self.ongoing_inbound - .insert(peer, (probe_id, request_id, addrs.clone(), channel)); - self.throttled_clients.push((peer, Instant::now())); - - VecDeque::from([ - ToSwarm::GenerateEvent(Event::InboundProbe( - InboundProbeEvent::Request { - probe_id, - peer, - addresses: addrs.clone(), - }, - )), - ToSwarm::Dial { - opts: DialOpts::peer_id(peer) - .condition(PeerCondition::Always) - .override_dial_concurrency_factor( - NonZeroU8::new(1).expect("1 > 0"), - ) - .addresses(addrs) - .allocate_new_port() - .build(), - }, - ]) - } - Err((status_text, error)) => { - tracing::debug!( - %peer, - status=%status_text, - "Reject inbound dial request from peer" - ); - - let response = DialResponse { - result: Err(error.clone()), - status_text: Some(status_text), - }; - let _ = self.inner.send_response(channel, response); - - VecDeque::from([ToSwarm::GenerateEvent(Event::InboundProbe( - InboundProbeEvent::Error { - probe_id, - peer, - error: InboundProbeError::Response(error), - }, - ))]) - } - } - } - request_response::Event::InboundFailure { - peer, - error, - request_id, - } => { - tracing::debug!( - %peer, - "Inbound Failure {} when on dial-back request from peer", - error - ); - - let probe_id = match self.ongoing_inbound.get(&peer) { - Some((_, rq_id, _, _)) if *rq_id == request_id => { - self.ongoing_inbound.remove(&peer).unwrap().0 - } - _ => self.probe_id.next(), - }; - - VecDeque::from([ToSwarm::GenerateEvent(Event::InboundProbe( - InboundProbeEvent::Error { - probe_id, - peer, - error: InboundProbeError::InboundRequest(error), - }, - ))]) - } - _ => VecDeque::new(), - } - } -} - -impl<'a> AsServer<'a> { - pub(crate) fn on_outbound_connection( - &mut self, - peer: &PeerId, - address: &Multiaddr, - ) -> Option { - let (_, _, addrs, _) = self.ongoing_inbound.get(peer)?; - - // Check if the dialed address was among the requested addresses. - if !addrs.contains(address) { - return None; - } - - tracing::debug!( - %peer, - %address, - "Dial-back to peer succeeded" - ); - - let (probe_id, _, _, channel) = self.ongoing_inbound.remove(peer).unwrap(); - let response = DialResponse { - result: Ok(address.clone()), - status_text: None, - }; - let _ = self.inner.send_response(channel, response); - - Some(InboundProbeEvent::Response { - probe_id, - peer: *peer, - address: address.clone(), - }) - } - - pub(crate) fn on_outbound_dial_error( - &mut self, - peer: Option, - error: &DialError, - ) -> Option { - let (probe_id, _, _, channel) = peer.and_then(|p| self.ongoing_inbound.remove(&p))?; - - match peer { - Some(p) => tracing::debug!( - peer=%p, - "Dial-back to peer failed with error {:?}", - error - ), - None => tracing::debug!( - "Dial-back to non existent peer failed with error {:?}", - error - ), - }; - - let response_error = ResponseError::DialError; - let response = DialResponse { - result: Err(response_error.clone()), - status_text: Some("dial failed".to_string()), - }; - let _ = self.inner.send_response(channel, response); - - Some(InboundProbeEvent::Error { - probe_id, - peer: peer.expect("PeerId is present."), - error: InboundProbeError::Response(response_error), - }) - } - - // Validate the inbound request and collect the addresses to be dialed. - fn resolve_inbound_request( - &mut self, - sender: PeerId, - request: DialRequest, - ) -> Result, (String, ResponseError)> { - // Update list of throttled clients. - let i = self.throttled_clients.partition_point(|(_, time)| { - *time + self.config.throttle_clients_period < Instant::now() - }); - self.throttled_clients.drain(..i); - - if request.peer_id != sender { - let status_text = "peer id mismatch".to_string(); - return Err((status_text, ResponseError::BadRequest)); - } - - if self.ongoing_inbound.contains_key(&sender) { - let status_text = "dial-back already ongoing".to_string(); - return Err((status_text, ResponseError::DialRefused)); - } - - if self.throttled_clients.len() >= self.config.throttle_clients_global_max { - let status_text = "too many total dials".to_string(); - return Err((status_text, ResponseError::DialRefused)); - } - - let throttled_for_client = self - .throttled_clients - .iter() - .filter(|(p, _)| p == &sender) - .count(); - - if throttled_for_client >= self.config.throttle_clients_peer_max { - let status_text = "too many dials for peer".to_string(); - return Err((status_text, ResponseError::DialRefused)); - } - - // Obtain an observed address from non-relayed connections. - let observed_addr = self - .connected - .get(&sender) - .expect("Peer is connected.") - .values() - .find_map(|a| a.as_ref()) - .ok_or_else(|| { - let status_text = "refusing to dial peer with blocked observed address".to_string(); - (status_text, ResponseError::DialRefused) - })?; - - let mut addrs = Self::filter_valid_addrs(sender, request.addresses, observed_addr); - addrs.truncate(self.config.max_peer_addresses); - - if addrs.is_empty() { - let status_text = "no dialable addresses".to_string(); - return Err((status_text, ResponseError::DialRefused)); - } - - Ok(addrs) - } - - // Filter dial addresses and replace demanded ip with the observed one. - fn filter_valid_addrs( - peer: PeerId, - demanded: Vec, - observed_remote_at: &Multiaddr, - ) -> Vec { - let Some(observed_ip) = observed_remote_at - .into_iter() - .find(|p| matches!(p, Protocol::Ip4(_) | Protocol::Ip6(_))) - else { - return Vec::new(); - }; - - let mut distinct = HashSet::new(); - demanded - .into_iter() - .filter_map(|addr| { - // Replace the demanded ip with the observed one. - let i = addr - .iter() - .position(|p| matches!(p, Protocol::Ip4(_) | Protocol::Ip6(_)))?; - let mut addr = addr.replace(i, |_| Some(observed_ip.clone()))?; - - let is_valid = addr.iter().all(|proto| match proto { - Protocol::P2pCircuit => false, - Protocol::P2p(peer_id) => peer_id == peer, - _ => true, - }); - - if !is_valid { - return None; - } - if !addr.iter().any(|p| matches!(p, Protocol::P2p(_))) { - addr.push(Protocol::P2p(peer)) - } - // Only collect distinct addresses. - distinct.insert(addr.clone()).then_some(addr) - }) - .collect() - } -} - -#[cfg(test)] -mod test { - use super::*; - - use std::net::Ipv4Addr; - - fn random_ip<'a>() -> Protocol<'a> { - Protocol::Ip4(Ipv4Addr::new( - rand::random(), - rand::random(), - rand::random(), - rand::random(), - )) - } - fn random_port<'a>() -> Protocol<'a> { - Protocol::Tcp(rand::random()) - } - - #[test] - fn filter_addresses() { - let peer_id = PeerId::random(); - let observed_ip = random_ip(); - let observed_addr = Multiaddr::empty() - .with(observed_ip.clone()) - .with(random_port()) - .with(Protocol::P2p(peer_id)); - // Valid address with matching peer-id - let demanded_1 = Multiaddr::empty() - .with(random_ip()) - .with(random_port()) - .with(Protocol::P2p(peer_id)); - // Invalid because peer_id does not match - let demanded_2 = Multiaddr::empty() - .with(random_ip()) - .with(random_port()) - .with(Protocol::P2p(PeerId::random())); - // Valid address without peer-id - let demanded_3 = Multiaddr::empty().with(random_ip()).with(random_port()); - // Invalid because relayed - let demanded_4 = Multiaddr::empty() - .with(random_ip()) - .with(random_port()) - .with(Protocol::P2p(PeerId::random())) - .with(Protocol::P2pCircuit) - .with(Protocol::P2p(peer_id)); - let demanded = vec![ - demanded_1.clone(), - demanded_2, - demanded_3.clone(), - demanded_4, - ]; - let filtered = AsServer::filter_valid_addrs(peer_id, demanded, &observed_addr); - let expected_1 = demanded_1 - .replace(0, |_| Some(observed_ip.clone())) - .unwrap(); - let expected_2 = demanded_3 - .replace(0, |_| Some(observed_ip)) - .unwrap() - .with(Protocol::P2p(peer_id)); - assert_eq!(filtered, vec![expected_1, expected_2]); - } -} diff --git a/protocols/autonat/src/generated/mod.rs b/protocols/autonat/src/generated/mod.rs deleted file mode 100644 index e52c5a80bc0..00000000000 --- a/protocols/autonat/src/generated/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -// Automatically generated mod.rs -pub mod structs; diff --git a/protocols/autonat/src/generated/structs.proto b/protocols/autonat/src/generated/structs.proto deleted file mode 100644 index 19e27abd36a..00000000000 --- a/protocols/autonat/src/generated/structs.proto +++ /dev/null @@ -1,37 +0,0 @@ -syntax = "proto2"; - -package structs; - -message Message { - enum MessageType { - DIAL = 0; - DIAL_RESPONSE = 1; - } - - enum ResponseStatus { - OK = 0; - E_DIAL_ERROR = 100; - E_DIAL_REFUSED = 101; - E_BAD_REQUEST = 200; - E_INTERNAL_ERROR = 300; - } - - message PeerInfo { - optional bytes id = 1; - repeated bytes addrs = 2; - } - - message Dial { - optional PeerInfo peer = 1; - } - - message DialResponse { - optional ResponseStatus status = 1; - optional string statusText = 2; - optional bytes addr = 3; - } - - optional MessageType type = 1; - optional Dial dial = 2; - optional DialResponse dialResponse = 3; -} diff --git a/protocols/autonat/src/generated/structs.rs b/protocols/autonat/src/generated/structs.rs deleted file mode 100644 index 3a6d416b2b1..00000000000 --- a/protocols/autonat/src/generated/structs.rs +++ /dev/null @@ -1,242 +0,0 @@ -// Automatically generated rust module for 'structs.proto' file - -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(unused_imports)] -#![allow(unknown_lints)] -#![allow(clippy::all)] -#![cfg_attr(rustfmt, rustfmt_skip)] - - -use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; -use quick_protobuf::sizeofs::*; -use super::*; - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct Message { - pub type_pb: Option, - pub dial: Option, - pub dialResponse: Option, -} - -impl<'a> MessageRead<'a> for Message { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.type_pb = Some(r.read_enum(bytes)?), - Ok(18) => msg.dial = Some(r.read_message::(bytes)?), - Ok(26) => msg.dialResponse = Some(r.read_message::(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for Message { - fn get_size(&self) -> usize { - 0 - + self.type_pb.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.dial.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) - + self.dialResponse.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.type_pb { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - if let Some(ref s) = self.dial { w.write_with_tag(18, |w| w.write_message(s))?; } - if let Some(ref s) = self.dialResponse { w.write_with_tag(26, |w| w.write_message(s))?; } - Ok(()) - } -} - -pub mod mod_Message { - -use super::*; - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct PeerInfo { - pub id: Option>, - pub addrs: Vec>, -} - -impl<'a> MessageRead<'a> for PeerInfo { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.id = Some(r.read_bytes(bytes)?.to_owned()), - Ok(18) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for PeerInfo { - fn get_size(&self) -> usize { - 0 - + self.id.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) - + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.id { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } - for s in &self.addrs { w.write_with_tag(18, |w| w.write_bytes(&**s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct Dial { - pub peer: Option, -} - -impl<'a> MessageRead<'a> for Dial { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(10) => msg.peer = Some(r.read_message::(bytes)?), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for Dial { - fn get_size(&self) -> usize { - 0 - + self.peer.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.peer { w.write_with_tag(10, |w| w.write_message(s))?; } - Ok(()) - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, Default, PartialEq, Clone)] -pub struct DialResponse { - pub status: Option, - pub statusText: Option, - pub addr: Option>, -} - -impl<'a> MessageRead<'a> for DialResponse { - fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { - let mut msg = Self::default(); - while !r.is_eof() { - match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), - Ok(18) => msg.statusText = Some(r.read_string(bytes)?.to_owned()), - Ok(26) => msg.addr = Some(r.read_bytes(bytes)?.to_owned()), - Ok(t) => { r.read_unknown(bytes, t)?; } - Err(e) => return Err(e), - } - } - Ok(msg) - } -} - -impl MessageWrite for DialResponse { - fn get_size(&self) -> usize { - 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.statusText.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) - + self.addr.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) - } - - fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - if let Some(ref s) = self.statusText { w.write_with_tag(18, |w| w.write_string(&**s))?; } - if let Some(ref s) = self.addr { w.write_with_tag(26, |w| w.write_bytes(&**s))?; } - Ok(()) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum MessageType { - DIAL = 0, - DIAL_RESPONSE = 1, -} - -impl Default for MessageType { - fn default() -> Self { - MessageType::DIAL - } -} - -impl From for MessageType { - fn from(i: i32) -> Self { - match i { - 0 => MessageType::DIAL, - 1 => MessageType::DIAL_RESPONSE, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for MessageType { - fn from(s: &'a str) -> Self { - match s { - "DIAL" => MessageType::DIAL, - "DIAL_RESPONSE" => MessageType::DIAL_RESPONSE, - _ => Self::default(), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum ResponseStatus { - OK = 0, - E_DIAL_ERROR = 100, - E_DIAL_REFUSED = 101, - E_BAD_REQUEST = 200, - E_INTERNAL_ERROR = 300, -} - -impl Default for ResponseStatus { - fn default() -> Self { - ResponseStatus::OK - } -} - -impl From for ResponseStatus { - fn from(i: i32) -> Self { - match i { - 0 => ResponseStatus::OK, - 100 => ResponseStatus::E_DIAL_ERROR, - 101 => ResponseStatus::E_DIAL_REFUSED, - 200 => ResponseStatus::E_BAD_REQUEST, - 300 => ResponseStatus::E_INTERNAL_ERROR, - _ => Self::default(), - } - } -} - -impl<'a> From<&'a str> for ResponseStatus { - fn from(s: &'a str) -> Self { - match s { - "OK" => ResponseStatus::OK, - "E_DIAL_ERROR" => ResponseStatus::E_DIAL_ERROR, - "E_DIAL_REFUSED" => ResponseStatus::E_DIAL_REFUSED, - "E_BAD_REQUEST" => ResponseStatus::E_BAD_REQUEST, - "E_INTERNAL_ERROR" => ResponseStatus::E_INTERNAL_ERROR, - _ => Self::default(), - } - } -} - -} - diff --git a/protocols/autonat/src/protocol.rs b/protocols/autonat/src/protocol.rs deleted file mode 100644 index 2ce538fddf4..00000000000 --- a/protocols/autonat/src/protocol.rs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2021 Protocol Labs. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::proto; -use async_trait::async_trait; -use asynchronous_codec::{FramedRead, FramedWrite}; -use futures::io::{AsyncRead, AsyncWrite}; -use futures::{SinkExt, StreamExt}; -use libp2p_core::Multiaddr; -use libp2p_identity::PeerId; -use libp2p_request_response::{self as request_response}; -use libp2p_swarm::StreamProtocol; -use std::io; - -/// The protocol name used for negotiating with multistream-select. -pub const DEFAULT_PROTOCOL_NAME: StreamProtocol = StreamProtocol::new("/libp2p/autonat/1.0.0"); - -#[derive(Clone)] -pub struct AutoNatCodec; - -#[async_trait] -impl request_response::Codec for AutoNatCodec { - type Protocol = StreamProtocol; - type Request = DialRequest; - type Response = DialResponse; - - async fn read_request(&mut self, _: &StreamProtocol, io: &mut T) -> io::Result - where - T: AsyncRead + Send + Unpin, - { - let message = FramedRead::new(io, codec()) - .next() - .await - .ok_or(io::ErrorKind::UnexpectedEof)??; - let request = DialRequest::from_proto(message)?; - - Ok(request) - } - - async fn read_response( - &mut self, - _: &StreamProtocol, - io: &mut T, - ) -> io::Result - where - T: AsyncRead + Send + Unpin, - { - let message = FramedRead::new(io, codec()) - .next() - .await - .ok_or(io::ErrorKind::UnexpectedEof)??; - let response = DialResponse::from_proto(message)?; - - Ok(response) - } - - async fn write_request( - &mut self, - _: &StreamProtocol, - io: &mut T, - data: Self::Request, - ) -> io::Result<()> - where - T: AsyncWrite + Send + Unpin, - { - let mut framed = FramedWrite::new(io, codec()); - framed.send(data.into_proto()).await?; - framed.close().await?; - - Ok(()) - } - - async fn write_response( - &mut self, - _: &StreamProtocol, - io: &mut T, - data: Self::Response, - ) -> io::Result<()> - where - T: AsyncWrite + Send + Unpin, - { - let mut framed = FramedWrite::new(io, codec()); - framed.send(data.into_proto()).await?; - framed.close().await?; - - Ok(()) - } -} - -fn codec() -> quick_protobuf_codec::Codec { - quick_protobuf_codec::Codec::::new(1024) -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DialRequest { - pub peer_id: PeerId, - pub addresses: Vec, -} - -impl DialRequest { - pub fn from_proto(msg: proto::Message) -> Result { - if msg.type_pb != Some(proto::MessageType::DIAL) { - return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid type")); - } - - let peer_id_result = msg.dial.and_then(|dial| { - dial.peer - .and_then(|peer_info| peer_info.id.map(|peer_id| (peer_id, peer_info.addrs))) - }); - - let (peer_id, addrs) = peer_id_result - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid dial message"))?; - - let peer_id = { - PeerId::try_from(peer_id.to_vec()) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid peer id"))? - }; - - let addrs = addrs - .into_iter() - .filter_map(|a| match Multiaddr::try_from(a.to_vec()) { - Ok(a) => Some(a), - Err(e) => { - tracing::debug!("Unable to parse multiaddr: {e}"); - None - } - }) - .collect(); - Ok(Self { - peer_id, - addresses: addrs, - }) - } - - pub fn into_proto(self) -> proto::Message { - let peer_id = self.peer_id.to_bytes(); - let addrs = self - .addresses - .into_iter() - .map(|addr| addr.to_vec()) - .collect(); - - proto::Message { - type_pb: Some(proto::MessageType::DIAL), - dial: Some(proto::Dial { - peer: Some(proto::PeerInfo { - id: Some(peer_id.to_vec()), - addrs, - }), - }), - dialResponse: None, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum ResponseError { - DialError, - DialRefused, - BadRequest, - InternalError, -} - -impl From for proto::ResponseStatus { - fn from(t: ResponseError) -> Self { - match t { - ResponseError::DialError => proto::ResponseStatus::E_DIAL_ERROR, - ResponseError::DialRefused => proto::ResponseStatus::E_DIAL_REFUSED, - ResponseError::BadRequest => proto::ResponseStatus::E_BAD_REQUEST, - ResponseError::InternalError => proto::ResponseStatus::E_INTERNAL_ERROR, - } - } -} - -impl TryFrom for ResponseError { - type Error = io::Error; - - fn try_from(value: proto::ResponseStatus) -> Result { - match value { - proto::ResponseStatus::E_DIAL_ERROR => Ok(ResponseError::DialError), - proto::ResponseStatus::E_DIAL_REFUSED => Ok(ResponseError::DialRefused), - proto::ResponseStatus::E_BAD_REQUEST => Ok(ResponseError::BadRequest), - proto::ResponseStatus::E_INTERNAL_ERROR => Ok(ResponseError::InternalError), - proto::ResponseStatus::OK => { - tracing::debug!("Received response with status code OK but expected error"); - Err(io::Error::new( - io::ErrorKind::InvalidData, - "invalid response error type", - )) - } - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DialResponse { - pub status_text: Option, - pub result: Result, -} - -impl DialResponse { - pub fn from_proto(msg: proto::Message) -> Result { - if msg.type_pb != Some(proto::MessageType::DIAL_RESPONSE) { - return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid type")); - } - - Ok(match msg.dialResponse { - Some(proto::DialResponse { - status: Some(proto::ResponseStatus::OK), - statusText, - addr: Some(addr), - }) => { - let addr = Multiaddr::try_from(addr.to_vec()) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - Self { - status_text: statusText, - result: Ok(addr), - } - } - Some(proto::DialResponse { - status: Some(status), - statusText, - addr: None, - }) => Self { - status_text: statusText, - result: Err(ResponseError::try_from(status)?), - }, - _ => { - tracing::debug!("Received malformed response message"); - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "invalid dial response message", - )); - } - }) - } - - pub fn into_proto(self) -> proto::Message { - let dial_response = match self.result { - Ok(addr) => proto::DialResponse { - status: Some(proto::ResponseStatus::OK), - statusText: self.status_text, - addr: Some(addr.to_vec()), - }, - Err(error) => proto::DialResponse { - status: Some(error.into()), - statusText: self.status_text, - addr: None, - }, - }; - - proto::Message { - type_pb: Some(proto::MessageType::DIAL_RESPONSE), - dial: None, - dialResponse: Some(dial_response), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_request_encode_decode() { - let request = DialRequest { - peer_id: PeerId::random(), - addresses: vec![ - "/ip4/8.8.8.8/tcp/30333".parse().unwrap(), - "/ip4/192.168.1.42/tcp/30333".parse().unwrap(), - ], - }; - let proto = request.clone().into_proto(); - let request2 = DialRequest::from_proto(proto).unwrap(); - assert_eq!(request, request2); - } - - #[test] - fn test_response_ok_encode_decode() { - let response = DialResponse { - result: Ok("/ip4/8.8.8.8/tcp/30333".parse().unwrap()), - status_text: None, - }; - let proto = response.clone().into_proto(); - let response2 = DialResponse::from_proto(proto).unwrap(); - assert_eq!(response, response2); - } - - #[test] - fn test_response_err_encode_decode() { - let response = DialResponse { - result: Err(ResponseError::DialError), - status_text: Some("dial failed".to_string()), - }; - let proto = response.clone().into_proto(); - let response2 = DialResponse::from_proto(proto).unwrap(); - assert_eq!(response, response2); - } - - #[test] - fn test_skip_unparsable_multiaddr() { - let valid_multiaddr: Multiaddr = "/ip6/2001:db8::/tcp/1234".parse().unwrap(); - let valid_multiaddr_bytes = valid_multiaddr.to_vec(); - - let invalid_multiaddr = { - let a = vec![255; 8]; - assert!(Multiaddr::try_from(a.clone()).is_err()); - a - }; - - let msg = proto::Message { - type_pb: Some(proto::MessageType::DIAL), - dial: Some(proto::Dial { - peer: Some(proto::PeerInfo { - id: Some(PeerId::random().to_bytes()), - addrs: vec![valid_multiaddr_bytes, invalid_multiaddr], - }), - }), - dialResponse: None, - }; - - let request = DialRequest::from_proto(msg).expect("not to fail"); - - assert_eq!(request.addresses, vec![valid_multiaddr]) - } -} From 03c2717561ded9f894e354f3ce2b2d3b96a221f5 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 14:59:34 +0900 Subject: [PATCH 163/179] Detect if local host supports ipv6 --- Cargo.lock | 13 + protocols/autonat/Cargo.toml | 4 +- protocols/autonat/src/v2.rs | 1 + protocols/autonat/src/v2/global_ip.rs | 249 ++++++++++++++++++ .../src/v2/server/handler/dial_request.rs | 49 +++- 5 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 protocols/autonat/src/v2/global_ip.rs diff --git a/Cargo.lock b/Cargo.lock index 2d9f3542d93..786ab4fe3f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2704,6 +2704,7 @@ dependencies = [ "libp2p-request-response", "libp2p-swarm", "libp2p-swarm-test", + "network-interface", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", @@ -3974,6 +3975,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "network-interface" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433419f898328beca4f2c6c73a1b52540658d92b0a99f0269330457e0fd998d5" +dependencies = [ + "cc", + "libc", + "thiserror", + "winapi", +] + [[package]] name = "nix" version = "0.24.3" diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 5e975b33795..9f8c524ea26 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -31,6 +31,7 @@ rand = "0.8" rand_core = { version = "0.6", optional = true } thiserror = { version = "1.0.52", optional = true } void = { version = "1", optional = true } +network-interface = { version = "2.0.0", optional = true } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt", "sync"]} @@ -43,7 +44,8 @@ libp2p-swarm = { workspace = true, features = ["macros"]} [features] default = ["v1", "v2"] v1 = ["dep:libp2p-request-response"] -v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", + "dep:rand_core", "dep:network-interface"] [lints] workspace = true diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 8cab10a1d14..6d9f36f1d8b 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -3,6 +3,7 @@ use libp2p_swarm::StreamProtocol; pub mod client; pub(crate) mod protocol; pub mod server; +mod global_ip; pub(crate) mod generated { #![allow(unreachable_pub)] diff --git a/protocols/autonat/src/v2/global_ip.rs b/protocols/autonat/src/v2/global_ip.rs new file mode 100644 index 00000000000..811c258cec7 --- /dev/null +++ b/protocols/autonat/src/v2/global_ip.rs @@ -0,0 +1,249 @@ +// Copyright 2023 Protocol Labs +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +/// This module contains an implementation of the `is_global` IPv4 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) +pub(crate) mod ipv4_global { + use std::net::Ipv4Addr; + + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + #[must_use] + #[inline] + const fn is_reserved(a: Ipv4Addr) -> bool { + a.octets()[0] & 240 == 240 && !a.is_broadcast() + } + + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + #[must_use] + #[inline] + const fn is_benchmarking(a: Ipv4Addr) -> bool { + a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 + } + + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + #[must_use] + #[inline] + const fn is_shared(a: Ipv4Addr) -> bool { + a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + #[must_use] + #[inline] + const fn is_private(a: Ipv4Addr) -> bool { + match a.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, + } + } + + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + #[must_use] + #[inline] + pub(crate) const fn is_global(a: Ipv4Addr) -> bool { + !(a.octets()[0] == 0 // "This network" + || is_private(a) + || is_shared(a) + || a.is_loopback() + || a.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) + || a.is_documentation() + || is_benchmarking(a) + || is_reserved(a) + || a.is_broadcast()) + } +} + +/// This module contains an implementation of the `is_global` IPv6 address space. +/// +/// Credit for this implementation goes to the Rust standard library team. +/// +/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) +pub(crate) mod ipv6_global { + use std::net::Ipv6Addr; + + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + #[inline] + const fn is_unicast_link_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xffc0) == 0xfe80 + } + + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + #[must_use] + #[inline] + const fn is_unique_local(a: Ipv6Addr) -> bool { + (a.segments()[0] & 0xfe00) == 0xfc00 + } + + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + #[must_use] + #[inline] + const fn is_documentation(a: Ipv6Addr) -> bool { + (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) + } + + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + #[must_use] + #[inline] + pub(crate) const fn is_global(a: Ipv6Addr) -> bool { + !(a.is_unspecified() + || a.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || is_documentation(a) + || is_unique_local(a) + || is_unicast_link_local(a)) + } +} diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index 9a3729d4ccf..a4927299c23 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -1,5 +1,6 @@ use std::{ io, + sync::OnceLock, task::{Context, Poll}, time::Duration, }; @@ -11,6 +12,7 @@ use futures::{ }; use futures_bounded::FuturesSet; use libp2p_core::{ + multiaddr::Protocol, upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, }; @@ -19,10 +21,12 @@ use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; +use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig}; use rand_core::RngCore; use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, + global_ip::{ipv4_global, ipv6_global}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::Event, Nonce, DIAL_REQUEST_PROTOCOL, @@ -265,7 +269,7 @@ async fn handle_request_internal( where I: AsyncRead + AsyncWrite + Unpin, { - let DialRequest { mut addrs, nonce } = match coder + let DialRequest { addrs, nonce } = match coder .next() .await .map_err(|_| HandleFail::InternalError(0))? @@ -276,8 +280,17 @@ where } }; all_addrs.clone_from(&addrs); - let idx = 0; - let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; + let (idx, addr) = addrs + .into_iter() + .enumerate() + .find(|(_, addr)| { + addr.iter().any(|proto| match proto { + Protocol::Ip4(addr) if ipv4_global::is_global(addr) => support_ipv4(), + Protocol::Ip6(addr) if ipv6_global::is_global(addr) => support_ipv6(), + _ => false, + }) + }) + .ok_or(HandleFail::DialRefused)?; *tested_addrs = Some(addr.clone()); *data_amount = 0; if addr != observed_multiaddr { @@ -330,3 +343,33 @@ where dial_status: DialStatus::OK, }) } + +fn support_ipv4() -> bool { + static IPV4_SUPPORT: OnceLock = OnceLock::new(); + *IPV4_SUPPORT.get_or_init(|| { + NetworkInterface::show() + .unwrap_or_default() + .iter() + .any(|interface| { + interface + .addr + .iter() + .any(|addr| matches!(addr, Addr::V4(a) if ipv4_global::is_global(a.ip) )) + }) + }) +} + +fn support_ipv6() -> bool { + static IPV6_SUPPORT: OnceLock = OnceLock::new(); + *IPV6_SUPPORT.get_or_init(|| { + NetworkInterface::show() + .unwrap_or_default() + .iter() + .any(|interface| { + interface + .addr + .iter() + .any(|addr| matches!(addr, Addr::V6(a) if ipv6_global::is_global(a.ip) )) + }) + }) +} From 236b356ac00adddac54ab2abf457e0dfe1fb70c8 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 15:16:50 +0900 Subject: [PATCH 164/179] Revert "Detect if local host supports ipv6" This reverts commit 03c2717561ded9f894e354f3ce2b2d3b96a221f5. --- Cargo.lock | 13 - protocols/autonat/Cargo.toml | 4 +- protocols/autonat/src/v2.rs | 1 - protocols/autonat/src/v2/global_ip.rs | 249 ------------------ .../src/v2/server/handler/dial_request.rs | 49 +--- 5 files changed, 4 insertions(+), 312 deletions(-) delete mode 100644 protocols/autonat/src/v2/global_ip.rs diff --git a/Cargo.lock b/Cargo.lock index 786ab4fe3f3..2d9f3542d93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2704,7 +2704,6 @@ dependencies = [ "libp2p-request-response", "libp2p-swarm", "libp2p-swarm-test", - "network-interface", "quick-protobuf", "quick-protobuf-codec", "rand 0.8.5", @@ -3975,18 +3974,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "network-interface" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433419f898328beca4f2c6c73a1b52540658d92b0a99f0269330457e0fd998d5" -dependencies = [ - "cc", - "libc", - "thiserror", - "winapi", -] - [[package]] name = "nix" version = "0.24.3" diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 9f8c524ea26..5e975b33795 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -31,7 +31,6 @@ rand = "0.8" rand_core = { version = "0.6", optional = true } thiserror = { version = "1.0.52", optional = true } void = { version = "1", optional = true } -network-interface = { version = "2.0.0", optional = true } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt", "sync"]} @@ -44,8 +43,7 @@ libp2p-swarm = { workspace = true, features = ["macros"]} [features] default = ["v1", "v2"] v1 = ["dep:libp2p-request-response"] -v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", - "dep:rand_core", "dep:network-interface"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] [lints] workspace = true diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 6d9f36f1d8b..8cab10a1d14 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -3,7 +3,6 @@ use libp2p_swarm::StreamProtocol; pub mod client; pub(crate) mod protocol; pub mod server; -mod global_ip; pub(crate) mod generated { #![allow(unreachable_pub)] diff --git a/protocols/autonat/src/v2/global_ip.rs b/protocols/autonat/src/v2/global_ip.rs deleted file mode 100644 index 811c258cec7..00000000000 --- a/protocols/autonat/src/v2/global_ip.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2023 Protocol Labs -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -/// This module contains an implementation of the `is_global` IPv4 address space. -/// -/// Credit for this implementation goes to the Rust standard library team. -/// -/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) -pub(crate) mod ipv4_global { - use std::net::Ipv4Addr; - - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - #[must_use] - #[inline] - const fn is_reserved(a: Ipv4Addr) -> bool { - a.octets()[0] & 240 == 240 && !a.is_broadcast() - } - - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - #[must_use] - #[inline] - const fn is_benchmarking(a: Ipv4Addr) -> bool { - a.octets()[0] == 198 && (a.octets()[1] & 0xfe) == 18 - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - #[must_use] - #[inline] - const fn is_shared(a: Ipv4Addr) -> bool { - a.octets()[0] == 100 && (a.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - #[must_use] - #[inline] - const fn is_private(a: Ipv4Addr) -> bool { - match a.octets() { - [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, - [192, 168, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - #[must_use] - #[inline] - pub(crate) const fn is_global(a: Ipv4Addr) -> bool { - !(a.octets()[0] == 0 // "This network" - || is_private(a) - || is_shared(a) - || a.is_loopback() - || a.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(a.octets()[0] == 192 && a.octets()[1] == 0 && a.octets()[2] == 0) - || a.is_documentation() - || is_benchmarking(a) - || is_reserved(a) - || a.is_broadcast()) - } -} - -/// This module contains an implementation of the `is_global` IPv6 address space. -/// -/// Credit for this implementation goes to the Rust standard library team. -/// -/// Unstable tracking issue: [#27709](https://github.com/rust-lang/rust/issues/27709) -pub(crate) mod ipv6_global { - use std::net::Ipv6Addr; - - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - #[inline] - const fn is_unicast_link_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xffc0) == 0xfe80 - } - - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - #[must_use] - #[inline] - const fn is_unique_local(a: Ipv6Addr) -> bool { - (a.segments()[0] & 0xfe00) == 0xfc00 - } - - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - #[must_use] - #[inline] - const fn is_documentation(a: Ipv6Addr) -> bool { - (a.segments()[0] == 0x2001) && (a.segments()[1] == 0xdb8) - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - #[must_use] - #[inline] - pub(crate) const fn is_global(a: Ipv6Addr) -> bool { - !(a.is_unspecified() - || a.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(a.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(a.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(a.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(a.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(a.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(a.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(a.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || is_documentation(a) - || is_unique_local(a) - || is_unicast_link_local(a)) - } -} diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index a4927299c23..9a3729d4ccf 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -1,6 +1,5 @@ use std::{ io, - sync::OnceLock, task::{Context, Poll}, time::Duration, }; @@ -12,7 +11,6 @@ use futures::{ }; use futures_bounded::FuturesSet; use libp2p_core::{ - multiaddr::Protocol, upgrade::{DeniedUpgrade, ReadyUpgrade}, Multiaddr, }; @@ -21,12 +19,10 @@ use libp2p_swarm::{ handler::{ConnectionEvent, FullyNegotiatedInbound, ListenUpgradeError}, ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, SubstreamProtocol, }; -use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig}; use rand_core::RngCore; use crate::v2::{ generated::structs::{mod_DialResponse::ResponseStatus, DialStatus}, - global_ip::{ipv4_global, ipv6_global}, protocol::{Coder, DialDataRequest, DialRequest, DialResponse, Request, Response}, server::behaviour::Event, Nonce, DIAL_REQUEST_PROTOCOL, @@ -269,7 +265,7 @@ async fn handle_request_internal( where I: AsyncRead + AsyncWrite + Unpin, { - let DialRequest { addrs, nonce } = match coder + let DialRequest { mut addrs, nonce } = match coder .next() .await .map_err(|_| HandleFail::InternalError(0))? @@ -280,17 +276,8 @@ where } }; all_addrs.clone_from(&addrs); - let (idx, addr) = addrs - .into_iter() - .enumerate() - .find(|(_, addr)| { - addr.iter().any(|proto| match proto { - Protocol::Ip4(addr) if ipv4_global::is_global(addr) => support_ipv4(), - Protocol::Ip6(addr) if ipv6_global::is_global(addr) => support_ipv6(), - _ => false, - }) - }) - .ok_or(HandleFail::DialRefused)?; + let idx = 0; + let addr = addrs.pop().ok_or(HandleFail::DialRefused)?; *tested_addrs = Some(addr.clone()); *data_amount = 0; if addr != observed_multiaddr { @@ -343,33 +330,3 @@ where dial_status: DialStatus::OK, }) } - -fn support_ipv4() -> bool { - static IPV4_SUPPORT: OnceLock = OnceLock::new(); - *IPV4_SUPPORT.get_or_init(|| { - NetworkInterface::show() - .unwrap_or_default() - .iter() - .any(|interface| { - interface - .addr - .iter() - .any(|addr| matches!(addr, Addr::V4(a) if ipv4_global::is_global(a.ip) )) - }) - }) -} - -fn support_ipv6() -> bool { - static IPV6_SUPPORT: OnceLock = OnceLock::new(); - *IPV6_SUPPORT.get_or_init(|| { - NetworkInterface::show() - .unwrap_or_default() - .iter() - .any(|interface| { - interface - .addr - .iter() - .any(|addr| matches!(addr, Addr::V6(a) if ipv6_global::is_global(a.ip) )) - }) - }) -} From b0adbd7434302f6ffba275d4bf4ee422c38dbb3f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:45:06 +0900 Subject: [PATCH 165/179] Detect reachability through catching the error --- Cargo.lock | 1 + protocols/autonat/Cargo.toml | 4 ++- protocols/autonat/src/v2/server/behaviour.rs | 31 ++++++++++++++++--- .../src/v2/server/handler/dial_request.rs | 9 ++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d9f3542d93..7c5af4812d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2698,6 +2698,7 @@ dependencies = [ "futures-bounded", "futures-timer", "instant", + "libc", "libp2p-core", "libp2p-identify", "libp2p-identity", diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 5e975b33795..c7498d13fbc 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -31,6 +31,7 @@ rand = "0.8" rand_core = { version = "0.6", optional = true } thiserror = { version = "1.0.52", optional = true } void = { version = "1", optional = true } +libc = { version = "0.2", optional = true } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt", "sync"]} @@ -43,7 +44,8 @@ libp2p-swarm = { workspace = true, features = ["macros"]} [features] default = ["v1", "v2"] v1 = ["dep:libp2p-request-response"] -v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", + "dep:rand_core", "dep:libc"] [lints] workspace = true diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 97446c6263f..c710e606f0d 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -6,13 +6,16 @@ use std::{ use crate::v2::server::handler::dial_request::DialBackStatus; use either::Either; -use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; +use libp2p_core::{ + transport::{PortUse, TransportError}, + Endpoint, Multiaddr, +}; use libp2p_identity::PeerId; -use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, ToSwarm, }; +use libp2p_swarm::{dial_opts::PeerCondition, DialError}; use rand_core::{OsRng, RngCore}; use crate::v2::server::handler::{ @@ -91,11 +94,25 @@ where } fn on_swarm_event(&mut self, event: FromSwarm) { - if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { + if let FromSwarm::DialFailure(DialFailure { + connection_id, + error, + .. + }) = event + { if let Some(DialBackCommand { back_channel, .. }) = self.dialing_dial_back.remove(&connection_id) { - let _ = back_channel.send(Err(DialBackStatus::DialErr)); + let dial_back_status = if let DialError::Transport(errors) = error { + if errors.into_iter().any(|(_, e)| matches!(e, TransportError::Other(ie) if is_network_unreachable(ie))) { + DialBackStatus::Unreachable + } else { + DialBackStatus::DialErr + } + } else { + DialBackStatus::DialErr + }; + let _ = back_channel.send(Err(dial_back_status)); } } } @@ -153,3 +170,9 @@ pub struct Event { /// The result of the test. pub result: Result<(), io::Error>, } + +fn is_network_unreachable(err: &io::Error) -> bool { + err.raw_os_error() + .map(|e| e == libc::ENETUNREACH) + .unwrap_or(false) +} diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index 9a3729d4ccf..f008c7da733 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -34,6 +34,8 @@ pub(crate) enum DialBackStatus { DialErr, /// Failure during dial back DialBackErr, + /// Unreachable + Unreachable, } #[derive(Debug)] @@ -185,6 +187,13 @@ impl From for DialResponse { dial_status: match result { Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, + Err(DialBackStatus::Unreachable) => { + return Self { + status: ResponseStatus::E_DIAL_REFUSED, + addr_idx: 0, + dial_status: DialStatus::UNUSED, + } + } Ok(()) => DialStatus::OK, }, }, From 4fac6da3ad1413942991c575c27c6e58a654b797 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:52:44 +0900 Subject: [PATCH 166/179] Compile on wasm and make clippy happy --- protocols/autonat/src/v2/server/behaviour.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index c710e606f0d..1074a29a658 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -104,7 +104,7 @@ where self.dialing_dial_back.remove(&connection_id) { let dial_back_status = if let DialError::Transport(errors) = error { - if errors.into_iter().any(|(_, e)| matches!(e, TransportError::Other(ie) if is_network_unreachable(ie))) { + if errors.iter().any(|(_, e)| matches!(e, TransportError::Other(ie) if is_network_unreachable(ie))) { DialBackStatus::Unreachable } else { DialBackStatus::DialErr @@ -171,8 +171,14 @@ pub struct Event { pub result: Result<(), io::Error>, } +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] fn is_network_unreachable(err: &io::Error) -> bool { err.raw_os_error() .map(|e| e == libc::ENETUNREACH) .unwrap_or(false) } + +#[cfg(all(target_family = "wasm", target_os = "unknown"))] +fn is_network_unreachable(_err: &io::Error) -> bool { + false +} From ff806a3aa7f3c751ebd8ff9ebcd025fff8135655 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Fri, 14 Jun 2024 18:31:41 +0900 Subject: [PATCH 167/179] Switch to protobuf version 3 --- .../autonat/src/v2/generated/structs.proto | 2 +- protocols/autonat/src/v2/generated/structs.rs | 72 +++++++++---------- protocols/autonat/src/v2/protocol.rs | 70 ++++++------------ 3 files changed, 60 insertions(+), 84 deletions(-) diff --git a/protocols/autonat/src/v2/generated/structs.proto b/protocols/autonat/src/v2/generated/structs.proto index 31791463956..d298f43d047 100644 --- a/protocols/autonat/src/v2/generated/structs.proto +++ b/protocols/autonat/src/v2/generated/structs.proto @@ -1,4 +1,4 @@ -syntax = "proto2"; +syntax = "proto3"; package structs; diff --git a/protocols/autonat/src/v2/generated/structs.rs b/protocols/autonat/src/v2/generated/structs.rs index 12568dd0364..e188adb8a42 100644 --- a/protocols/autonat/src/v2/generated/structs.rs +++ b/protocols/autonat/src/v2/generated/structs.rs @@ -120,7 +120,7 @@ impl Default for OneOfmsg { #[derive(Debug, Default, PartialEq, Clone)] pub struct DialRequest { pub addrs: Vec>, - pub nonce: Option, + pub nonce: u64, } impl<'a> MessageRead<'a> for DialRequest { @@ -129,7 +129,7 @@ impl<'a> MessageRead<'a> for DialRequest { while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.addrs.push(r.read_bytes(bytes)?.to_owned()), - Ok(17) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(17) => msg.nonce = r.read_fixed64(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -142,12 +142,12 @@ impl MessageWrite for DialRequest { fn get_size(&self) -> usize { 0 + self.addrs.iter().map(|s| 1 + sizeof_len((s).len())).sum::() - + self.nonce.as_ref().map_or(0, |_| 1 + 8) + + if self.nonce == 0u64 { 0 } else { 1 + 8 } } fn write_message(&self, w: &mut Writer) -> Result<()> { for s in &self.addrs { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } - if let Some(ref s) = self.nonce { w.write_with_tag(17, |w| w.write_fixed64(*s))?; } + if self.nonce != 0u64 { w.write_with_tag(17, |w| w.write_fixed64(*&self.nonce))?; } Ok(()) } } @@ -155,8 +155,8 @@ impl MessageWrite for DialRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct DialDataRequest { - pub addrIdx: Option, - pub numBytes: Option, + pub addrIdx: u32, + pub numBytes: u64, } impl<'a> MessageRead<'a> for DialDataRequest { @@ -164,8 +164,8 @@ impl<'a> MessageRead<'a> for DialDataRequest { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(8) => msg.addrIdx = Some(r.read_uint32(bytes)?), - Ok(16) => msg.numBytes = Some(r.read_uint64(bytes)?), + Ok(8) => msg.addrIdx = r.read_uint32(bytes)?, + Ok(16) => msg.numBytes = r.read_uint64(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -177,13 +177,13 @@ impl<'a> MessageRead<'a> for DialDataRequest { impl MessageWrite for DialDataRequest { fn get_size(&self) -> usize { 0 - + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.numBytes.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + if self.addrIdx == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.addrIdx) as u64) } + + if self.numBytes == 0u64 { 0 } else { 1 + sizeof_varint(*(&self.numBytes) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.addrIdx { w.write_with_tag(8, |w| w.write_uint32(*s))?; } - if let Some(ref s) = self.numBytes { w.write_with_tag(16, |w| w.write_uint64(*s))?; } + if self.addrIdx != 0u32 { w.write_with_tag(8, |w| w.write_uint32(*&self.addrIdx))?; } + if self.numBytes != 0u64 { w.write_with_tag(16, |w| w.write_uint64(*&self.numBytes))?; } Ok(()) } } @@ -191,9 +191,9 @@ impl MessageWrite for DialDataRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct DialResponse { - pub status: Option, - pub addrIdx: Option, - pub dialStatus: Option, + pub status: structs::mod_DialResponse::ResponseStatus, + pub addrIdx: u32, + pub dialStatus: structs::DialStatus, } impl<'a> MessageRead<'a> for DialResponse { @@ -201,9 +201,9 @@ impl<'a> MessageRead<'a> for DialResponse { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), - Ok(16) => msg.addrIdx = Some(r.read_uint32(bytes)?), - Ok(24) => msg.dialStatus = Some(r.read_enum(bytes)?), + Ok(8) => msg.status = r.read_enum(bytes)?, + Ok(16) => msg.addrIdx = r.read_uint32(bytes)?, + Ok(24) => msg.dialStatus = r.read_enum(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -215,15 +215,15 @@ impl<'a> MessageRead<'a> for DialResponse { impl MessageWrite for DialResponse { fn get_size(&self) -> usize { 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.addrIdx.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) - + self.dialStatus.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + if self.status == structs::mod_DialResponse::ResponseStatus::E_INTERNAL_ERROR { 0 } else { 1 + sizeof_varint(*(&self.status) as u64) } + + if self.addrIdx == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.addrIdx) as u64) } + + if self.dialStatus == structs::DialStatus::UNUSED { 0 } else { 1 + sizeof_varint(*(&self.dialStatus) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } - if let Some(ref s) = self.addrIdx { w.write_with_tag(16, |w| w.write_uint32(*s))?; } - if let Some(ref s) = self.dialStatus { w.write_with_tag(24, |w| w.write_enum(*s as i32))?; } + if self.status != structs::mod_DialResponse::ResponseStatus::E_INTERNAL_ERROR { w.write_with_tag(8, |w| w.write_enum(*&self.status as i32))?; } + if self.addrIdx != 0u32 { w.write_with_tag(16, |w| w.write_uint32(*&self.addrIdx))?; } + if self.dialStatus != structs::DialStatus::UNUSED { w.write_with_tag(24, |w| w.write_enum(*&self.dialStatus as i32))?; } Ok(()) } } @@ -274,7 +274,7 @@ impl<'a> From<&'a str> for ResponseStatus { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct DialDataResponse { - pub data: Option>, + pub data: Vec, } impl<'a> MessageRead<'a> for DialDataResponse { @@ -282,7 +282,7 @@ impl<'a> MessageRead<'a> for DialDataResponse { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(10) => msg.data = Some(r.read_bytes(bytes)?.to_owned()), + Ok(10) => msg.data = r.read_bytes(bytes)?.to_owned(), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -294,11 +294,11 @@ impl<'a> MessageRead<'a> for DialDataResponse { impl MessageWrite for DialDataResponse { fn get_size(&self) -> usize { 0 - + self.data.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + + if self.data.is_empty() { 0 } else { 1 + sizeof_len((&self.data).len()) } } fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.data { w.write_with_tag(10, |w| w.write_bytes(&**s))?; } + if !self.data.is_empty() { w.write_with_tag(10, |w| w.write_bytes(&**&self.data))?; } Ok(()) } } @@ -306,7 +306,7 @@ impl MessageWrite for DialDataResponse { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct DialBack { - pub nonce: Option, + pub nonce: u64, } impl<'a> MessageRead<'a> for DialBack { @@ -314,7 +314,7 @@ impl<'a> MessageRead<'a> for DialBack { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(9) => msg.nonce = Some(r.read_fixed64(bytes)?), + Ok(9) => msg.nonce = r.read_fixed64(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -326,11 +326,11 @@ impl<'a> MessageRead<'a> for DialBack { impl MessageWrite for DialBack { fn get_size(&self) -> usize { 0 - + self.nonce.as_ref().map_or(0, |_| 1 + 8) + + if self.nonce == 0u64 { 0 } else { 1 + 8 } } fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.nonce { w.write_with_tag(9, |w| w.write_fixed64(*s))?; } + if self.nonce != 0u64 { w.write_with_tag(9, |w| w.write_fixed64(*&self.nonce))?; } Ok(()) } } @@ -338,7 +338,7 @@ impl MessageWrite for DialBack { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct DialBackResponse { - pub status: Option, + pub status: structs::mod_DialBackResponse::DialBackStatus, } impl<'a> MessageRead<'a> for DialBackResponse { @@ -346,7 +346,7 @@ impl<'a> MessageRead<'a> for DialBackResponse { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { - Ok(8) => msg.status = Some(r.read_enum(bytes)?), + Ok(8) => msg.status = r.read_enum(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } @@ -358,11 +358,11 @@ impl<'a> MessageRead<'a> for DialBackResponse { impl MessageWrite for DialBackResponse { fn get_size(&self) -> usize { 0 - + self.status.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + + if self.status == structs::mod_DialBackResponse::DialBackStatus::OK { 0 } else { 1 + sizeof_varint(*(&self.status) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { - if let Some(ref s) = self.status { w.write_with_tag(8, |w| w.write_enum(*s as i32))?; } + if self.status != structs::mod_DialBackResponse::DialBackStatus::OK { w.write_with_tag(8, |w| w.write_enum(*&self.status as i32))?; } Ok(()) } } diff --git a/protocols/autonat/src/v2/protocol.rs b/protocols/autonat/src/v2/protocol.rs index 126077fe936..4077fd65f5d 100644 --- a/protocols/autonat/src/v2/protocol.rs +++ b/protocols/autonat/src/v2/protocol.rs @@ -22,12 +22,6 @@ fn new_io_invalid_data_err(msg: impl Into) -> io::Error { io::Error::new(io::ErrorKind::InvalidData, msg.into()) } -macro_rules! ok_or_invalid_data { - ($field:ident) => { - $field.ok_or_else(|| new_io_invalid_data_err(concat!(stringify!($field), " is missing"))) - }; -} - pub(crate) struct Coder { inner: Framed>, } @@ -93,7 +87,7 @@ pub(crate) enum Request { impl From for proto::Message { fn from(val: DialRequest) -> Self { let addrs = val.addrs.iter().map(|e| e.to_vec()).collect(); - let nonce = Some(val.nonce); + let nonce = val.nonce; proto::Message { msg: proto::mod_Message::OneOfmsg::dialRequest(proto::DialRequest { addrs, nonce }), @@ -109,7 +103,7 @@ impl From for proto::Message { ); proto::Message { msg: proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { - data: Some(vec![0; val.data_count]), // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI + data: vec![0; val.data_count], // One could use Cow::Borrowed here, but it will require a modification of the generated code and that will fail the CI }), } } @@ -155,11 +149,10 @@ impl TryFrom for Request { }) }) .collect::, io::Error>>()?; - let nonce = ok_or_invalid_data!(nonce)?; Ok(Self::Dial(DialRequest { addrs, nonce })) } proto::mod_Message::OneOfmsg::dialDataResponse(proto::DialDataResponse { data }) => { - let data_count = ok_or_invalid_data!(data)?.len(); + let data_count = data.len(); Ok(Self::Data(DialDataResponse { data_count })) } _ => Err(new_io_invalid_data_err( @@ -197,27 +190,18 @@ impl TryFrom for Response { status, addrIdx, dialStatus, - }) => { - let status = ok_or_invalid_data!(status)?; - let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; - let dial_status = ok_or_invalid_data!(dialStatus)?; - Ok(Response::Dial(DialResponse { - status, - addr_idx, - dial_status, - })) - } + }) => Ok(Response::Dial(DialResponse { + status, + addr_idx: addrIdx as usize, + dial_status: dialStatus, + })), proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { addrIdx, numBytes, - }) => { - let addr_idx = ok_or_invalid_data!(addrIdx)? as usize; - let num_bytes = ok_or_invalid_data!(numBytes)? as usize; - Ok(Self::Data(DialDataRequest { - addr_idx, - num_bytes, - })) - } + }) => Ok(Self::Data(DialDataRequest { + addr_idx: addrIdx as usize, + num_bytes: numBytes as usize, + })), _ => Err(new_io_invalid_data_err( "invalid message type, expected dialResponse or dialDataRequest", )), @@ -234,9 +218,9 @@ impl From for proto::Message { dial_status, }) => proto::Message { msg: proto::mod_Message::OneOfmsg::dialResponse(proto::DialResponse { - status: Some(status), - addrIdx: Some(addr_idx as u32), - dialStatus: Some(dial_status), + status, + addrIdx: addr_idx as u32, + dialStatus: dial_status, }), }, Response::Data(DialDataRequest { @@ -244,8 +228,8 @@ impl From for proto::Message { num_bytes, }) => proto::Message { msg: proto::mod_Message::OneOfmsg::dialDataRequest(proto::DialDataRequest { - addrIdx: Some(addr_idx as u32), - numBytes: Some(num_bytes as u64), + addrIdx: addr_idx as u32, + numBytes: num_bytes as u64, }), }, } @@ -265,7 +249,7 @@ impl DialDataRequest { const DIAL_BACK_MAX_SIZE: usize = 10; pub(crate) async fn dial_back(stream: impl AsyncWrite + Unpin, nonce: Nonce) -> io::Result<()> { - let msg = proto::DialBack { nonce: Some(nonce) }; + let msg = proto::DialBack { nonce }; let mut framed = FramedWrite::new(stream, Codec::::new(DIAL_BACK_MAX_SIZE)); framed @@ -282,14 +266,12 @@ pub(crate) async fn recv_dial_back(stream: impl AsyncRead + Unpin) -> io::Result .next() .await .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - let nonce = ok_or_invalid_data!(nonce)?; - Ok(nonce) } pub(crate) async fn dial_back_response(stream: impl AsyncWrite + Unpin) -> io::Result<()> { let msg = proto::DialBackResponse { - status: Some(proto::mod_DialBackResponse::DialBackStatus::OK), + status: proto::mod_DialBackResponse::DialBackStatus::OK, }; let mut framed = FramedWrite::new( stream, @@ -315,7 +297,7 @@ pub(crate) async fn recv_dial_back_response( .await .ok_or(io::Error::from(io::ErrorKind::UnexpectedEof))??; - if let Some(proto::mod_DialBackResponse::DialBackStatus::OK) = status { + if proto::mod_DialBackResponse::DialBackStatus::OK == status { Ok(()) } else { Err(io::Error::new( @@ -335,7 +317,7 @@ mod tests { fn message_correct_max_size() { let message_bytes = quick_protobuf::serialize_into_vec(&Message { msg: OneOfmsg::dialDataResponse(GenDialDataResponse { - data: Some(vec![0; 4096]), + data: vec![0; 4096], }), }) .unwrap(); @@ -344,17 +326,11 @@ mod tests { #[test] fn dial_back_correct_size() { - let dial_back = super::proto::DialBack { nonce: Some(0) }; + let dial_back = super::proto::DialBack { nonce: 0 }; let buf = quick_protobuf::serialize_into_vec(&dial_back).unwrap(); assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - let dial_back_none = super::proto::DialBack { nonce: None }; - let buf = quick_protobuf::serialize_into_vec(&dial_back_none).unwrap(); - assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); - - let dial_back_max_nonce = super::proto::DialBack { - nonce: Some(u64::MAX), - }; + let dial_back_max_nonce = super::proto::DialBack { nonce: u64::MAX }; let buf = quick_protobuf::serialize_into_vec(&dial_back_max_nonce).unwrap(); assert!(buf.len() <= super::DIAL_BACK_MAX_SIZE); } From 75a86c204086a2d19abc23ad545639cd2090c5be Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 16 Jun 2024 18:52:58 +0900 Subject: [PATCH 168/179] Revert network unreachable detection --- Cargo.lock | 1 - protocols/autonat/Cargo.toml | 4 +-- protocols/autonat/src/v2/server/behaviour.rs | 29 ++------------------ 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c5af4812d9..2d9f3542d93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2698,7 +2698,6 @@ dependencies = [ "futures-bounded", "futures-timer", "instant", - "libc", "libp2p-core", "libp2p-identify", "libp2p-identity", diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index c7498d13fbc..5e975b33795 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -31,7 +31,6 @@ rand = "0.8" rand_core = { version = "0.6", optional = true } thiserror = { version = "1.0.52", optional = true } void = { version = "1", optional = true } -libc = { version = "0.2", optional = true } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt", "sync"]} @@ -44,8 +43,7 @@ libp2p-swarm = { workspace = true, features = ["macros"]} [features] default = ["v1", "v2"] v1 = ["dep:libp2p-request-response"] -v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", - "dep:rand_core", "dep:libc"] +v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] [lints] workspace = true diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 1074a29a658..619f224ae48 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -94,24 +94,11 @@ where } fn on_swarm_event(&mut self, event: FromSwarm) { - if let FromSwarm::DialFailure(DialFailure { - connection_id, - error, - .. - }) = event - { + if let FromSwarm::DialFailure(DialFailure { connection_id, .. }) = event { if let Some(DialBackCommand { back_channel, .. }) = self.dialing_dial_back.remove(&connection_id) { - let dial_back_status = if let DialError::Transport(errors) = error { - if errors.iter().any(|(_, e)| matches!(e, TransportError::Other(ie) if is_network_unreachable(ie))) { - DialBackStatus::Unreachable - } else { - DialBackStatus::DialErr - } - } else { - DialBackStatus::DialErr - }; + let dial_back_status = DialBackStatus::DialErr; let _ = back_channel.send(Err(dial_back_status)); } } @@ -170,15 +157,3 @@ pub struct Event { /// The result of the test. pub result: Result<(), io::Error>, } - -#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] -fn is_network_unreachable(err: &io::Error) -> bool { - err.raw_os_error() - .map(|e| e == libc::ENETUNREACH) - .unwrap_or(false) -} - -#[cfg(all(target_family = "wasm", target_os = "unknown"))] -fn is_network_unreachable(_err: &io::Error) -> bool { - false -} From 5fb67150d913ae2bb5fd71b11d704641c7574a96 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sat, 3 Aug 2024 16:49:10 +0200 Subject: [PATCH 169/179] Fix issues after merge --- misc/server/src/behaviour.rs | 6 ++++-- protocols/autonat/src/v2/server/behaviour.rs | 7 ++----- protocols/autonat/src/v2/server/handler/dial_request.rs | 9 --------- swarm/src/behaviour.rs | 4 ---- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index 36b18c9798d..a6d08689dfb 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + use libp2p::autonat; use libp2p::identify; use libp2p::kad; @@ -24,7 +26,7 @@ pub(crate) struct Behaviour { ping: ping::Behaviour, identify: identify::Behaviour, pub(crate) kademlia: Toggle>, - autonat: Toggle, + autonat: Toggle, } impl Behaviour { @@ -57,7 +59,7 @@ impl Behaviour { .into(); let autonat = if enable_autonat { - Some(autonat::Behaviour::new( + Some(autonat::v1::Behaviour::new( PeerId::from(pub_key.clone()), Default::default(), )) diff --git a/protocols/autonat/src/v2/server/behaviour.rs b/protocols/autonat/src/v2/server/behaviour.rs index 619f224ae48..5f7b21d165b 100644 --- a/protocols/autonat/src/v2/server/behaviour.rs +++ b/protocols/autonat/src/v2/server/behaviour.rs @@ -6,16 +6,13 @@ use std::{ use crate::v2::server::handler::dial_request::DialBackStatus; use either::Either; -use libp2p_core::{ - transport::{PortUse, TransportError}, - Endpoint, Multiaddr, -}; +use libp2p_core::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ dial_opts::DialOpts, dummy, ConnectionDenied, ConnectionHandler, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, ToSwarm, }; -use libp2p_swarm::{dial_opts::PeerCondition, DialError}; use rand_core::{OsRng, RngCore}; use crate::v2::server::handler::{ diff --git a/protocols/autonat/src/v2/server/handler/dial_request.rs b/protocols/autonat/src/v2/server/handler/dial_request.rs index f008c7da733..9a3729d4ccf 100644 --- a/protocols/autonat/src/v2/server/handler/dial_request.rs +++ b/protocols/autonat/src/v2/server/handler/dial_request.rs @@ -34,8 +34,6 @@ pub(crate) enum DialBackStatus { DialErr, /// Failure during dial back DialBackErr, - /// Unreachable - Unreachable, } #[derive(Debug)] @@ -187,13 +185,6 @@ impl From for DialResponse { dial_status: match result { Err(DialBackStatus::DialErr) => DialStatus::E_DIAL_ERROR, Err(DialBackStatus::DialBackErr) => DialStatus::E_DIAL_BACK_ERROR, - Err(DialBackStatus::Unreachable) => { - return Self { - status: ResponseStatus::E_DIAL_REFUSED, - addr_idx: 0, - dial_status: DialStatus::UNUSED, - } - } Ok(()) => DialStatus::OK, }, }, diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 5623dd6c646..35aed12fba5 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -39,10 +39,6 @@ use libp2p_core::{ transport::{ListenerId, PortUse}, ConnectedPoint, Endpoint, Multiaddr, }; -use libp2p_core::{ - transport::{ListenerId, PortUse}, - ConnectedPoint, Endpoint, Multiaddr, -}; use libp2p_identity::PeerId; use std::{task::Context, task::Poll}; From 0a566d472db41d39ef9b21fbfb9dde1d3be60f7b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 4 Aug 2024 17:01:31 +0200 Subject: [PATCH 170/179] Add changelog entry --- protocols/autonat/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index 2a799221f7c..2ffc6d5d27c 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.14.0-alpha + +- Introduce the new AutoNATv2 protocol. + It's split into a client and a server part, represented in their respective modules + Features: + - Since the server now always dials back over a newly allocated port, this made refactor(*): Transport redesign #4568 necessary; the client can be sure of the reachability state for other peers, even if the connection to the server was made through a hole punch. + - The server can now test addresses different from the observed address (i.e., the connection to the server was made through a `p2p-circuit`). To mitigate against DDoS attacks, the client has to send more data to the server than the dial-back costs. + See [PR 5526]. +- Deprecate the now unnecessary first version of AutoNAT. See [PR 5526]. + +[PR 5526]: https://github.com/libp2p/rust-libp2p/pull/5526 + ## 0.13.0 - Due to the refactor of `Transport` it's no longer required to create a seperate transport for From 3e85ffb1308fa02fe5a50a8345ccb216f7315a72 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Sun, 4 Aug 2024 17:01:40 +0200 Subject: [PATCH 171/179] Add documentation --- protocols/autonat/src/v2.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/protocols/autonat/src/v2.rs b/protocols/autonat/src/v2.rs index 8cab10a1d14..994497cb1a0 100644 --- a/protocols/autonat/src/v2.rs +++ b/protocols/autonat/src/v2.rs @@ -1,3 +1,23 @@ +//! The second version of the autonat protocol. +//! +//! The implementation follows the [libp2p spec](https://github.com/libp2p/specs/blob/03718ef0f2dea4a756a85ba716ee33f97e4a6d6c/autonat/autonat-v2.md). +//! +//! The new version fixes the issues of the first version: +//! - The server now always dials back over a newly allocated port. This greatly reduces the risk of +//! false positives that often occured in the first version, when the clinet-server connection +//! occured over a hole-punched port. +//! - The server protects against DoS attacks by requiring the client to send more data to the +//! server then the dial back puts on the client, thus making the protocol unatractive for an +//! attacker. +//! +//! The protocol is seperated into two parts: +//! - The client part, which is implemented in the `client` module. (The client is the party that +//! wants to check if it is reachable from the outside.) +//! - The server part, which is implemented in the `server` module. (The server is the party +//! performing reachability checks on behalf of the client.) +//! +//! The two can be used together. + use libp2p_swarm::StreamProtocol; pub mod client; From 921f5f1d5949aed54d19facf3971f4e833ec6d2a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 5 Aug 2024 19:15:31 +0200 Subject: [PATCH 172/179] Undo a bad merge and update changelog for silent roll out --- Cargo.lock | 2 +- examples/autonat/src/bin/autonat_client.rs | 1 - examples/autonat/src/bin/autonat_server.rs | 1 - misc/server/src/behaviour.rs | 6 ++---- protocols/autonat/CHANGELOG.md | 13 ++++--------- protocols/autonat/Cargo.toml | 6 +++--- protocols/autonat/src/lib.rs | 1 - protocols/autonat/src/v1.rs | 5 ++++- protocols/autonat/src/v1/behaviour.rs | 2 +- protocols/autonat/src/v1/behaviour/as_client.rs | 2 +- protocols/autonat/src/v1/behaviour/as_server.rs | 2 +- protocols/autonat/tests/test_client.rs | 1 - protocols/autonat/tests/test_server.rs | 1 - 13 files changed, 17 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ed539e5d2e..af3ceb9eb0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2698,7 +2698,6 @@ dependencies = [ "futures", "futures-bounded", "futures-timer", - "instant", "libp2p-core", "libp2p-identify", "libp2p-identity", @@ -2714,6 +2713,7 @@ dependencies = [ "tracing", "tracing-subscriber", "void", + "web-time 1.1.0", ] [[package]] diff --git a/examples/autonat/src/bin/autonat_client.rs b/examples/autonat/src/bin/autonat_client.rs index 76d0970ca13..def66c4823b 100644 --- a/examples/autonat/src/bin/autonat_client.rs +++ b/examples/autonat/src/bin/autonat_client.rs @@ -19,7 +19,6 @@ // DEALINGS IN THE SOFTWARE. #![doc = include_str!("../../README.md")] -#![allow(deprecated)] use clap::Parser; use futures::StreamExt; diff --git a/examples/autonat/src/bin/autonat_server.rs b/examples/autonat/src/bin/autonat_server.rs index 9b6ccfef92c..389cc0fa26f 100644 --- a/examples/autonat/src/bin/autonat_server.rs +++ b/examples/autonat/src/bin/autonat_server.rs @@ -19,7 +19,6 @@ // DEALINGS IN THE SOFTWARE. #![doc = include_str!("../../README.md")] -#![allow(deprecated)] use clap::Parser; use futures::StreamExt; diff --git a/misc/server/src/behaviour.rs b/misc/server/src/behaviour.rs index a6d08689dfb..36b18c9798d 100644 --- a/misc/server/src/behaviour.rs +++ b/misc/server/src/behaviour.rs @@ -1,5 +1,3 @@ -#![allow(deprecated)] - use libp2p::autonat; use libp2p::identify; use libp2p::kad; @@ -26,7 +24,7 @@ pub(crate) struct Behaviour { ping: ping::Behaviour, identify: identify::Behaviour, pub(crate) kademlia: Toggle>, - autonat: Toggle, + autonat: Toggle, } impl Behaviour { @@ -59,7 +57,7 @@ impl Behaviour { .into(); let autonat = if enable_autonat { - Some(autonat::v1::Behaviour::new( + Some(autonat::Behaviour::new( PeerId::from(pub_key.clone()), Default::default(), )) diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index 2ffc6d5d27c..c0e51cdf53e 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -1,21 +1,16 @@ -## 0.14.0-alpha +## 0.13.0 +- Due to the refactor of `Transport` it's no longer required to create a seperate transport for +AutoNAT where port reuse is disabled. This information is now passed by the behaviour. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568). - Introduce the new AutoNATv2 protocol. It's split into a client and a server part, represented in their respective modules Features: - Since the server now always dials back over a newly allocated port, this made refactor(*): Transport redesign #4568 necessary; the client can be sure of the reachability state for other peers, even if the connection to the server was made through a hole punch. - The server can now test addresses different from the observed address (i.e., the connection to the server was made through a `p2p-circuit`). To mitigate against DDoS attacks, the client has to send more data to the server than the dial-back costs. See [PR 5526]. -- Deprecate the now unnecessary first version of AutoNAT. See [PR 5526]. - [PR 5526]: https://github.com/libp2p/rust-libp2p/pull/5526 -## 0.13.0 - -- Due to the refactor of `Transport` it's no longer required to create a seperate transport for -AutoNAT where port reuse is disabled. This information is now passed by the behaviour. - See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568). - ## 0.12.1 diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 5e975b33795..31214545384 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -12,14 +12,14 @@ categories = ["network-programming", "asynchronous"] [dependencies] -async-trait = "0.1" +async-trait = { version = "0.1", optional = true } asynchronous-codec = { workspace = true } bytes = { version = "1", optional = true } either = { version = "1.9.0", optional = true } futures = { workspace = true } futures-bounded = { workspace = true, optional = true } futures-timer = "3.0" -instant = "0.1" +web-time = { workspace = true, optional = true } libp2p-core = { workspace = true } libp2p-identity = { workspace = true } libp2p-request-response = { workspace = true, optional = true } @@ -42,7 +42,7 @@ libp2p-swarm = { workspace = true, features = ["macros"]} [features] default = ["v1", "v2"] -v1 = ["dep:libp2p-request-response"] +v1 = ["dep:libp2p-request-response", "dep:web-time", "dep:async-trait"] v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] [lints] diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index a6fc66b28d1..4056b84389a 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -5,5 +5,4 @@ pub mod v1; pub mod v2; #[cfg(feature = "v1")] -#[allow(deprecated)] pub use v1::*; diff --git a/protocols/autonat/src/v1.rs b/protocols/autonat/src/v1.rs index 07b08310871..c60e4805f40 100644 --- a/protocols/autonat/src/v1.rs +++ b/protocols/autonat/src/v1.rs @@ -19,9 +19,12 @@ // DEALINGS IN THE SOFTWARE. //! Implementation of the [AutoNAT](https://github.com/libp2p/specs/blob/master/autonat/README.md) protocol. +//! +//! ## Eventual Deprecation +//! This version of the protocol will eventually be deprecated in favor of [v2](crate::v2). +//! We recommend using v2 for new projects. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![cfg_attr(not(test), deprecated(note = "Please use `v2` module instead."))] pub(crate) mod behaviour; pub(crate) mod protocol; diff --git a/protocols/autonat/src/v1/behaviour.rs b/protocols/autonat/src/v1/behaviour.rs index 6e2c36a467f..7a717baed8d 100644 --- a/protocols/autonat/src/v1/behaviour.rs +++ b/protocols/autonat/src/v1/behaviour.rs @@ -28,7 +28,6 @@ pub use as_client::{OutboundProbeError, OutboundProbeEvent}; use as_server::AsServer; pub use as_server::{InboundProbeError, InboundProbeEvent}; use futures_timer::Delay; -use instant::Instant; use libp2p_core::transport::PortUse; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -46,6 +45,7 @@ use std::{ task::{Context, Poll}, time::Duration, }; +use web_time::Instant; /// Config for the [`Behaviour`]. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/protocols/autonat/src/v1/behaviour/as_client.rs b/protocols/autonat/src/v1/behaviour/as_client.rs index 668f3b93719..8960163ccb3 100644 --- a/protocols/autonat/src/v1/behaviour/as_client.rs +++ b/protocols/autonat/src/v1/behaviour/as_client.rs @@ -26,7 +26,6 @@ use super::{ }; use futures::FutureExt; use futures_timer::Delay; -use instant::Instant; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use libp2p_request_response::{self as request_response, OutboundFailure, OutboundRequestId}; @@ -37,6 +36,7 @@ use std::{ task::{Context, Poll}, time::Duration, }; +use web_time::Instant; /// Outbound probe failed or was aborted. #[derive(Debug)] diff --git a/protocols/autonat/src/v1/behaviour/as_server.rs b/protocols/autonat/src/v1/behaviour/as_server.rs index e309023bc75..3ecdd3ac26e 100644 --- a/protocols/autonat/src/v1/behaviour/as_server.rs +++ b/protocols/autonat/src/v1/behaviour/as_server.rs @@ -21,7 +21,6 @@ use super::{ Action, AutoNatCodec, Config, DialRequest, DialResponse, Event, HandleInnerEvent, ProbeId, ResponseError, }; -use instant::Instant; use libp2p_core::{multiaddr::Protocol, Multiaddr}; use libp2p_identity::PeerId; use libp2p_request_response::{ @@ -35,6 +34,7 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, num::NonZeroU8, }; +use web_time::Instant; /// Inbound probe failed. #[derive(Debug)] diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 2b1460cac85..7509d3ef425 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -17,7 +17,6 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#![allow(deprecated)] use async_std::task::JoinHandle; use libp2p_autonat::{ diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index a01c5901b8e..fd97b1a9132 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -17,7 +17,6 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#![allow(deprecated)] use libp2p_autonat::{ Behaviour, Config, Event, InboundProbeError, InboundProbeEvent, ResponseError, From bf45de8798a8226212118cf17ea916889091e33a Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:26:23 +0200 Subject: [PATCH 173/179] Update protocols/autonat/CHANGELOG.md Co-authored-by: Thomas Eizinger --- protocols/autonat/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index c0e51cdf53e..68e3da16887 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -6,7 +6,8 @@ AutoNAT where port reuse is disabled. This information is now passed by the beha - Introduce the new AutoNATv2 protocol. It's split into a client and a server part, represented in their respective modules Features: - - Since the server now always dials back over a newly allocated port, this made refactor(*): Transport redesign #4568 necessary; the client can be sure of the reachability state for other peers, even if the connection to the server was made through a hole punch. + - The server now always dials back over a newly allocated port. + This more accurately reflects the reachability state for other peers and avoids accidental hole punching. - The server can now test addresses different from the observed address (i.e., the connection to the server was made through a `p2p-circuit`). To mitigate against DDoS attacks, the client has to send more data to the server than the dial-back costs. See [PR 5526]. [PR 5526]: https://github.com/libp2p/rust-libp2p/pull/5526 From ea419f7103b60ab9bd95af8d314a02533f86d732 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:26:35 +0200 Subject: [PATCH 174/179] Update protocols/autonat/CHANGELOG.md Co-authored-by: Thomas Eizinger --- protocols/autonat/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index 68e3da16887..e171412aa58 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -9,8 +9,7 @@ AutoNAT where port reuse is disabled. This information is now passed by the beha - The server now always dials back over a newly allocated port. This more accurately reflects the reachability state for other peers and avoids accidental hole punching. - The server can now test addresses different from the observed address (i.e., the connection to the server was made through a `p2p-circuit`). To mitigate against DDoS attacks, the client has to send more data to the server than the dial-back costs. - See [PR 5526]. -[PR 5526]: https://github.com/libp2p/rust-libp2p/pull/5526 + See [PR 5526](https://github.com/libp2p/rust-libp2p/pull/5526). From 6d1b431f90c157e3fe017140062d9ba5e39c906d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:27:39 +0200 Subject: [PATCH 175/179] this file is useless --- examples/autonatv2/src/lib.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/autonatv2/src/lib.rs diff --git a/examples/autonatv2/src/lib.rs b/examples/autonatv2/src/lib.rs deleted file mode 100644 index 8b137891791..00000000000 --- a/examples/autonatv2/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - From 431d7b6d87f37b2321101e12f42dd6cdd907f6cd Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:16:45 +0200 Subject: [PATCH 176/179] Trigger CI From 475c76b0ac30fb327402b7d2253b2008331c3988 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:29:30 +0200 Subject: [PATCH 177/179] Include doc cfg --- protocols/autonat/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protocols/autonat/src/lib.rs b/protocols/autonat/src/lib.rs index 4056b84389a..e49eaadcb83 100644 --- a/protocols/autonat/src/lib.rs +++ b/protocols/autonat/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + #[cfg(feature = "v1")] pub mod v1; From 80981d931430668a0f4592aab56362d13f1191fc Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:35:18 +0200 Subject: [PATCH 178/179] Trigger CI From 61b1f2dd98cfd4c4b32ca2b1fb2d4a3ce07bd889 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:17:16 +0200 Subject: [PATCH 179/179] Correct bad merge --- protocols/autonat/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 08faf74229e..2c01d18dceb 100644 --- a/protocols/autonat/Cargo.toml +++ b/protocols/autonat/Cargo.toml @@ -45,9 +45,6 @@ default = ["v1", "v2"] v1 = ["dep:libp2p-request-response", "dep:web-time", "dep:async-trait"] v2 = ["dep:bytes", "dep:either", "dep:futures-bounded", "dep:thiserror", "dep:void", "dep:rand_core"] -[lints] -workspace = true - # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling [package.metadata.docs.rs]