From 079b2d6b99262f4f0ca42994571614787a5c98f7 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Sat, 3 Aug 2024 16:05:59 +0200 Subject: [PATCH] refactor(*): Transport redesign Resolves: #4226. Resolves: #3953. Resolves: #3889. Pull-Request: #4568. --- Cargo.lock | 38 +- Cargo.toml | 39 +- core/CHANGELOG.md | 11 + core/Cargo.toml | 2 +- core/src/connection.rs | 13 +- core/src/either.rs | 36 +- core/src/lib.rs | 3 - core/src/transport.rs | 54 +-- core/src/transport/and_then.rs | 35 +- core/src/transport/boxed.rs | 42 +- core/src/transport/choice.rs | 55 +-- core/src/transport/dummy.rs | 13 +- core/src/transport/global_only.rs | 40 +- core/src/transport/map.rs | 27 +- core/src/transport/map_err.rs | 22 +- core/src/transport/memory.rs | 65 ++- core/src/transport/optional.rs | 23 +- core/src/transport/timeout.rs | 21 +- core/src/transport/upgrade.rs | 46 +- core/src/upgrade/ready.rs | 2 +- core/tests/transport_upgrade.rs | 15 +- examples/autonat/Cargo.toml | 2 +- examples/dcutr/src/main.rs | 2 +- examples/stream/Cargo.toml | 2 +- hole-punching-tests/src/main.rs | 2 +- libp2p/CHANGELOG.md | 11 + misc/allow-block-list/src/lib.rs | 2 + misc/connection-limits/src/lib.rs | 4 +- misc/memory-connection-limits/src/lib.rs | 3 +- .../tests/util/mod.rs | 3 +- misc/metrics/src/bandwidth.rs | 21 +- misc/server/src/main.rs | 2 +- muxers/mplex/benches/split_send_size.rs | 13 +- protocols/autonat/CHANGELOG.md | 8 + protocols/autonat/Cargo.toml | 2 +- protocols/autonat/src/behaviour.rs | 13 +- protocols/autonat/src/behaviour/as_server.rs | 1 + protocols/autonat/tests/test_server.rs | 2 + protocols/dcutr/CHANGELOG.md | 4 + protocols/dcutr/Cargo.toml | 2 +- protocols/dcutr/src/behaviour.rs | 3 + protocols/floodsub/CHANGELOG.md | 4 + protocols/floodsub/Cargo.toml | 2 +- protocols/floodsub/src/layer.rs | 2 + protocols/gossipsub/CHANGELOG.md | 4 +- protocols/gossipsub/src/behaviour.rs | 5 +- protocols/gossipsub/src/behaviour/tests.rs | 6 + protocols/identify/CHANGELOG.md | 5 + protocols/identify/src/behaviour.rs | 127 ++++- protocols/identify/tests/smoke.rs | 8 +- protocols/kad/CHANGELOG.md | 1 + protocols/kad/src/behaviour.rs | 4 +- protocols/kad/src/behaviour/test.rs | 3 + protocols/mdns/CHANGELOG.md | 3 + protocols/mdns/Cargo.toml | 2 +- protocols/mdns/src/behaviour.rs | 2 + protocols/mdns/src/behaviour/iface/query.rs | 9 +- protocols/perf/CHANGELOG.md | 2 + protocols/perf/src/client/behaviour.rs | 3 +- protocols/perf/src/server/behaviour.rs | 2 + protocols/ping/CHANGELOG.md | 5 +- protocols/ping/Cargo.toml | 2 +- protocols/ping/src/lib.rs | 2 + protocols/ping/src/protocol.rs | 11 +- protocols/relay/CHANGELOG.md | 4 + protocols/relay/Cargo.toml | 2 +- protocols/relay/src/behaviour.rs | 3 + protocols/relay/src/priv_client.rs | 2 + protocols/relay/src/priv_client/transport.rs | 41 +- protocols/rendezvous/CHANGELOG.md | 4 + protocols/rendezvous/Cargo.toml | 2 +- protocols/rendezvous/src/client.rs | 11 +- protocols/rendezvous/src/server.rs | 11 +- protocols/request-response/CHANGELOG.md | 4 + protocols/request-response/Cargo.toml | 2 +- protocols/request-response/src/lib.rs | 3 +- protocols/stream/CHANGELOG.md | 4 + protocols/stream/Cargo.toml | 2 +- protocols/stream/src/behaviour.rs | 3 +- protocols/upnp/CHANGELOG.md | 4 + protocols/upnp/Cargo.toml | 2 +- protocols/upnp/src/behaviour.rs | 7 +- swarm-derive/src/lib.rs | 4 +- swarm/CHANGELOG.md | 8 + swarm/benches/connection_handler.rs | 1 + swarm/src/behaviour.rs | 6 +- swarm/src/behaviour/either.rs | 4 + swarm/src/behaviour/toggle.rs | 3 + swarm/src/connection.rs | 11 +- swarm/src/connection/pool.rs | 16 +- swarm/src/dial_opts.rs | 79 ++-- swarm/src/dummy.rs | 2 + swarm/src/lib.rs | 76 ++- swarm/src/test.rs | 12 +- {core => swarm}/src/translation.rs | 7 +- swarm/tests/connection_close.rs | 2 + swarm/tests/listener.rs | 7 +- swarm/tests/swarm_derive.rs | 4 +- transports/dns/CHANGELOG.md | 5 + transports/dns/Cargo.toml | 2 +- transports/dns/src/lib.rs | 67 ++- transports/quic/CHANGELOG.md | 5 + transports/quic/Cargo.toml | 2 +- transports/quic/src/transport.rs | 251 +++++----- transports/quic/tests/smoke.rs | 87 ++-- transports/quic/tests/stream_compliance.rs | 14 +- transports/tcp/CHANGELOG.md | 9 + transports/tcp/src/lib.rs | 447 ++++++------------ transports/uds/CHANGELOG.md | 5 + transports/uds/Cargo.toml | 2 +- transports/uds/src/lib.rs | 35 +- transports/webrtc-websys/CHANGELOG.md | 5 + transports/webrtc-websys/Cargo.toml | 2 +- transports/webrtc-websys/src/transport.rs | 22 +- transports/webrtc/CHANGELOG.md | 5 + transports/webrtc/Cargo.toml | 2 +- transports/webrtc/src/tokio/transport.rs | 31 +- transports/webrtc/tests/smoke.rs | 16 +- transports/websocket-websys/CHANGELOG.md | 4 + transports/websocket-websys/Cargo.toml | 2 +- transports/websocket-websys/src/lib.rs | 22 +- transports/websocket/CHANGELOG.md | 4 + transports/websocket/Cargo.toml | 2 +- transports/websocket/src/framed.rs | 38 +- transports/websocket/src/lib.rs | 29 +- transports/webtransport-websys/CHANGELOG.md | 5 + transports/webtransport-websys/Cargo.toml | 2 +- .../webtransport-websys/src/transport.rs | 25 +- wasm-tests/webtransport-tests/src/lib.rs | 51 +- 129 files changed, 1294 insertions(+), 1175 deletions(-) rename {core => swarm}/src/translation.rs (95%) diff --git a/Cargo.lock b/Cargo.lock index b4d877e49d6..cf0cde790c3 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", @@ -2926,7 +2926,7 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.45.2" +version = "0.46.0" dependencies = [ "async-io 2.3.3", "async-std", @@ -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", @@ -3395,7 +3395,7 @@ dependencies = [ [[package]] name = "libp2p-uds" -version = "0.40.0" +version = "0.41.0" dependencies = [ "async-std", "futures", @@ -3407,7 +3407,7 @@ dependencies = [ [[package]] name = "libp2p-upnp" -version = "0.2.2" +version = "0.3.0" dependencies = [ "futures", "futures-timer", @@ -3421,7 +3421,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.7.1-alpha" +version = "0.8.0-alpha" dependencies = [ "async-trait", "bytes", @@ -3472,7 +3472,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc-websys" -version = "0.3.0-alpha" +version = "0.4.0-alpha" dependencies = [ "bytes", "futures", @@ -3492,7 +3492,7 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.43.2" +version = "0.44.0" dependencies = [ "async-std", "either", @@ -3515,7 +3515,7 @@ dependencies = [ [[package]] name = "libp2p-websocket-websys" -version = "0.3.3" +version = "0.4.0" dependencies = [ "bytes", "futures", @@ -3534,7 +3534,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 52226f6c6e3..7eb1517d24c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,45 +76,44 @@ futures-bounded = { version = "0.2.4" } 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.1", 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.3", path = "core" } -libp2p-dcutr = { version = "0.11.1", path = "protocols/dcutr" } -libp2p-dns = { version = "0.41.1", path = "transports/dns" } -libp2p-floodsub = { version = "0.44.0", path = "protocols/floodsub" } +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" } +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.9" } libp2p-kad = { version = "0.46.0", path = "protocols/kad" } -libp2p-mdns = { version = "0.45.2", 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.2", 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.4.0", path = "protocols/perf" } -libp2p-ping = { version = "0.44.2", 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.10.3", path = "transports/quic" } -libp2p-relay = { version = "0.17.3", path = "protocols/relay" } -libp2p-rendezvous = { version = "0.14.1", path = "protocols/rendezvous" } -libp2p-request-response = { version = "0.26.4", path = "protocols/request-response" } +libp2p-quic = { version = "0.11.0", path = "transports/quic" } +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-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.1", 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.1", path = "misc/webrtc-utils" } -libp2p-webrtc-websys = { version = "0.3.0-alpha", path = "transports/webrtc-websys" } -libp2p-websocket = { version = "0.43.2", path = "transports/websocket" } -libp2p-websocket-websys = { version = "0.3.3", 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.2", path = "muxers/yamux" } multiaddr = "0.18.1" multihash = "0.19.1" diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index b7f3c3c4640..5ed4b4e181d 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,3 +1,14 @@ +## 0.42.0 + +- Update `Transport::dial` function signature with a `DialOpts` param and remove `Transport::dial_as_listener`: + - `DialOpts` struct contains `PortUse` and `Endpoint`, + - `PortUse` allows controling port allocation of new connections (defaults to `PortUse::Reuse`) - + - Add `port_use` field to `ConnectedPoint` + - Set `endpoint` field in `DialOpts` to `Endpoint::Listener` to dial as a listener +- Remove `Transport::address_translation` and relocate functionality to `libp2p_swarm` + +See [PR 4568]. + ## 0.41.3 - Use `web-time` instead of `instant`. See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). diff --git a/core/Cargo.toml b/core/Cargo.toml index 76021c15186..3f9bd540f23 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.3" +version = "0.42.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/core/src/connection.rs b/core/src/connection.rs index ff613c5cce0..bb6639842c9 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)] @@ -80,6 +83,13 @@ 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. + /// + /// 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. Listener { @@ -133,6 +143,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 3f79b2b37a9..2593174290c 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}, @@ -172,48 +173,23 @@ where } } - fn dial(&mut self, addr: Multiaddr) -> Result> { - use TransportError::*; - match self { - Either::Left(a) => match a.dial(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(addr) { - Ok(connec) => Ok(EitherFuture::Second(connec)), - Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)), - Err(Other(err)) => Err(Other(Either::Right(err))), - }, - } - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, - ) -> Result> - where - Self: Sized, - { + opts: DialOpts, + ) -> Result> { use TransportError::*; match self { - Either::Left(a) => match a.dial_as_listener(addr) { + 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_as_listener(addr) { + 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))), }, } } - - 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 3ba153e1927..a42f56773df 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; @@ -62,7 +60,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 2246f3db747..28ce2dbf650 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; @@ -58,6 +58,30 @@ pub use self::upgrade::Upgrade; static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1); +/// 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 an existing port. + /// + /// If there is no listener present that can be used to dial, a new port is allocated. + #[default] + Reuse, +} + +/// Options to customize the behaviour during dialing. +#[derive(Debug, Copy, Clone)] +pub struct DialOpts { + /// 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 role: Endpoint, + /// The port use policy for a new connection. + pub port_use: PortUse, +} + /// A transport provides connection-oriented communication between two peers /// through ordered streams of data (i.e. connections). /// @@ -129,16 +153,10 @@ 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>; - - /// 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( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result>; /// Poll for [`TransportEvent`]s. @@ -157,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 6e0c7e32067..e85703f77fb 100644 --- a/core/src/transport/and_then.rs +++ b/core/src/transport/and_then.rs @@ -19,8 +19,8 @@ // DEALINGS IN THE SOFTWARE. use crate::{ - connection::{ConnectedPoint, Endpoint}, - transport::{ListenerId, Transport, TransportError, TransportEvent}, + connection::ConnectedPoint, + transport::{DialOpts, ListenerId, Transport, TransportError, TransportEvent}, }; use either::Either; use futures::prelude::*; @@ -68,32 +68,14 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let dialed_fut = self - .transport - .dial(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::Dialer, - }, - )), - _marker: PhantomPinned, - }; - Ok(future) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { let dialed_fut = self .transport - .dial_as_listener(addr.clone()) + .dial(addr.clone(), opts) .map_err(|err| err.map(Either::Left))?; let future = AndThenFuture { inner: Either::Left(Box::pin(dialed_fut)), @@ -101,7 +83,8 @@ where self.fun.clone(), ConnectedPoint::Dialer { address: addr, - role_override: Endpoint::Listener, + role_override: opts.role, + port_use: opts.port_use, }, )), _marker: PhantomPinned, @@ -109,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 1cede676c8e..596ab262221 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,9 +58,11 @@ trait Abstract { addr: Multiaddr, ) -> Result<(), TransportError>; fn remove_listener(&mut self, id: ListenerId) -> bool; - fn dial(&mut self, addr: Multiaddr) -> Result, TransportError>; - fn dial_as_listener(&mut self, addr: Multiaddr) -> Result, TransportError>; - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option; + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result, TransportError>; fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -86,24 +88,17 @@ where Transport::remove_listener(self, id) } - fn dial(&mut self, addr: Multiaddr) -> Result, TransportError> { - let fut = Transport::dial(self, addr) - .map(|r| r.map_err(box_err)) - .map_err(|e| e.map(box_err))?; - Ok(Box::pin(fut) as Dial<_>) - } - - fn dial_as_listener(&mut self, addr: Multiaddr) -> Result, TransportError> { - let fut = Transport::dial_as_listener(self, addr) + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result, TransportError> { + 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<_>) } - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - Transport::address_translation(self, server, observed) - } - fn poll( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -143,19 +138,12 @@ impl Transport for Boxed { self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - self.inner.dial(addr) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { - self.inner.dial_as_listener(addr) - } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.address_translation(server, observed) + self.inner.dial(addr, opts) } fn poll( diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index aa3acfc3231..4339f6bba71 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 multiaddr::Multiaddr; @@ -92,19 +92,23 @@ where self.0.remove_listener(id) || self.1.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { + fn dial( + &mut self, + addr: Multiaddr, + opts: DialOpts, + ) -> Result> { tracing::trace!( address=%addr, - "Attempting to dial address using {}", + "Attempting to dial using {}", std::any::type_name::() ); - let addr = match self.0.dial(addr) { + let addr = match self.0.dial(addr, opts) { Ok(connec) => return Ok(EitherFuture::First(connec)), Err(TransportError::MultiaddrNotSupported(addr)) => { tracing::debug!( address=%addr, - "Failed to dial address using {}", - std::any::type_name::() + "Failed to dial using {}", + std::any::type_name::(), ); addr } @@ -115,16 +119,16 @@ where tracing::trace!( address=%addr, - "Attempting to dial address using {}", + "Attempting to dial {}", std::any::type_name::() ); - let addr = match self.1.dial(addr) { + let addr = match self.1.dial(addr, opts) { Ok(connec) => return Ok(EitherFuture::Second(connec)), Err(TransportError::MultiaddrNotSupported(addr)) => { tracing::debug!( address=%addr, - "Failed to dial address using {}", - std::any::type_name::() + "Failed to dial using {}", + std::any::type_name::(), ); addr } @@ -136,37 +140,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) - } 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 0aab94b0396..72558d34a79 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,21 +71,14 @@ impl Transport for DummyTransport { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { - Err(TransportError::MultiaddrNotSupported(addr)) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + _opts: DialOpts, ) -> Result> { 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 0671b0e9984..83774f37004 100644 --- a/core/src/transport/global_only.rs +++ b/core/src/transport/global_only.rs @@ -20,7 +20,7 @@ use crate::{ multiaddr::{Multiaddr, Protocol}, - transport::{ListenerId, TransportError, TransportEvent}, + transport::{DialOpts, ListenerId, TransportError, TransportEvent}, }; use std::{ pin::Pin, @@ -287,47 +287,25 @@ impl crate::Transport for Transport { self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - match addr.iter().next() { - Some(Protocol::Ip4(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) - } - Some(Protocol::Ip6(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) - } - _ => { - tracing::debug!(address=%addr, "Not dialing unsupported Multiaddress"); - Err(TransportError::MultiaddrNotSupported(addr)) - } - } - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { match addr.iter().next() { Some(Protocol::Ip4(a)) => { if !ipv4_global::is_global(a) { - tracing::debug!(ip=?a, "Not dialing non global IP address"); + tracing::debug!(ip=%a, "Not dialing non global IP address"); return Err(TransportError::MultiaddrNotSupported(addr)); } - self.inner.dial_as_listener(addr) + self.inner.dial(addr, opts) } Some(Protocol::Ip6(a)) => { if !ipv6_global::is_global(a) { - tracing::debug!(ip=?a, "Not dialing non global IP address"); + tracing::debug!(ip=%a, "Not dialing non global IP address"); return Err(TransportError::MultiaddrNotSupported(addr)); } - self.inner.dial_as_listener(addr) + self.inner.dial(addr, opts) } _ => { tracing::debug!(address=%addr, "Not dialing unsupported Multiaddress"); @@ -336,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 553f3e6338d..9aab84ba8b1 100644 --- a/core/src/transport/map.rs +++ b/core/src/transport/map.rs @@ -18,8 +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, Endpoint}, + connection::ConnectedPoint, transport::{Transport, TransportError, TransportEvent}, }; use futures::prelude::*; @@ -73,26 +74,16 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let future = self.transport.dial(addr.clone())?; - let p = ConnectedPoint::Dialer { - address: addr, - role_override: Endpoint::Dialer, - }; - Ok(MapFuture { - inner: future, - args: Some((self.fun.clone(), p)), - }) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { - let future = self.transport.dial_as_listener(addr.clone())?; + let future = self.transport.dial(addr.clone(), opts)?; let p = ConnectedPoint::Dialer { address: addr, - role_override: Endpoint::Listener, + role_override: opts.role, + port_use: opts.port_use, }; Ok(MapFuture { inner: future, @@ -100,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 56e1ebf2929..5d44af9af2e 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,23 +65,13 @@ where self.transport.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let map = self.map.clone(); - match self.transport.dial(addr) { - Ok(future) => Ok(MapErrDial { - inner: future, - map: Some(map), - }), - Err(err) => Err(err.map(map)), - } - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { let map = self.map.clone(); - match self.transport.dial_as_listener(addr) { + match self.transport.dial(addr, opts) { Ok(future) => Ok(MapErrDial { inner: future, map: Some(map), @@ -90,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 6e4b9322ee5..85680265e8b 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, future::Ready, prelude::*, task::Context, task::Poll}; use multiaddr::{Multiaddr, Protocol}; @@ -208,7 +208,11 @@ 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 @@ -222,17 +226,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 - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -405,6 +398,8 @@ impl Drop for Chan { #[cfg(test)] mod tests { + use crate::{transport::PortUse, Endpoint}; + use super::*; #[test] @@ -489,7 +484,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 { + role: Endpoint::Dialer, + port_use: PortUse::New + } + ) .is_err()); transport .listen_on( @@ -498,7 +499,13 @@ mod tests { ) .unwrap(); assert!(transport - .dial("/memory/810172461024613".parse().unwrap()) + .dial( + "/memory/810172461024613".parse().unwrap(), + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New + } + ) .is_ok()); } @@ -565,7 +572,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 { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); socket.write_all(&msg).await.unwrap(); }; @@ -601,7 +618,13 @@ mod tests { let dialer = async move { MemoryTransport::default() - .dial(listener_addr_cloned) + .dial( + listener_addr_cloned, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap() .await .unwrap(); @@ -652,7 +675,13 @@ mod tests { let dialer = async move { let chan = MemoryTransport::default() - .dial(listener_addr_cloned) + .dial( + listener_addr_cloned, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) .unwrap() .await .unwrap(); diff --git a/core/src/transport/optional.rs b/core/src/transport/optional.rs index 839f55a4000..f18bfa441b0 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,33 +80,18 @@ where } } - fn dial(&mut self, addr: Multiaddr) -> Result> { - if let Some(inner) = self.0.as_mut() { - inner.dial(addr) - } else { - Err(TransportError::MultiaddrNotSupported(addr)) - } - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { if let Some(inner) = self.0.as_mut() { - inner.dial_as_listener(addr) + inner.dial(addr, opts) } 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) - } 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 0e8ab3f5201..830ed099629 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, @@ -99,24 +100,14 @@ where self.inner.remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let dial = self - .inner - .dial(addr) - .map_err(|err| err.map(TransportTimeoutError::Other))?; - Ok(Timeout { - inner: dial, - timer: Delay::new(self.outgoing_timeout), - }) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + opts: DialOpts, ) -> Result> { let dial = self .inner - .dial_as_listener(addr) + .dial(addr, opts) .map_err(|err| err.map(TransportTimeoutError::Other))?; Ok(Timeout { inner: dial, @@ -124,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 9626312b5fb..66b9e7509af 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}, @@ -335,21 +336,18 @@ 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 { 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, @@ -358,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<'_>, @@ -404,10 +398,14 @@ 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), @@ -419,20 +417,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, @@ -443,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/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/core/tests/transport_upgrade.rs b/core/tests/transport_upgrade.rs index a8872051618..d8bec6f2b59 100644 --- a/core/tests/transport_upgrade.rs +++ b/core/tests/transport_upgrade.rs @@ -19,10 +19,11 @@ // 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, InboundConnectionUpgrade, OutboundConnectionUpgrade, UpgradeInfo, }; +use libp2p_core::Endpoint; use libp2p_identity as identity; use libp2p_mplex::MplexConfig; use libp2p_noise as noise; @@ -121,7 +122,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 { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); assert_eq!(peer, listener_id); }; diff --git a/examples/autonat/Cargo.toml b/examples/autonat/Cargo.toml index 9bdf0be393d..010b76623e0 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/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/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 } diff --git a/hole-punching-tests/src/main.rs b/hole-punching-tests/src/main.rs index bbecc948e08..02229e16262 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/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md index 977d9f91924..7e1b95c6e75 100644 --- a/libp2p/CHANGELOG.md +++ b/libp2p/CHANGELOG.md @@ -7,12 +7,23 @@ - Raise MSRV to 1.73. See [PR 5266](https://github.com/libp2p/rust-libp2p/pull/5266). +- 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]. + - Use `web-time` instead of `instant`. See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). - Remove redundant async signature from builder methods. See [PR 5468](https://github.com/libp2p/rust-libp2p/pull/5468). +[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/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..b02e52f25a1 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::{transport::PortUse, ConnectedPoint, Endpoint, Multiaddr}; 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); @@ -569,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/src/lib.rs b/misc/memory-connection-limits/src/lib.rs index 08c45154221..7b5803a61aa 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, @@ -177,6 +177,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/misc/memory-connection-limits/tests/util/mod.rs b/misc/memory-connection-limits/tests/util/mod.rs index f40ce319929..d18aa78fd22 100644 --- a/misc/memory-connection-limits/tests/util/mod.rs +++ b/misc/memory-connection-limits/tests/util/mod.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/misc/metrics/src/bandwidth.rs b/misc/metrics/src/bandwidth.rs index ac6a4178ce9..8a0f54e5b65 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,33 +84,20 @@ 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)) }))) } - 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/misc/server/src/main.rs b/misc/server/src/main.rs index eb28b9ad5c2..820921beaed 100644 --- a/misc/server/src/main.rs +++ b/misc/server/src/main.rs @@ -71,7 +71,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/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/CHANGELOG.md b/protocols/autonat/CHANGELOG.md index 6a25dc173dd..2a799221f7c 100644 --- a/protocols/autonat/CHANGELOG.md +++ b/protocols/autonat/CHANGELOG.md @@ -1,3 +1,11 @@ +## 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). diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml index 970dd27bc53..f047cb7d5ba 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.1" +version = "0.13.0" license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["peer-to-peer", "libp2p", "networking"] diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index a47852e5206..64bebfb6233 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -28,6 +28,7 @@ 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::{ @@ -338,6 +339,7 @@ impl Behaviour { ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + .. } => { if let Some(event) = self.as_server().on_outbound_connection(&peer, address) { self.pending_actions @@ -347,6 +349,7 @@ impl Behaviour { 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 @@ -512,9 +515,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_server.rs b/protocols/autonat/src/behaviour/as_server.rs index af6be799e21..8f1d6642de5 100644 --- a/protocols/autonat/src/behaviour/as_server.rs +++ b/protocols/autonat/src/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/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index b0610ef59a4..fd97b1a9132 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -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, .. diff --git a/protocols/dcutr/CHANGELOG.md b/protocols/dcutr/CHANGELOG.md index 4865a0540bb..0ddc4aa1148 100644 --- a/protocols/dcutr/CHANGELOG.md +++ b/protocols/dcutr/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.0 + + + ## 0.11.1 - Use `web-time` instead of `instant`. See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). diff --git a/protocols/dcutr/Cargo.toml b/protocols/dcutr/Cargo.toml index 24a5560f5b5..cd46840caf3 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.1" +version = "0.12.0" authors = ["Max Inden "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/dcutr/src/behaviour.rs b/protocols/dcutr/src/behaviour.rs index 4409b5f6a64..574c96205fa 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/CHANGELOG.md b/protocols/floodsub/CHANGELOG.md index 8e3cb70ddf1..4192e0ea58d 100644 --- a/protocols/floodsub/CHANGELOG.md +++ b/protocols/floodsub/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.45.0 + + + ## 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/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/CHANGELOG.md b/protocols/gossipsub/CHANGELOG.md index 7c43b98f0a7..8e115052d31 100644 --- a/protocols/gossipsub/CHANGELOG.md +++ b/protocols/gossipsub/CHANGELOG.md @@ -1,4 +1,6 @@ ## 0.47.0 + + - Add ConnectionError to FromSwarm::ConnectionClosed. See [PR 5485](https://github.com/libp2p/rust-libp2p/pull/5485). @@ -9,7 +11,7 @@ ## 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/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index 7980f362acf..0d1af1ada0c 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -34,7 +34,9 @@ use futures_ticker::Ticker; use prometheus_client::registry::Registry; use rand::{seq::SliceRandom, thread_rng}; -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::{ @@ -3012,6 +3014,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new(self.config.protocol_config())) } diff --git a/protocols/gossipsub/src/behaviour/tests.rs b/protocols/gossipsub/src/behaviour/tests.rs index fe861a674dd..c44152ed2f9 100644 --- a/protocols/gossipsub/src/behaviour/tests.rs +++ b/protocols/gossipsub/src/behaviour/tests.rs @@ -206,6 +206,7 @@ where ConnectedPoint::Dialer { address, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, } } else { ConnectedPoint::Listener { @@ -256,6 +257,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. @@ -562,6 +564,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, @@ -4052,6 +4055,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 0, @@ -4073,6 +4077,7 @@ fn test_scoring_p6() { endpoint: &ConnectedPoint::Dialer { address: addr2.clone(), role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, }, failed_addresses: &[], other_established: 1, @@ -4103,6 +4108,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/identify/CHANGELOG.md b/protocols/identify/CHANGELOG.md index b8ab3f8df50..275f7114b28 100644 --- a/protocols/identify/CHANGELOG.md +++ b/protocols/identify/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.45.0 +- Address translation is moved here from `libp2p-core`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) + + + - Add `ConnectionId` in `Event`. See [PR 4981](https://github.com/libp2p/rust-libp2p/pull/4981). diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 92a0dc46103..6590ccd6588 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -20,6 +20,8 @@ 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_identity::PeerId; use libp2p_identity::PublicKey; @@ -27,6 +29,7 @@ use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailu use libp2p_swarm::{ ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, NotifyHandler, PeerAddresses, StreamUpgradeError, THandlerInEvent, ToSwarm, + _address_translation, }; use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent}; @@ -39,6 +42,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. /// @@ -52,6 +99,9 @@ pub struct Behaviour { /// The address a remote observed for us. our_observed_addresses: HashMap, + /// The outbound connections established without port reuse (require translation) + outbound_connections_with_ephemeral_port: HashSet, + /// Pending events to be emitted when polled. events: VecDeque>, /// The addresses of all peers that we have discovered. @@ -153,6 +203,7 @@ impl Behaviour { config, connected: HashMap::new(), our_observed_addresses: Default::default(), + outbound_connections_with_ephemeral_port: Default::default(), events: VecDeque::new(), discovered_peers, listen_addresses: Default::default(), @@ -213,6 +264,58 @@ impl Behaviour { .cloned() .collect() } + + fn emit_new_external_addr_candidate_event( + &mut self, + connection_id: ConnectionId, + observed: &Multiaddr, + ) { + if self + .outbound_connections_with_ephemeral_port + .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)); + } + } + return; + } + + // outgoing connection dialed with port reuse + // incomming connection + self.events + .push_back(ToSwarm::NewExternalAddrCandidate(observed.clone())); + } } impl NetworkBehaviour for Behaviour { @@ -239,11 +342,25 @@ impl NetworkBehaviour for Behaviour { fn handle_established_outbound_connection( &mut self, - _: ConnectionId, + connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, _: 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(); + } + + if port_use == PortUse::New { + self.outbound_connections_with_ephemeral_port + .insert(connection_id); + } + Ok(Handler::new( self.config.interval, peer, @@ -289,8 +406,7 @@ impl NetworkBehaviour for Behaviour { match self.our_observed_addresses.entry(connection_id) { Entry::Vacant(not_yet_observed) => { not_yet_observed.insert(observed.clone()); - self.events - .push_back(ToSwarm::NewExternalAddrCandidate(observed)); + self.emit_new_external_addr_candidate_event(connection_id, &observed); } Entry::Occupied(already_observed) if already_observed.get() == &observed => { // No-op, we already observed this address. @@ -303,8 +419,7 @@ impl NetworkBehaviour for Behaviour { ); *already_observed.get_mut() = observed.clone(); - self.events - .push_back(ToSwarm::NewExternalAddrCandidate(observed)); + self.emit_new_external_addr_candidate_event(connection_id, &observed); } } } @@ -403,6 +518,8 @@ impl NetworkBehaviour for Behaviour { } self.our_observed_addresses.remove(&connection_id); + self.outbound_connections_with_ephemeral_port + .remove(&connection_id); } FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => { if let (Some(peer_id), Some(cache), DialError::Transport(errors)) = diff --git a/protocols/identify/tests/smoke.rs b/protocols/identify/tests/smoke.rs index dd92d10979a..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; @@ -59,12 +58,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)); diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index f88484bafa1..a5b404ae6bd 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -21,6 +21,7 @@ See [PR 5317](https://github.com/libp2p/rust-libp2p/pull/5317). - Use `web-time` instead of `instant`. See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). + - Correctly handle the `NoKnownPeers` error on automatic bootstrap. See [PR 5349](https://github.com/libp2p/rust-libp2p/pull/5349). - Improve automatic bootstrap triggering conditions: diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 069eec1a5b4..8d6f86d7e0f 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -35,7 +35,7 @@ use crate::record::{ use crate::{bootstrap, K_VALUE}; use crate::{jobs::*, protocol}; use fnv::FnvHashSet; -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, @@ -2168,10 +2168,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, }; let mut handler = Handler::new( diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index b82ec966f89..c4859f2f138 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -1310,6 +1310,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. @@ -1360,10 +1361,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/mdns/CHANGELOG.md b/protocols/mdns/CHANGELOG.md index 18fe0e76e6f..67b1d669f60 100644 --- a/protocols/mdns/CHANGELOG.md +++ b/protocols/mdns/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.46.0 + + ## 0.45.2 - Add `#[track_caller]` on all `spawn` wrappers. diff --git a/protocols/mdns/Cargo.toml b/protocols/mdns/Cargo.toml index 724c8818621..0c8229465d6 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.2" +version = "0.46.0" description = "Implementation of the libp2p mDNS discovery method" authors = ["Parity Technologies "] license = "MIT" diff --git a/protocols/mdns/src/behaviour.rs b/protocols/mdns/src/behaviour.rs index d4a0a707a01..6355fbf4943 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; @@ -263,6 +264,7 @@ where _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/protocols/mdns/src/behaviour/iface/query.rs b/protocols/mdns/src/behaviour/iface/query.rs index eeb699fca6b..70b84816d0f 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}; @@ -179,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 b347f21e9e0..abeca9fad25 100644 --- a/protocols/perf/CHANGELOG.md +++ b/protocols/perf/CHANGELOG.md @@ -1,4 +1,6 @@ ## 0.4.0 + + - Add ConnectionError to FromSwarm::ConnectionClosed. See [PR 5485](https://github.com/libp2p/rust-libp2p/pull/5485). diff --git a/protocols/perf/src/client/behaviour.rs b/protocols/perf/src/client/behaviour.rs index 5e430f8f0c1..1b181557acc 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/ping/CHANGELOG.md b/protocols/ping/CHANGELOG.md index 7c7d7f3a54f..c0a124333e9 100644 --- a/protocols/ping/CHANGELOG.md +++ b/protocols/ping/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.45.0 + + + ## 0.44.2 - Use `web-time` instead of `instant`. @@ -12,7 +16,6 @@ `ping::Event` can now be shared between threads. See [PR 5250] - [PR 5250]: https://github.com/libp2p/rust-libp2p/pull/5250 ## 0.44.0 diff --git a/protocols/ping/Cargo.toml b/protocols/ping/Cargo.toml index 79c0a43aa69..2c347adb2c4 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.2" +version = "0.45.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/ping/src/lib.rs b/protocols/ping/src/lib.rs index 5eaa6d4952a..82f240cab6b 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 { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(Handler::new(self.config.clone())) } diff --git a/protocols/ping/src/protocol.rs b/protocols/ping/src/protocol.rs index 8fc18505d35..566e5e258e2 100644 --- a/protocols/ping/src/protocol.rs +++ b/protocols/ping/src/protocol.rs @@ -86,7 +86,8 @@ mod tests { use futures::StreamExt; use libp2p_core::{ multiaddr::multiaddr, - transport::{memory::MemoryTransport, ListenerId, Transport}, + transport::{memory::MemoryTransport, DialOpts, ListenerId, PortUse, Transport}, + Endpoint, }; #[test] @@ -110,7 +111,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/protocols/relay/CHANGELOG.md b/protocols/relay/CHANGELOG.md index 97638d1ae6a..fc71ccedad5 100644 --- a/protocols/relay/CHANGELOG.md +++ b/protocols/relay/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.18.0 + + + ## 0.17.3 - Use `web-time` instead of `instant`. See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). diff --git a/protocols/relay/Cargo.toml b/protocols/relay/Cargo.toml index 4981c94d9c8..1a0e7836158 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.3" +version = "0.18.0" authors = ["Parity Technologies ", "Max Inden "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index cf0e76e3662..463febf9f2f 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -28,6 +28,7 @@ use crate::proto; use crate::protocol::{inbound_hop, outbound_stop}; use either::Either; 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 b4374aa4672..ec1e8ca5fb8 100644 --- a/protocols/relay/src/priv_client/transport.rs +++ b/protocols/relay/src/priv_client/transport.rs @@ -31,7 +31,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; @@ -49,7 +49,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; @@ -66,7 +66,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. @@ -165,7 +168,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,24 +213,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 - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/protocols/rendezvous/CHANGELOG.md b/protocols/rendezvous/CHANGELOG.md index 4aa18985f1e..1ed9e5bc3b0 100644 --- a/protocols/rendezvous/CHANGELOG.md +++ b/protocols/rendezvous/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.15.0 + + + ## 0.14.1 - Use `web-time` instead of `instant`. See [PR 5347](https://github.com/libp2p/rust-libp2p/pull/5347). diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 6879a4093ce..32b233813e3 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.1" +version = "0.15.0" authors = ["The COMIT guys "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" 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 bee91f28e88..45a525d9573 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; @@ -139,9 +140,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/request-response/CHANGELOG.md b/protocols/request-response/CHANGELOG.md index beee817f024..db0d9126516 100644 --- a/protocols/request-response/CHANGELOG.md +++ b/protocols/request-response/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.27.0 + + + ## 0.26.4 - Use `web-time` instead of `instant`. diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 4bc2378ae54..af70fd9a83e 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.4" +version = "0.27.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 4362b3255ad..e627f5668ff 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}, @@ -772,6 +772,7 @@ where peer: PeerId, remote_address: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { let mut handler = Handler::new( self.inbound_protocols.clone(), diff --git a/protocols/stream/CHANGELOG.md b/protocols/stream/CHANGELOG.md index 1e3b85da0b9..2532970d3c6 100644 --- a/protocols/stream/CHANGELOG.md +++ b/protocols/stream/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0-alpha + + + ## 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/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/CHANGELOG.md b/protocols/upnp/CHANGELOG.md index 9472c7153c6..21e90f9534b 100644 --- a/protocols/upnp/CHANGELOG.md +++ b/protocols/upnp/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0 + + + ## 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/protocols/upnp/src/behaviour.rs b/protocols/upnp/src/behaviour.rs index a94ef9526dd..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, 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 +252,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/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index 2e7daf7acc4..258c0b976c8 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -76,6 +76,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/CHANGELOG.md b/swarm/CHANGELOG.md index f4901947b8b..9aeaa5a1ccc 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -1,6 +1,14 @@ ## 0.45.0 +- Implement refactored `Transport`. + See [PR 4568] +- Move `address_translation` into swarm and into `libp2p-identify`. + See [PR 4568] + +[PR 4568]: https://github.com/libp2p/rust-libp2p/pull/4568 + +## 0.44.3 - Optimize internal connection `fn poll`. New implementation now scales much better with number of listen protocols active. No changes to public API introduced. See [PR 5026](https://github.com/libp2p/rust-libp2p/pull/5026) diff --git a/swarm/benches/connection_handler.rs b/swarm/benches/connection_handler.rs index b9986d9649f..09340421f83 100644 --- a/swarm/benches/connection_handler.rs +++ b/swarm/benches/connection_handler.rs @@ -223,6 +223,7 @@ impl NetworkBehaviour for SpinningBehaviour { _peer: libp2p_identity::PeerId, _addr: &libp2p_core::Multiaddr, _role_override: libp2p_core::Endpoint, + _port_use: libp2p_core::transport::PortUse, ) -> Result, libp2p_swarm::ConnectionDenied> { Ok(SpinningHandler { iter_count: self.iter_count, diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 8a8418739c8..35aed12fba5 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -35,7 +35,10 @@ use crate::{ ConnectionDenied, ConnectionError, 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}; @@ -196,6 +199,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 25da83fa11f..7a51303e74d 100644 --- a/swarm/src/behaviour/either.rs +++ b/swarm/src/behaviour/either.rs @@ -22,6 +22,7 @@ use crate::behaviour::{self, NetworkBehaviour, 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 e81c5343701..398c919ae86 100644 --- a/swarm/src/behaviour/toggle.rs +++ b/swarm/src/behaviour/toggle.rs @@ -30,6 +30,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}; @@ -139,6 +140,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 }), @@ -150,6 +152,7 @@ where peer, addr, role_override, + port_use, )?; Ok(ToggleConnectionHandler { diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 9c5e39830ed..603a5b0d7c4 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::{ @@ -1352,6 +1353,7 @@ enum PendingPoint { Dialer { /// Same as [`ConnectedPoint::Dialer`] `role_override`. role_override: Endpoint, + port_use: PortUse, }, /// The socket comes from a listener. Listener { @@ -1365,7 +1367,14 @@ 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 48ba92f1020..07f6968dec9 100644 --- a/swarm/src/connection/pool.rs +++ b/swarm/src/connection/pool.rs @@ -39,6 +39,7 @@ use futures::{ }; use libp2p_core::connection::Endpoint; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt}; +use libp2p_core::transport::PortUse; use std::task::Waker; use std::{ collections::HashMap, @@ -424,6 +425,7 @@ where >, peer: Option, role_override: Endpoint, + port_use: PortUse, dial_concurrency_factor_override: Option, connection_id: ConnectionId, ) { @@ -444,7 +446,10 @@ where .instrument(span), ); - let endpoint = PendingPoint::Dialer { role_override }; + let endpoint = PendingPoint::Dialer { + role_override, + port_use, + }; self.counters.inc_pending(&endpoint); self.pending.insert( @@ -650,10 +655,17 @@ 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, }, Some(errors), ), diff --git a/swarm/src/dial_opts.rs b/swarm/src/dial_opts.rs index 4442d913847..4f5b621327c 100644 --- a/swarm/src/dial_opts.rs +++ b/swarm/src/dial_opts.rs @@ -22,10 +22,38 @@ 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! fn_override_role { + () => { + /// 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 + } + }; +} + +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. + pub fn allocate_new_port(mut self) -> Self { + self.port_use = PortUse::New; + self + } + }; +} + /// Options to configure a dial to a known or unknown peer. /// /// Used in [`Swarm::dial`](crate::Swarm::dial) and @@ -45,6 +73,7 @@ pub struct DialOpts { role_override: Endpoint, dial_concurrency_factor_override: Option, connection_id: ConnectionId, + port_use: PortUse, } impl DialOpts { @@ -65,6 +94,7 @@ impl DialOpts { condition: Default::default(), role_override: Endpoint::Dialer, dial_concurrency_factor_override: Default::default(), + port_use: PortUse::Reuse, } } @@ -124,6 +154,10 @@ impl DialOpts { pub(crate) fn role_override(&self) -> Endpoint { self.role_override } + + pub(crate) fn port_use(&self) -> PortUse { + self.port_use + } } impl From for DialOpts { @@ -144,6 +178,7 @@ pub struct WithPeerId { condition: PeerCondition, role_override: Endpoint, dial_concurrency_factor_override: Option, + port_use: PortUse, } impl WithPeerId { @@ -169,19 +204,12 @@ impl WithPeerId { extend_addresses_through_behaviour: false, role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, + 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 - } + fn_override_role!(); + fn_allocate_new_port!(); /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { @@ -193,6 +221,7 @@ impl WithPeerId { role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, connection_id: ConnectionId::next(), + port_use: self.port_use, } } } @@ -205,6 +234,7 @@ pub struct WithPeerIdWithAddresses { extend_addresses_through_behaviour: bool, role_override: Endpoint, dial_concurrency_factor_override: Option, + port_use: PortUse, } impl WithPeerIdWithAddresses { @@ -221,16 +251,8 @@ 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 - } + fn_override_role!(); + fn_allocate_new_port!(); /// Override /// Number of addresses concurrently dialed for a single outbound connection attempt. @@ -249,6 +271,7 @@ impl WithPeerIdWithAddresses { role_override: self.role_override, dial_concurrency_factor_override: self.dial_concurrency_factor_override, connection_id: ConnectionId::next(), + port_use: self.port_use, } } } @@ -262,6 +285,7 @@ impl WithoutPeerId { WithoutPeerIdWithAddress { address, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, } } } @@ -270,19 +294,13 @@ impl WithoutPeerId { pub struct WithoutPeerIdWithAddress { address: Multiaddr, role_override: Endpoint, + port_use: PortUse, } 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 - } + fn_override_role!(); + fn_allocate_new_port!(); + /// Build the final [`DialOpts`]. pub fn build(self) -> DialOpts { DialOpts { @@ -293,6 +311,7 @@ impl WithoutPeerIdWithAddress { role_override: self.role_override, dial_concurrency_factor_override: None, connection_id: ConnectionId::next(), + port_use: self.port_use, } } } diff --git a/swarm/src/dummy.rs b/swarm/src/dummy.rs index 86df676443b..6e1b4d56eb9 100644 --- a/swarm/src/dummy.rs +++ b/swarm/src/dummy.rs @@ -7,6 +7,7 @@ use crate::{ ConnectionDenied, ConnectionHandlerEvent, 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/lib.rs b/swarm/src/lib.rs index 03e30240771..81b1ca1a68d 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; +mod translation; /// Bundles all symbols required for the [`libp2p_swarm_derive::NetworkBehaviour`] macro. #[doc(hidden)] @@ -99,7 +100,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; @@ -134,13 +135,15 @@ use connection::{ }; use dial_opts::{DialOpts, PeerCondition}; use futures::{prelude::*, stream::FusedStream}; + use libp2p_core::{ connection::ConnectedPoint, muxing::StreamMuxerBox, transport::{self, ListenerId, TransportError, TransportEvent}, - Endpoint, Multiaddr, Transport, + Multiaddr, Transport, }; use libp2p_identity::PeerId; + use smallvec::SmallVec; use std::collections::{HashMap, HashSet, VecDeque}; use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; @@ -151,6 +154,8 @@ use std::{ task::{Context, Poll}, }; use tracing::Instrument; +#[doc(hidden)] +pub use translation::_address_translation; /// Event generated by the [`NetworkBehaviour`] that the swarm will report back. type TBehaviourOutEvent = ::ToSwarm; @@ -528,18 +533,15 @@ where .into_iter() .map(|a| match peer_id.map_or(Ok(a.clone()), |p| a.with_p2p(p)) { Ok(address) => { - let (dial, span) = match dial_opts.role_override() { - Endpoint::Dialer => ( - self.transport.dial(address.clone()), - tracing::debug_span!(parent: tracing::Span::none(), "Transport::dial", %address), - ), - Endpoint::Listener => ( - self.transport.dial_as_listener(address.clone()), - tracing::debug_span!(parent: tracing::Span::none(), "Transport::dial_as_listener", %address), - ), - }; + let dial = self.transport.dial( + address.clone(), + transport::DialOpts { + role: dial_opts.role_override(), + port_use: dial_opts.port_use(), + }, + ); + let span = tracing::debug_span!(parent: tracing::Span::none(), "Transport::dial", %address); span.follows_from(tracing::Span::current()); - match dial { Ok(fut) => fut .map(|r| (address, r.map_err(TransportError::Other))) @@ -560,6 +562,7 @@ where dials, peer_id, dial_opts.role_override(), + dial_opts.port_use(), dial_opts.dial_concurrency_override(), connection_id, ); @@ -706,12 +709,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) => { @@ -1135,40 +1140,12 @@ where self.pending_handler_event = Some((peer_id, handler, event)); } ToSwarm::NewExternalAddrCandidate(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 { - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, - )); - self.pending_swarm_events - .push_back(SwarmEvent::NewExternalAddrCandidate { address: 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()); @@ -1784,7 +1761,9 @@ mod tests { use crate::test::{CallTraceBehaviour, MockBehaviour}; use libp2p_core::multiaddr::multiaddr; use libp2p_core::transport::memory::MemoryTransportError; - use libp2p_core::{multiaddr, upgrade}; + use libp2p_core::transport::{PortUse, TransportEvent}; + use libp2p_core::Endpoint; + use libp2p_core::{multiaddr, transport, upgrade}; use libp2p_identity as identity; use libp2p_plaintext as plaintext; use libp2p_yamux as yamux; @@ -2179,6 +2158,7 @@ mod tests { ConnectedPoint::Dialer { address: other_addr, role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, } ); } diff --git a/swarm/src/test.rs b/swarm/src/test.rs index d49b504392a..a6cb7c4d4eb 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -26,6 +26,7 @@ use crate::{ ConnectionDenied, ConnectionHandler, ConnectionId, NetworkBehaviour, 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()) } @@ -427,6 +429,7 @@ where peer: PeerId, addr: &Multiaddr, role_override: Endpoint, + port_use: PortUse, ) -> Result, ConnectionDenied> { self.handle_established_outbound_connection.push(( peer, @@ -434,8 +437,13 @@ where role_override, connection_id, )); - 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/core/src/translation.rs b/swarm/src/translation.rs similarity index 95% rename from core/src/translation.rs rename to swarm/src/translation.rs index efddae31052..baa80c907b5 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. /// @@ -35,7 +35,8 @@ use multiaddr::{Multiaddr, Protocol}; /// 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/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/listener.rs b/swarm/tests/listener.rs index 8d22acc90e2..160b1f5b064 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, 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 +97,7 @@ impl NetworkBehaviour for Behaviour { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/swarm/tests/swarm_derive.rs b/swarm/tests/swarm_derive.rs index 12f0d0a5ba8..919ed0cab7f 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::{transport::PortUse, Endpoint, Multiaddr}; use libp2p_identify as identify; use libp2p_ping as ping; use libp2p_swarm::{ @@ -404,6 +404,7 @@ fn with_generics_constrained() { _: libp2p_identity::PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } @@ -565,6 +566,7 @@ fn custom_out_event_no_type_parameters() { _: PeerId, _: &Multiaddr, _: Endpoint, + _: PortUse, ) -> Result, ConnectionDenied> { Ok(dummy::ConnectionHandler) } diff --git a/transports/dns/CHANGELOG.md b/transports/dns/CHANGELOG.md index 91cfbc00883..e4f951f157f 100644 --- a/transports/dns/CHANGELOG.md +++ b/transports/dns/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.42.0 + +- 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 1e50ad444b8..b728509364a 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/dns/src/lib.rs b/transports/dns/src/lib.rs index 63bd9bbad26..7d92cc8ecfc 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -149,9 +149,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,19 +230,12 @@ where self.inner.lock().remove_listener(id) } - fn dial(&mut self, addr: Multiaddr) -> Result> { - Ok(self.do_dial(addr, Endpoint::Dialer)) - } - - fn dial_as_listener( + fn dial( &mut self, addr: Multiaddr, + dial_opts: DialOpts, ) -> Result> { - Ok(self.do_dial(addr, Endpoint::Listener)) - } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.inner.lock().address_translation(server, observed) + Ok(self.do_dial(addr, dial_opts)) } fn poll( @@ -269,7 +261,7 @@ where fn do_dial( &mut self, addr: Multiaddr, - role_override: Endpoint, + dial_opts: DialOpts, ) -> ::Dial { let resolver = self.resolver.clone(); let inner = self.inner.clone(); @@ -355,10 +347,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 @@ -625,7 +614,12 @@ where #[cfg(all(test, any(feature = "tokio", feature = "async-std")))] mod tests { use super::*; - use libp2p_core::Transport; + use futures::future::BoxFuture; + use libp2p_core::{ + multiaddr::{Multiaddr, Protocol}, + transport::{PortUse, TransportError, TransportEvent}, + Endpoint, Transport, + }; use libp2p_identity::PeerId; #[test] @@ -655,7 +649,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, @@ -664,17 +662,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 - } - fn poll( self: Pin<&mut Self>, _: &mut Context<'_>, @@ -690,30 +677,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(); @@ -722,7 +713,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(); @@ -734,6 +725,7 @@ mod tests { format!("/dnsaddr/bootstrap.libp2p.io/p2p/{}", PeerId::random()) .parse() .unwrap(), + dial_opts, ) .unwrap() .await @@ -745,7 +737,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/CHANGELOG.md b/transports/quic/CHANGELOG.md index 3ec52ec0ddb..2593af605df 100644 --- a/transports/quic/CHANGELOG.md +++ b/transports/quic/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.11.0 + +- 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 c7e031b1cfd..d2d8c6fc2b0 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/quic/src/transport.rs b/transports/quic/src/transport.rs index 9bd4c035cec..057d0f978d7 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}, @@ -195,6 +197,21 @@ impl GenTransport

{ Ok(socket.into()) } + + fn bound_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)?; + let endpoint_config = self.quinn_config.endpoint_config.clone(); + let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; + Ok(endpoint) + } } impl Transport for GenTransport

{ @@ -247,119 +264,110 @@ 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) -> 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::Reuse) => { + 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 { + if let Some(occupied) = self.dialer.get(&socket_family) { + occupied.clone() + } else { + let endpoint = self.bound_socket(socket_addr)?; + self.dialer.insert(socket_family, endpoint.clone()); + endpoint + } + } else { + self.bound_socket(socket_addr)? + }; + dialer + }; + 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); - } - Entry::Vacant(entry) => { - 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 + })) } - }; + (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( @@ -722,33 +730,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 { @@ -921,7 +902,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::Reuse, + }, + ) .unwrap(); assert!(transport.dialer.contains_key(&SocketFamily::Ipv4)); diff --git a/transports/quic/tests/smoke.rs b/transports/quic/tests/smoke.rs index 74423076780..6a760f9997c 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(); @@ -114,18 +117,14 @@ 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`]. - 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 +132,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 +179,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 +319,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 +341,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); @@ -408,7 +426,7 @@ async fn write_after_peer_dropped_stream() { .try_init(); let (stream_a, mut stream_b) = build_streams::().await; drop(stream_a); - futures_timer::Delay::new(Duration::from_millis(10)).await; + futures_timer::Delay::new(Duration::from_millis(100)).await; let data = vec![0; 10]; stream_b.write_all(&data).await.expect("Write failed."); @@ -765,7 +783,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/CHANGELOG.md b/transports/tcp/CHANGELOG.md index f0204f1ba85..107d0d13ece 100644 --- a/transports/tcp/CHANGELOG.md +++ b/transports/tcp/CHANGELOG.md @@ -1,5 +1,14 @@ ## 0.42.0 +- Implement refactored `Transport`. + 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 + - Disable Nagle's algorithm (i.e. `TCP_NODELAY`) by default. See [PR 4916](https://github.com/libp2p/rust-libp2p/pull/4916) diff --git a/transports/tcp/src/lib.rs b/transports/tcp/src/lib.rs index 3d881a6421c..386caa78b0f 100644 --- a/transports/tcp/src/lib.rs +++ b/transports/tcp/src/lib.rs @@ -40,9 +40,8 @@ use futures::{future::Ready, prelude::*, stream::SelectAll}; use futures_timer::Delay; use if_watch::IfEvent; 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}; @@ -65,27 +64,16 @@ 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>>, } impl PortReuse { @@ -93,26 +81,22 @@ 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 { - tracing::trace!(%ip, %port, "Registering for port reuse"); - listen_addrs - .write() - .expect("`register()` and `unregister()` never panic while holding the lock") - .insert((ip, port)); - } + tracing::trace!(%ip, %port, "Registering for port reuse"); + 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 { - tracing::trace!(%ip, %port, "Unregistering for port reuse"); - listen_addrs - .write() - .expect("`register()` and `unregister()` never panic while holding the lock") - .remove(&(ip, port)); - } + tracing::trace!(%ip, %port, "Unregistering for port reuse"); + 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 @@ -125,20 +109,17 @@ 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)); } } } @@ -163,7 +144,6 @@ impl Config { ttl: None, nodelay: Some(false), // Disable Nagle's algorithm by default backlog: 1024, - enable_port_reuse: false, } } @@ -189,101 +169,55 @@ impl Config { /// 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. - /// } - /// _ => {} - /// } + /// # Deprecation Notice /// - /// 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(()) - /// } - /// ``` + /// 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. /// - /// 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. + /// The API to configure port reuse is part of [`Transport`] and the option can be found in + /// [`libp2p_core::transport::DialOpts`]. /// - /// 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; + /// 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 } + + fn create_socket(&self, socket_addr: SocketAddr, port_use: PortUse) -> io::Result { + let socket = Socket::new( + Domain::for_address(socket_addr), + Type::STREAM, + Some(socket2::Protocol::TCP), + )?; + if socket_addr.is_ipv6() { + socket.set_only_v6(true)?; + } + if let Some(ttl) = self.ttl { + socket.set_ttl(ttl)?; + } + if let Some(nodelay) = self.nodelay { + socket.set_nodelay(nodelay)?; + } + socket.set_reuse_address(true)?; + #[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] + 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) + + socket.set_nonblocking(true)?; + + Ok(socket) + } } impl Default for Config { @@ -328,49 +262,18 @@ 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 - }; Transport { config, - port_reuse, ..Default::default() } } - fn create_socket(&self, socket_addr: SocketAddr) -> io::Result { - let socket = Socket::new( - Domain::for_address(socket_addr), - Type::STREAM, - Some(socket2::Protocol::TCP), - )?; - if socket_addr.is_ipv6() { - socket.set_only_v6(true)?; - } - if let Some(ttl) = self.config.ttl { - socket.set_ttl(ttl)?; - } - if let Some(nodelay) = self.config.nodelay { - socket.set_nodelay(nodelay)?; - } - socket.set_reuse_address(true)?; - #[cfg(unix)] - if let PortReuse::Enabled { .. } = &self.port_reuse { - socket.set_reuse_port(true)?; - } - Ok(socket) - } - fn do_listen( &mut self, id: ListenerId, socket_addr: SocketAddr, ) -> io::Result> { - let socket = self.create_socket(socket_addr)?; + let socket = self.config.create_socket(socket_addr, PortUse::Reuse)?; socket.bind(&socket_addr.into())?; socket.listen(self.config.backlog as _)?; socket.set_nonblocking(true)?; @@ -404,17 +307,9 @@ 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 - }; Transport { - port_reuse, - config, + port_reuse: PortReuse::default(), + config: Config::default(), listeners: SelectAll::new(), pending_events: VecDeque::new(), } @@ -456,7 +351,11 @@ 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)); @@ -468,26 +367,45 @@ where tracing::debug!(address=%socket_addr, "dialing address"); let socket = self - .create_socket(socket_addr) + .config + .create_socket(socket_addr, opts.port_use) .map_err(TransportError::Other)?; - if let Some(addr) = self.port_reuse.local_dial_addr(&socket_addr.ip()) { - tracing::trace!(address=%addr, "Binding dial socket to listen socket address"); - socket.bind(&addr.into()).map_err(TransportError::Other)?; - } + let bind_addr = match self.port_reuse.local_dial_addr(&socket_addr.ip()) { + Some(socket_addr) if opts.port_use == PortUse::Reuse => { + tracing::trace!(address=%addr, "Binding dial socket to listen socket address"); + Some(socket_addr) + } + _ => None, + }; - socket - .set_nonblocking(true) - .map_err(TransportError::Other)?; + let local_config = self.config.clone(); Ok(async move { + if let Some(bind_addr) = bind_addr { + socket.bind(&bind_addr.into())?; + } + // [`Transport::dial`] should do no work unless the returned [`Future`] is polled. Thus // do the `connect` call within the [`Future`]. - match socket.connect(&socket_addr.into()) { - Ok(()) => {} - Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {} - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - Err(err) => return Err(err), + let socket = match (socket.connect(&socket_addr.into()), bind_addr) { + (Ok(()), _) => socket, + (Err(err), _) if err.raw_os_error() == Some(libc::EINPROGRESS) => socket, + (Err(err), _) if err.kind() == io::ErrorKind::WouldBlock => socket, + (Err(err), Some(bind_addr)) if err.kind() == io::ErrorKind::AddrNotAvailable => { + // The socket was bound to a local address that is no longer available. + // Retry without binding. + tracing::debug!(connect_addr = %socket_addr, ?bind_addr, "Failed to connect using existing socket because we already have a connection, re-dialing with new port"); + std::mem::drop(socket); + let socket = local_config.create_socket(socket_addr, PortUse::New)?; + match socket.connect(&socket_addr.into()) { + Ok(()) => socket, + Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) => socket, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => socket, + Err(err) => return Err(err), + } + } + (Err(err), _) => return Err(err), }; let stream = T::new_stream(socket.into()).await?; @@ -496,40 +414,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 - /// `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; - } - match &self.port_reuse { - PortReuse::Disabled => address_translation(listen, observed), - PortReuse::Enabled { .. } => Some(observed.clone()), - } - } - /// Poll all listeners. #[tracing::instrument(level = "trace", name = "Transport::poll", skip(self, cx))] fn poll( @@ -819,23 +703,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::*; @@ -843,8 +710,8 @@ mod tests { channel::{mpsc, oneshot}, future::poll_fn, }; + use libp2p_core::Endpoint; use libp2p_core::Transport as _; - use libp2p_identity::PeerId; #[test] fn multiaddr_to_tcp_conversion() { @@ -927,7 +794,17 @@ 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 { + role: Endpoint::Dialer, + port_use: PortUse::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); socket.write_all(&[0x1, 0x2, 0x3]).await.unwrap(); let mut buf = [0u8; 3]; @@ -1003,7 +880,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).unwrap().await.unwrap(); + tcp.dial( + dest_addr, + DialOpts { + role: Endpoint::Dialer, + port_use: PortUse::New, + }, + ) + .unwrap() + .await + .unwrap(); } fn test(addr: Multiaddr) { @@ -1083,7 +969,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 { .. } => { @@ -1102,7 +988,17 @@ 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 { + role: 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]; @@ -1153,7 +1049,7 @@ mod tests { .try_init(); 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 { @@ -1262,55 +1158,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::() - } - - 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() @@ -1373,7 +1220,7 @@ mod tests { .build() .unwrap(); rt.block_on(async { - test::(); + test::(); }); } } diff --git a/transports/uds/CHANGELOG.md b/transports/uds/CHANGELOG.md index aad61d21547..aa068fe3877 100644 --- a/transports/uds/CHANGELOG.md +++ b/transports/uds/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.41.0 + +- 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/uds/src/lib.rs b/transports/uds/src/lib.rs index 4b1c7a1670d..5c57e255b4d 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::{DialOpts, TransportError, TransportEvent}, 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,21 +169,6 @@ macro_rules! codegen { } } - fn dial_as_listener( - &mut self, - addr: Multiaddr, - ) -> Result> { - self.dial(addr) - } - - fn address_translation( - &self, - _server: &Multiaddr, - _observed: &Multiaddr, - ) -> Option { - None - } - fn poll( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -260,8 +245,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::{borrow::Cow, path::Path}; @@ -318,7 +303,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-websys/CHANGELOG.md b/transports/webrtc-websys/CHANGELOG.md index 634120c53c3..475b13727e6 100644 --- a/transports/webrtc-websys/CHANGELOG.md +++ b/transports/webrtc-websys/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0-alpha + +- 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-websys/src/transport.rs b/transports/webrtc-websys/src/transport.rs index ecf137eab8a..836acb0b9f6 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,23 +98,12 @@ 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<'_>, ) -> Poll> { Poll::Pending } - - fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } } /// Checks if local Firefox. diff --git a/transports/webrtc/CHANGELOG.md b/transports/webrtc/CHANGELOG.md index 930526d58d5..90d4ce83df3 100644 --- a/transports/webrtc/CHANGELOG.md +++ b/transports/webrtc/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.0-alpha + +- 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 4fb4a2f8a45..a205810e7c4 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/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index 2e73ac9c459..62049c8f59b 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}; 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,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() { + // 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() { @@ -150,21 +162,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) - } } /// A stream of incoming connections on one or more interfaces. 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-websys/CHANGELOG.md b/transports/websocket-websys/CHANGELOG.md index c16ad6cc406..70d866e6141 100644 --- a/transports/websocket-websys/CHANGELOG.md +++ b/transports/websocket-websys/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.0 + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) ## 0.3.3 - Fix use-after-free handler invocation from JS side. diff --git a/transports/websocket-websys/Cargo.toml b/transports/websocket-websys/Cargo.toml index a2127986ddc..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.3" +version = "0.4.0" authors = ["Vince Vasta "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/websocket-websys/src/lib.rs b/transports/websocket-websys/src/lib.rs index c96d1d6aa3a..d2589715bbb 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,23 +110,12 @@ 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<'_>, ) -> 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/CHANGELOG.md b/transports/websocket/CHANGELOG.md index 419ff41c6fc..50b1c42d3e1 100644 --- a/transports/websocket/CHANGELOG.md +++ b/transports/websocket/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.44.0 + +- Implement refactored `Transport`. + See [PR 4568](https://github.com/libp2p/rust-libp2p/pull/4568) ## 0.43.2 - fix: Avoid websocket panic on polling after errors. See [PR 5482]. diff --git a/transports/websocket/Cargo.toml b/transports/websocket/Cargo.toml index f1b0a413115..271631b4021 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.2" +version = "0.44.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" diff --git a/transports/websocket/src/framed.rs b/transports/websocket/src/framed.rs index 69a01fdbd46..fc6a3f0e90e 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,19 +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) - } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.lock().address_translation(server, observed) + self.do_dial(addr, dial_opts) } fn poll( @@ -263,7 +255,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 +274,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 +298,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..40d6db44471 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,19 +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) - } - - fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option { - self.transport.address_translation(server, observed) + self.transport.dial(addr, opts) } fn poll( @@ -292,7 +285,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; @@ -339,7 +336,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); diff --git a/transports/webtransport-websys/CHANGELOG.md b/transports/webtransport-websys/CHANGELOG.md index 0409819a63f..2aab226ab12 100644 --- a/transports/webtransport-websys/CHANGELOG.md +++ b/transports/webtransport-websys/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 + +- 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 ", diff --git a/transports/webtransport-websys/src/transport.rs b/transports/webtransport-websys/src/transport.rs index 3f14f3e476b..6a9a9dad954 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::debug!("{}", e); @@ -83,21 +93,10 @@ 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<'_>, ) -> Poll> { Poll::Pending } - - fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { - None - } } diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs index 1f420cd6671..938cdf0b3e1 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::Dialer, + port_use: PortUse::Reuse, + }, + ) + .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::Dialer, + port_use: PortUse::Reuse, + }, + ) + .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::Reuse, + }, + ) + .unwrap() + .await + .unwrap(); conn }